mcpField
B
broken-access-control

broken-access-control

github.com/scholarly360安全2252 次安装29 周前更新
代码仓库

关于 broken-access-control

面向审查 Python Web 应用的安全技能,帮助开发者检查访问控制缺陷。内容涵盖缺失授权、IDOR、CORS、SSRF、CSRF 及 JWT 和会话安全,并提供审计步骤与扫描建议。

主要功能

  1. 01检查路由授权与权限验证
  2. 02核对资源所有权以发现 IDOR
  3. 03审查 CORS 与 SSRF 风险
  4. 04检查 CSRF、JWT 和会话设置
  5. 05提供 Bandit、OWASP ZAP 扫描建议

适用场景

  1. 01审计 FastAPI 或 Flask 应用
  2. 02检查用户资源是否存在越权访问
  3. 03排查跨域配置及用户输入 URL 风险

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/scholarly360/owasp-top10-web-skills --skill broken-access-control

安装后在 Claude Code 中输入/broken-access-control确认已加载。

附带文件references/bac-detail.md

详细介绍

访问控制失效(A01:2025)

连续两个周期位列 OWASP 风险榜首。 在 3.74% 的应用程序中发现,共有 超过 180 万个实例和 32,654 个 CVE。访问控制用于确保用户无法执行超出其预期权限范围的操作。访问控制失效会导致数据遭到未经授权的泄露、修改或破坏。

如需了解所有 CWE 和修复模式的详细参考,请参阅 references/bac-detail.md。


此技能涵盖的内容

子类别描述关键 CWE
缺少授权路由/端点没有身份验证保护CWE-862, CWE-284
IDOR未验证资源所有权CWE-639, CWE-285
CORS 配置错误使用通配符或过于宽松的来源CWE-284
SSRF向内部/私有 IP 发起出站请求CWE-918
CSRF更改状态的表单缺少令牌验证CWE-352
权限提升用户访问更高权限的角色CWE-269
JWT/会话操纵元数据篡改、会话固定CWE-287

工作流:审计 FastAPI 或 Flask 应用

步骤 1 — 找出所有路由

FastAPI:

# List all routes and their dependencies
for route in app.routes:
    print(route.path, route.methods, route.dependencies)

Flask:

# Print all registered endpoints
for rule in app.url_map.iter_rules():
    print(rule.endpoint, rule.methods, rule.rule)

标记任何符合以下情况的路由:

  • 处理 POST、PUT、PATCH、DELETE 请求但没有身份验证依赖项
  • 返回用户特定数据(个人资料、订单、文件)但没有所有权检查
  • 对敏感操作没有速率限制

步骤 2 — 检查授权保护

FastAPI — 查找缺少 Depends() 的情况:

# VULNERABLE — no auth check
@app.get("/users/{user_id}/data")
async def get_user_data(user_id: int):
    return db.query(UserData).filter_by(owner_id=user_id).all()

# SECURE — requires authenticated user
@app.get("/users/{user_id}/data")
async def get_user_data(user_id: int, current_user: User = Depends(get_current_user)):
    if current_user.id != user_id:
        raise HTTPException(status_code=403, detail="Forbidden")
    return db.query(UserData).filter_by(owner_id=user_id).all()

Flask — 查找缺少 @login_required 的情况:

# VULNERABLE
@app.route("/dashboard")
def dashboard():
    return render_template("dashboard.html")

# SECURE
@app.route("/dashboard")
@login_required
def dashboard():
    return render_template("dashboard.html")

步骤 3 — IDOR 检查(资源所有权)

IDOR 是最常见的 BAC 失效。每个获取用户所有资源的查询都必须将 resource.owner_id 与 current_user.id 进行比较。

# VULNERABLE IDOR — attacker can request any document_id
@app.get("/documents/{doc_id}")
async def get_doc(doc_id: int, user=Depends(get_current_user)):
    return db.get(Document, doc_id)  # No ownership check!

# SECURE
@app.get("/documents/{doc_id}")
async def get_doc(doc_id: int, user=Depends(get_current_user)):
    doc = db.get(Document, doc_id)
    if not doc or doc.owner_id != user.id:
        raise HTTPException(status_code=404)  # 404 preferred over 403 (avoids enumeration)
    return doc

**测试向量:**以用户 A 的身份进行身份验证,记录一个资源 ID;然后以用户 B 的身份进行身份验证,并请求同一个 ID。如果能获取数据,即存在 IDOR。


步骤 4 — CORS 配置错误

FastAPI:

# VULNERABLE — wildcard origin
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True)

# SECURE — explicit allowlist
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.yourdomain.com"],
    allow_credentials=True,
    allow_methods=["GET", "POST"],
    allow_headers=["Authorization", "Content-Type"],
)

Flask:

# VULNERABLE
CORS(app, origins="*")

# SECURE
CORS(app, origins=["https://app.yourdomain.com"], supports_credentials=True)

⚠️ 将 allow_origins=["*"] 与 allow_credentials=True 结合使用会带来双重危险——大多数浏览器会阻止这种情况,但这表明配置存在严重问题。


步骤 5 — SSRF(现已纳入 A01:2025)

检查任何使用用户控制的 URL 发起出站 HTTP 请求的代码。

import ipaddress, re

PRIVATE_RANGES = [
    ipaddress.ip_network("10.0.0.0/8"),
    ipaddress.ip_network("172.16.0.0/12"),
    ipaddress.ip_network("192.168.0.0/16"),
    ipaddress.ip_network("127.0.0.0/8"),
    ipaddress.ip_network("169.254.0.0/16"),  # AWS metadata
]

def is_safe_url(url: str) -> bool:
    from urllib.parse import urlparse
    import socket
    try:
        host = urlparse(url).hostname
        ip = ipaddress.ip_address(socket.gethostbyname(host))
        return not any(ip in net for net in PRIVATE_RANGES)
    except Exception:
        return False

# In your endpoint:
@app.post("/fetch")
async def fetch_url(url: str, user=Depends(get_current_user)):
    if not is_safe_url(url):
        raise HTTPException(status_code=400, detail="URL not allowed")
    return await httpx.get(url)

测试向量:http://169.254.169.254/latest/meta-data/(AWS 元数据)、http://localhost:6379(Redis)、http://10.0.0.1/admin


步骤 6 — CSRF(Flask 特有风险)

使用 JWT/Bearer 令牌的 FastAPI API 通常能够防范 CSRF。使用会话 Cookie 的 Flask 应用需要明确的保护措施。

# Flask — verify Flask-WTF is installed and CSRF is enabled
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)

# All state-changing forms must include {{ form.hidden_tag() }} or {{ csrf_token() }}

检查配置中的 WTF_CSRF_ENABLED = True(默认情况下已启用,但请确认它未被禁用)。


步骤 7 — JWT 和会话安全
# Verify JWT expiry is set (short-lived: 15 min–1 hr)
jwt.encode({"sub": user_id, "exp": datetime.utcnow() + timedelta(minutes=30)}, SECRET_KEY)

# Flask — verify session security flags
app.config["SESSION_COOKIE_SECURE"] = True    # HTTPS only
app.config["SESSION_COOKIE_HTTPONLY"] = True  # No JS access
app.config["SESSION_COOKIE_SAMESITE"] = "Lax" # CSRF mitigation
app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(hours=2)

快速审计清单

对任何 Python Web 应用执行以下检查:

  • 每个非公开路由都有身份验证依赖项/装饰器
  • 所有用户所有资源的查询都会验证 resource.owner_id == current_user.id
  • CORS 来源是明确的允许列表,而不是 *
  • 出站 HTTP 调用会验证 URL 不在私有 IP 范围内
  • Flask 应用的所有更改状态的表单都启用了 Flask-WTF CSRF 保护
  • JWT 具有较短的 exp 声明;会话具有 SECURE、HTTPONLY、SAMESITE 标志
  • 注销时会使会话 ID 失效
  • 会记录并告警访问控制失效

自动化扫描

工具可检测内容命令
BanditSSRF 模式、硬编码密钥、危险函数bandit -r . -t B105,B106,B107,B501,B601
OWASP ZAPIDOR、运行时缺少身份验证、CORSzap-baseline.py -t http://localhost:8000
truffleHogGit 历史记录中泄露的令牌/密钥trufflehog git file://. --only-verified

关键 CWE 参考

CWE名称典型模式
CWE-284访问控制不当路由无需任何身份验证即可访问
CWE-285授权不当已进行身份验证,但未检查角色/所有权
CWE-352CSRF更改状态的端点接受 Cookie 身份验证,且没有 CSRF 令牌
CWE-639通过用户可控密钥绕过身份验证IDOR — GET /orders/{id} 未检查所有权
CWE-862缺少授权没有 @login_required 或 Depends(get_current_user)
CWE-918SSRF未验证就调用 requests.get(user_input_url)
CWE-200敏感信息泄露未经授权的用户收到他人的 PII/数据

另请参阅

评论

支持 Markdown