mcpField
S
security-logging-alerting-failures

security-logging-alerting-failures

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

关于 security-logging-alerting-failures

security-logging-alerting-failures 是针对 OWASP A09:2025 的安全审查技能,供审计 FastAPI 和 Flask 应用的日志与告警实践。它提供检查清单、结构化日志建议、告警阈值及自动扫描命令。

主要功能

  1. 01检查日志注入和敏感信息泄露
  2. 02核对认证与权限事件日志
  3. 03评估结构化日志及 SIEM 兼容性
  4. 04检查告警规则和事件响应
  5. 05提供 CI/CD 自动扫描命令

适用场景

  1. 01审查 FastAPI 或 Flask 的安全日志
  2. 02查找日志中的用户输入与机密数据
  3. 03核验认证失败和管理操作是否记录
  4. 04评估生产环境告警覆盖情况

安装

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

$npx skills add https://github.com/scholarly360/owasp-top10-web-skills --skill security-logging-alerting-failures

安装后在 Claude Code 中输入/security-logging-alerting-failures确认已加载。

附带文件references/alerting-platforms.mdreferences/remediation-patterns.md

详细介绍

A09:2025 — 安全日志记录与告警失效

OWASP 排名: #9(社区连续第三轮投票选出)
2025 年更名: 新增“告警”——只记录日志而不发出告警,安全价值极低。
统计数据: 5 个 CWE、723 个 CVE、3.91% 平均发生率
涵盖的 CWE: CWE-778、CWE-117、CWE-532、CWE-223、CWE-390


此类别涵盖的内容

失效类型描述
日志记录不足未记录可审计事件(登录、访问控制失败、验证错误)
日志注入用户输入未经转义直接写入日志消息,导致日志伪造
日志中的敏感数据密码、令牌、PII、卡号被写入日志输出
遗漏安全相关信息记录错误/警告时缺少上下文(IP、用户、时间戳、操作)
无告警虽然存在日志,但没有阈值或规则,也没有平台将其发送给事件响应团队

审计清单 — FastAPI 和 Flask

按顺序检查各个部分。对于每项,请标注:通过、失败或不适用。

1. 日志注入(CWE-117)

绝不能将用户可控的输入未经处理直接写入日志语句。

需要检查:

# VULNERABLE — newline injection, ANSI escape injection
logger.info(f"User logged in: {request.form['username']}")
logger.warning("Failed login for: " + username)

# SAFE — sanitize before logging
import re
safe_username = re.sub(r'[\r\n\x1b]', '_', username)
logger.info("Failed login for: %s", safe_username)

清单:

  • 日志调用中不使用 f-string 或 + 拼接原始请求数据
  • 从所有用户提供的日志值中去除换行符(\r、\n)
  • 去除 ANSI 转义码(\x1b[)(防止终端注入)
  • 使用结构化日志记录(JSON 格式)——通过字段分隔从设计上防止注入

2. 日志中的敏感数据(CWE-532)

日志输出中绝不能出现机密信息,即使在 DEBUG 级别也不例外。

需要标记的模式:

# VULNERABLE
logger.debug(f"Auth token: {token}")
logger.info(f"Processing payment for card: {card_number}")
app.logger.debug(request.json)         # May contain password field
print(f"Password reset for {email}, token={reset_token}")

# SAFE
logger.info("Auth token validated for user_id=%s", user_id)
logger.info("Payment processed for user_id=%s, last4=%s", user_id, card_number[-4:])

需要扫描的内容:

  • 不记录以下内容:password、passwd、secret、token、api_key、authorization 标头值
  • 不记录以下内容:信用卡号、SSN、完整的电子邮箱和密码组合
  • 不在未筛选字段的情况下完整记录 request.json / request.form
  • 生产代码中没有包含敏感变量的 print() 语句
  • CI/CD 中通过 Bandit 规则 B506(及类似规则)的检查

3. 日志记录不足(CWE-778)

以下事件必须生成包含完整上下文的日志条目。

必需记录的日志事件:

事件最低必需字段
身份验证失败时间戳、IP、尝试使用的用户名、用户代理
身份验证成功时间戳、IP、user_id、方法(密码/OAuth/MFA)
授权失败(403)时间戳、IP、user_id、尝试访问的资源、HTTP 方法
输入验证失败时间戳、IP、user_id、字段名(敏感信息不记录其值)、错误
高价值交易时间戳、user_id、操作、资源 ID、更改前/后的状态
账户变更(密码重置、角色变更)时间戳、actor_user_id、target_user_id、变更类型
管理员操作时间戳、admin_user_id、操作、受影响的资源

FastAPI 实现模式:

import logging
import structlog  # recommended for structured logs

log = structlog.get_logger()

# In auth endpoint
@router.post("/login")
async def login(credentials: LoginSchema, request: Request):
    user = await authenticate(credentials)
    if not user:
        log.warning(
            "auth.failed",
            ip=request.client.host,
            username=credentials.username,    # OK — username is not a secret
            user_agent=request.headers.get("user-agent"),
        )
        raise HTTPException(status_code=401, detail="Invalid credentials")
    log.info("auth.success", user_id=user.id, ip=request.client.host)

Flask 实现模式:

from flask import request
import logging

@app.route("/login", methods=["POST"])
def login():
    user = authenticate(request.form["username"], request.form["password"])
    if not user:
        app.logger.warning(
            "auth.failed ip=%s username=%s ua=%s",
            request.remote_addr,
            request.form.get("username"),
            request.user_agent.string,
        )
        return {"error": "Invalid credentials"}, 401

清单:

  • 所有 401/403 响应都有对应的 logger.warning() 或 logger.error() 调用
  • 日志条目包含:时间戳(由 logging 自动添加)、源 IP、用户标识符
  • 记录密码重置和账户修改流程
  • 记录管理面板操作,并包含操作人的身份
  • 高价值业务交易会生成审计日志条目

4. 结构化日志记录与 SIEM 兼容性

日志格式必须可供可观测性平台(ELK、Splunk、Datadog、Sentry)解析。

FastAPI — structlog + JSON:

# main.py
import structlog
import logging

structlog.configure(
    processors=[
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.stdlib.add_log_level,
        structlog.processors.JSONRenderer(),  # machine-parseable
    ],
    wrapper_class=structlog.BoundLogger,
    logger_factory=structlog.PrintLoggerFactory(),
)

Flask — python-json-logger:

from pythonjsonlogger import jsonlogger

handler = logging.StreamHandler()
handler.setFormatter(jsonlogger.JsonFormatter(
    fmt="%(asctime)s %(levelname)s %(name)s %(message)s"
))
app.logger.addHandler(handler)

清单:

  • 生产环境中的日志输出为 JSON 格式(而非纯文本)
  • 生产环境中的日志级别不是 DEBUG(以免泄露过多信息)
  • 日志发送到集中式平台:Sentry、ELK、Splunk、Datadog、CloudWatch
  • 已制定日志保留政策,并符合合规要求(例如至少保留 90 天)

5. 告警集成

只有日志记录而没有告警 = 故障悄无声息。请确认已启用监控。

Sentry(异常告警):

# FastAPI
import sentry_sdk
from sentry_sdk.integrations.fastapi import FastApiIntegration
sentry_sdk.init(dsn=settings.SENTRY_DSN, integrations=[FastApiIntegration()])

# Flask
from sentry_sdk.integrations.flask import FlaskIntegration
sentry_sdk.init(dsn=settings.SENTRY_DSN, integrations=[FlaskIntegration()])

建议的告警阈值:

信号告警触发条件
登录失败5 分钟内同一用户名登录失败次数 >10
登录失败5 分钟内来自同一 IP 的登录失败次数 >100
403 响应1 分钟内来自同一 user_id 的响应次数 >50
500 错误任意 1 分钟窗口内的请求中 >5%
新建管理员账户每次发生时立即告警
权限提升每次发生时立即告警

检查清单:

  • Sentry 或同类工具已初始化,并在生产环境中接收事件
  • 速率限制违规会生成告警事件(而不只是返回 HTTP 429 响应)
  • 重复的身份验证失败会触发告警(暴力破解检测)
  • 每种告警类型都有对应的告警运行手册/操作手册
  • 在高价值区域部署了蜜罐令牌(近乎零误报的入侵检测)

自动化扫描

将这些工具纳入 CI/CD 流程:

# Static analysis — catches log injection patterns, sensitive data in logs
bandit -r . -t B106,B107,B110 --severity-level medium

# Search for raw request data in log calls
grep -rn "logger\.\(info\|warning\|error\|debug\)" . \
  | grep -E "request\.(form|json|args|data|headers)\[" \
  | grep -v "\.get(" 

# Search for potential sensitive field logging
grep -rn "logger\|logging\|print" . \
  | grep -iE "password|passwd|secret|token|api_key|card|ssn|cvv"

重要 CWE 参考

CWE名称Python 风险
CWE-778日志记录不足身份验证/授权失败时没有日志记录
CWE-117日志注入在 logger 调用中使用 f-string 插入用户输入
CWE-532日志中的敏感信息调试输出中包含密码/令牌
CWE-223遗漏安全相关信息日志缺少 IP/用户上下文
CWE-390检测到错误条件但未采取操作捕获异常后将其吞掉,且未记录日志

参考文件

  • references/remediation-patterns.md — 各 CWE 的完整修复前后代码
  • references/alerting-platforms.md — Sentry、ELK、Splunk、Datadog 集成指南

当用户需要超出检查清单范围的详细实现帮助时,请阅读这些文件。

评论

支持 Markdown