数据越权访问如何拦截

wen 网络安全 24

本文目录导读:

数据越权访问如何拦截

  1. 核心原则:前端不可信,权限必须由后端强制校验
  2. 第一层:服务端权限校验(强控层)
  3. 第二层:API设计与请求校验(入口层)
  4. 第三层:防御性编程与数据隔离(架构层)
  5. 第四层:监控与事后拦截(兜底层)
  6. 一个典型的越权拦截流程
  7. 立即可以做的几件事(快速自查清单)

数据越权访问(IDOR,不安全的直接对象引用)是Web应用中最常见的安全漏洞之一,要有效拦截数据越权,需要从架构设计、服务端逻辑、监控告警三个层面构建纵深防御体系。

以下是具体的拦截方案,按优先级排序:

核心原则:前端不可信,权限必须由后端强制校验

绝对不要依赖前端隐藏按钮、禁用下拉选项等方式来防止越权。 攻击者可以轻易绕过前端限制直接调用API。


第一层:服务端权限校验(强控层)

这是最根本的拦截手段,每次操作数据时,必须验证请求者身份的所有权角色权限

主体校验:我是谁?(Authentication)

  • 措施: 使用JWT(JSON Web Token)或Session,确保每个API请求都能明确识别出当前用户(user_id)。
  • 防篡改: Token必须经过签名(如RS256算法),防止客户端伪造用户身份。

客体校验:我想访问谁的数据?(Authorization)

这是拦截越权的核心代码逻辑,假设用户A试图访问订单order_id=123,后端代码不应直接执行:

# ❌ 危险的写法:任何人都可以查任何订单
def get_order(order_id):
    order = Order.query.get(order_id) # 直接查库,无权限过滤
    return order

应该改为:

# ✅ 安全的写法:必须校验当前用户有权限访问该订单
from flask import g, request
def get_order(order_id):
    current_user_id = get_current_user_id_from_token() # 解析JWT/Token
    # 核心:查询时加上用户ID条件
    order = Order.query.filter_by(
        id=order_id, 
        user_id=current_user_id # 只查属于当前用户的订单
    ).first()
    if not order:
        # 不要告诉用户是“权限不足”,而是返回“资源不存在”
        return {"error": "Order not found"}, 404 
    return order

角色与权限矩阵(RBAC/ABAC)

对于多租户系统或后台管理,需要更精细的权限控制。

  • 垂直权限(水平越权): 普通用户访问普通用户的数据,但A不能看B的数据。
    • 拦截: 通过user_id绑定(如上例所示)。
  • 水平权限(垂直越权): 普通用户试图执行管理员的操作(如删除所有用户)。
    • 拦截: 使用角色(Role)权限点(Permission)校验。
def delete_user(admin_id, target_user_id):
    # 1. 先获取当前用户角色
    current_user = User.query.get(admin_id)
    # 2. 校验是否有删除用户的权限
    if not current_user.has_permission('user:delete'):
        return {"error": "Insufficient privileges"}, 403
    # 3. 即使是管理员,也要做二次限制(比如不能删除自己或更高权限角色)
    target_user = User.query.get(target_user_id)
    if target_user.role.level >= current_user.role.level:
        return {"error": "Cannot delete user with higher or equal role"}, 403
    # 执行删除
    User.query.filter_by(id=target_user_id).delete()

第二层:API设计与请求校验(入口层)

使用间接引用(随机ID代替自增ID)

  • 问题: 如果订单ID是自增整数(如1001, 1002),攻击者很容易遍历。
  • 解决: 使用UUID、HashID或随机字符串作为外部暴露的ID。
    • 例子: order_id 在数据库里是 1,但API返回的是 ord_d3f8a2b1c9e7
    • 效果: 攻击者无法通过猜数字来尝试访问他人数据。

严格的参数校验与类型检查

  • 措施: 所有传入的ID、角色、权限参数都必须经过白名单校验
    • user_id 必须是从Token中解析出来的,绝不接受客户端直接传入的 user_id 参数。
    • 禁止传入 role=admin 来提权,角色必须在服务端Session中设置。

使用GraphQL / REST 的Field-Level授权

  • 如果使用GraphQL,必须为每个Resolver添加授权逻辑,防止用户查询自己没有权限的字段。
  • 如果是REST API,返回的数据字段也需要根据权限做过滤(普通用户看不到用户的phone_number字段)。

第三层:防御性编程与数据隔离(架构层)

分层查询模式(Repository模式)

  • 措施: 不要在整个应用中散落Order.query.all()这种查询,封装一个专门的数据访问层(Repository)
    • OrderRepository.get_orders_by_owner(owner_id) → 永远只能查特定用户的数据。
    • OrderRepository.get_admin_all_orders() → 只能由管理员调用。

数据库行级安全(Row-Level Security, RLS)

  • 措施: 在PostgreSQL等数据库中启用RLS策略。
    • 数据库层面自动拦截:SELECT * FROM orders WHERE user_id = current_setting('app.current_user_id')
    • 优点: 即使后端代码写漏了权限校验,数据库层面也会拒绝查询其他用户的数据。

数据脱敏与代理

  • 对于一些只读但敏感的数据(如日志、账单),不要直接暴露原始数据,通过反向代理(NGINX/OpenResty)API网关进行脱敏处理。

第四层:监控与事后拦截(兜底层)

如果以上全部失效,需要有检测能力:

异常行为检测

  • 指标: 短时间内某一用户访问了大量不同的用户ID(user_id参数变化频繁)。
  • 响应:
    • 自动限流(Rate Limiting): 触发WAF或API限流规则,暂时封IP。
    • 日志告警: 输出到SIEM系统,例:
      # 检测条件
      (请求路径包含 "/api/v1/user/" 或 "/api/orders/" ) 
      AND (同一Token在5秒内请求了超过10个不同的 user_id 或 order_id)
    • 自动封禁: 如果检测到连续的404/403高频率访问不同资源(说明在遍历),自动将该Token或IP加入黑名单。

审计日志

  • 所有关键数据访问操作(增、删、改、查特定敏感数据)都必须记录日志,内容包括:
    • 时间、请求来源IP、当前用户ID、操作类型、目标资源ID。
    • 作用: 用于事后溯源,并训练AI模型(如Splunk)发现异常。

一个典型的越权拦截流程

假设用户手机App发送请求:GET /api/orders/1001

  1. 网关/负载均衡层: 校验JWT签名,提取user_id=123,若token过期或伪造,直接返回401。
  2. 权限校验层(核心):
    • 后端拿到order_id=1001user_id=123
    • 查询数据库:SELECT * FROM orders WHERE id=1001 AND user_id=123
    • 结果1: 找到记录 → 返回数据。
    • 结果2: 未找到记录 → 返回404(千万不要返回403并说“这个订单属于别人”),404可以迷惑攻击者,让他不知道是数据不存在还是权限不足。
  3. 数据返回层: 返回的JSON数据只包含权限内的字段(如价格,不包含余额等)。
  4. 监控层: 如果该用户连续请求了100个不同的order_id且全部返回404,监控系统触发告警并临时封禁该Token。

立即可以做的几件事(快速自查清单)

  1. 代码审计: 搜索代码中的 find_by_idget_objectquery.filter_by(id=x) 等函数,检查是否在使用前强制关联了当前用户ID
  2. 删除危险端点: 停用所有依赖前端传参来获取用户信息的API(POST /api/getUserInfo?user_id={user_id})。
  3. 前端改造: 前端应只显示业务数据,后端不返回权限外的任何信息(如用户邮箱、身份证等)。
  4. 测试: 针对核心API(订单、用户详情、支付记录)手动测试:用A的Token去请求B的数据资源,看返回是否是404/403而不是200。

抱歉,评论功能暂时关闭!