访问控制是Web应用安全的核心。系统如何判断一个用户是否有权限访问某个资源?这就是访问控制要解决的问题。如果访问控制实现不当,攻击者可以越权访问其他用户的资源,或者访问不应该访问的功能。

水平越权(Horizontal Privilege Escalation)是指攻击者可以访问同级别其他用户的资源。比如用户A可以访问用户B的数据。
1. ID可预测
系统使用可预测的ID(如自增ID)来标识资源:
GET /api/user/1/profile
GET /api/user/2/profile
GET /api/user/3/profile如果系统只检查用户是否登录,不检查ID是否属于当前用户,就可能存在水平越权。
案例:
假设一个系统的用户个人信息接口是/api/user/{id}/profile,系统只检查用户是否登录,不检查id参数是否属于当前用户。当前用户ID是1,但可以访问/api/user/2/profile,获取其他用户的信息。
测试方法:
import requests
def test_horizontal_privilege():
base_url = "https://example.com"
session = requests.Session()
# 登录用户A
session.post(f"{base_url}/login", data={"username": "userA", "password": "passA"})
user_a_id = 1 # 假设用户A的ID是1
# 尝试访问用户B的资源(ID=2)
response = session.get(f"{base_url}/api/user/2/profile")
if response.status_code == 200:
print("VULNERABLE: Horizontal privilege escalation!")
print(f"User A can access User B's data: {response.json()}")
else:
print("SECURE: Access denied.")2. 参数可篡改
系统通过参数传递资源标识,但参数可以被篡改:
GET /api/order?order_id=123如果系统不验证order_id是否属于当前用户,就可能存在水平越权。
案例:
假设一个电商系统的订单查询接口是/api/order?order_id=123,系统只检查用户是否登录,不检查订单是否属于当前用户。攻击者可以修改order_id参数,查看其他用户的订单。
3. 直接对象引用(IDOR)
直接对象引用(Insecure Direct Object Reference,IDOR)是指系统直接使用用户提供的参数来访问资源,没有验证权限。
常见形式:文件下载/download?file_id=123、数据查询/api/data?id=456、资源访问/resource/789等。
常见测试方法:
import requests
def test_idor():
base_url = "https://example.com"
session = requests.Session()
# 登录
session.post(f"{base_url}/login", data={"username": "test", "password": "test"})
# 获取自己的资源ID(比如文件ID)
response = session.get(f"{base_url}/my_files")
my_file_id = response.json()[0]['id'] # 假设返回文件列表
# 尝试访问其他ID
for test_id in range(1, 100):
if test_id != my_file_id:
response = session.get(f"{base_url}/download?file_id={test_id}")
if response.status_code == 200:
print(f"VULNERABLE: Can access file {test_id}!")垂直越权(Vertical Privilege Escalation)是指攻击者可以访问更高级别的权限。比如普通用户可以访问管理员功能。
1. 权限验证绕过
系统通过前端隐藏管理员功能,但后端不验证权限:
<!-- 前端:普通用户看不到这个按钮 -->
{% if user.is_admin %}
<a href="/admin/delete_user">删除用户</a>
{% endif %}但后端可能不验证:
// 后端:没有权限检查
if ($_POST['action'] == 'delete_user') {
delete_user($_POST['user_id']);
}案例:
比如前端普通用户看不到“删除用户”按钮,但可以直接访问POST /admin/delete_user,后端不验证权限,可以删除任意用户。
常见测试方法:
import requests
def test_vertical_privilege():
base_url = "https://example.com"
session = requests.Session()
# 登录普通用户
session.post(f"{base_url}/login", data={"username": "user", "password": "pass"})
# 尝试访问管理员功能
admin_endpoints = [
"/admin/users",
"/admin/delete_user",
"/admin/settings",
"/api/admin/users"
]
for endpoint in admin_endpoints:
response = session.get(f"{base_url}{endpoint}")
if response.status_code == 200:
print(f"VULNERABLE: Can access admin endpoint {endpoint}!")2. 功能未授权访问
系统有隐藏的管理功能,但可以通过直接访问URL来访问:
GET /admin/panel
GET /admin/users
GET /admin/settings案例:
假设通过目录扫描发现了/admin管理后台,尝试访问,发现不需要管理员权限,可以访问所有管理功能。
3. 角色提升
系统通过参数或Cookie来标识用户角色,但可以被篡改:
Cookie: user_role=admin或者:
POST /api/user/update
{"user_id": 1, "role": "admin"}案例:
假设一个系统,用户角色存在Cookie中:user_role=user,攻击者修改Cookie为user_role=admin,刷新页面,获得管理员权限。
常见测试方法:
import requests
def test_role_escalation():
base_url = "https://example.com"
session = requests.Session()
# 登录普通用户
session.post(f"{base_url}/login", data={"username": "user", "password": "pass"})
# 尝试修改角色
# 方法1:修改Cookie
session.cookies.set('user_role', 'admin')
# 方法2:修改请求参数
response = session.post(f"{base_url}/api/user/update", json={
"user_id": 1,
"role": "admin"
})
# 测试是否获得管理员权限
response = session.get(f"{base_url}/admin/panel")
if response.status_code == 200:
print("VULNERABLE: Role escalation successful!")业务逻辑中的权限问题往往是最难发现的,因为需要深入理解业务流程。
常见场景:
状态机缺陷,订单状态可以回退,可以跳过某些状态,可以重复执行某些操作。 工作流绕过,可以跳过审批流程,可以修改流程参数。 业务规则绕过,可以绕过业务限制,可以修改业务参数。
案例1:订单状态回退
比如一个电商系统,正常流程是下单 → 支付 → 发货 → 完成,但可以通过修改请求,把订单状态从“完成”改回“待支付”,然后可以取消订单,获得退款(即使已经收到货)。
常见测试方法:
import requests
def test_order_state_manipulation():
base_url = "https://example.com"
session = requests.Session()
# 登录
session.post(f"{base_url}/login", data={"username": "test", "password": "test"})
# 创建一个订单并完成
order_id = create_and_complete_order(session)
# 尝试修改订单状态
response = session.post(f"{base_url}/api/order/update", json={
"order_id": order_id,
"status": "pending_payment" # 改回待支付
})
if response.status_code == 200:
# 尝试取消订单
cancel_response = session.post(f"{base_url}/api/order/cancel", json={
"order_id": order_id
})
if cancel_response.status_code == 200:
print("VULNERABLE: Can manipulate order state!")案例2:审批流程绕过
假设一个审批系统,正常流程是提交 → 部门审批 → 财务审批 → 通过,但可以直接访问财务审批接口,跳过部门审批,或者修改审批状态,直接标记为通过。
隐藏功能是指系统存在但没有在前端显示的功能。这些功能可能是测试代码没有删除,是管理功能没有权限控制,是调试接口没有关闭。
常见发现方法:
目录扫描,使用工具如dirsearch扫描目录,可能发现隐藏的页面和接口。 参数fuzz,使用工具如ffuf对参数进行fuzz,可能发现隐藏的API接口。
源码分析,如果有源码,分析路由、API接口,可能发现隐藏的功能,查找注释、TODO、测试代码。
JavaScript分析,前端JavaScript可能包含API调用,分析网络请求,发现隐藏接口。
案例:
假设在测试一个系统时,通过目录扫描发现了/admin管理后台(需要权限)、/admin/test测试接口(不需要权限)、/api/debug调试接口(泄露敏感信息)、/backup备份目录(可以下载数据库备份)。
常见测试脚本:
import requests
from concurrent.futures import ThreadPoolExecutor
def test_endpoint(endpoint):
base_url = "https://example.com"
session = requests.Session()
# 登录普通用户
session.post(f"{base_url}/login", data={"username": "user", "password": "pass"})
# 测试端点
response = session.get(f"{base_url}{endpoint}")
if response.status_code == 200:
print(f"Found accessible endpoint: {endpoint}")
return endpoint
return None
# 常见的管理和测试端点
endpoints = [
"/admin", "/admin/panel", "/admin/users",
"/api/admin", "/api/debug", "/api/test",
"/backup", "/test", "/debug", "/dev",
"/phpinfo.php", "/info.php", "/test.php"
]
with ThreadPoolExecutor(max_workers=10) as executor:
results = executor.map(test_endpoint, endpoints)
found = [r for r in results if r]
print(f"Found {len(found)} accessible endpoints")发现隐藏功能后,要分析它的作用和安全问题。
常见问题:
案例:
假设发现一个隐藏的调试接口/api/debug/info,返回系统信息,不需要权限,返回了数据库配置、API密钥、内部系统地址。
总的来说,越权漏洞大致分为三类:水平越权是指可以访问与自己权限相同但属于其他用户的数据;垂直越权是指普通用户可以越级访问到本该只有管理员等高权限用户才能使用的功能;而业务逻辑越权则涉及在业务流程中的权限校验缺失。此外,别忽视隐藏功能带来的风险,很多内部接口、测试功能或后台入口如果被发现,可能造成重要信息泄露或敏感操作被滥用。
下一节,我们将进入“输入验证”这个至关重要的议题——这是SQL注入、命令注入等一系列Web安全高发问题的根源!
可以尝试测试所有需要认证的功能,尝试修改ID参数,访问其他用户的资源,尝试访问管理员功能,分析业务流程,寻找权限问题,使用目录扫描和fuzz发现隐藏功能。