ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Bandit B703 插件解析:检测 Django mark_safe 潜在 XSS 漏洞

Bandit B703 插件解析:检测 Django mark_safe 潜在 XSS 漏洞 SAST应用安全【免费下载链接】banditBandit is a tool designed to find common security issues in Python code.项目地址https://gitcode.com/gh_mirrors/ba/bandit点击查看免费下载Bandit 的 B703 检测插件django_mark_safe专门针对 Django 模板安全中的高频误用场景当代码通过django.utils.safestring模块的mark_safe、SafeText、SafeString等 API 将动态内容标记为安全 HTML时若传入内容并非可信的字符串字面量就可能引入跨站脚本XSS漏洞。本指南基于 Bandit 仓库中 django_xss.py 的源码实现与其配套文档 b703_django_mark_safe.rst完整讲解该插件的检测范围、判定逻辑、输出格式与实际使用方法帮助你在 CI 流程中落地 Django 项目的 XSS 静态扫描。插件定位B7xx XSS 检测家族的一员Bandit 将检测插件按 ID 分组管理在 doc/source/plugins/index.rst 中可以看到ID 分组描述B1xxmisc tests杂项测试B2xxapplication/framework misconfiguration应用/框架错误配置B3xxblacklists (calls)调用黑名单B4xxblacklists (imports)导入黑名单B5xxcryptography密码学B6xxinjection注入B7xxXSSB703 属于 B7xx XSS 家族与 B701Jinja2 模板自动转义关闭、B702Mako 模板使用、B704MarkupSafe Markup 构造 XSS等插件共同构成 Bandit 对模板/标记类 XSS 问题的检测面。该插件在仓库中的注册方式位于 setup.cfg 的 entry point 配置bandit.plugins ... # bandit/plugins/django_xss.py django_mark_safe bandit.plugins.django_xss:django_mark_safe ...即插件名为django_mark_safe实现函数为bandit.plugins.django_xss.django_mark_safe。检测原理识别 safestring 的可疑调用插件核心函数定义在 bandit/plugins/django_xss.py其触发条件分两层模块导入前置条件context.is_module_imported_like(django.utils.safestring)即当前文件中必须导入了django.utils.safestring模块from django.utils import safestring等写法均可被识别受影响的调用名单context.call_function_name命中以下任一函数名affected_functions [ mark_safe, SafeText, SafeUnicode, SafeString, SafeBytes, ]也就是说只要检测到对上述任一 API 的调用Call 节点插件就会取第一个实参context.node.args[0]进入风险分析流程。为什么 mark_safe 危险mark_safe的语义是声明该字符串为安全内容无需转义。它原本用于把已知安全的静态 HTML 片段交给模板渲染但如果把来自用户输入、数据库、外部接口的不可信字符串也标记为 safeDjango 模板引擎将跳过转义把其中的script、onerror等载荷原样渲染到页面形成存储型或反射型 XSS。B703 的任务正是在静态层面判断mark_safe收到的参数是否可证明是安全的。风险判定逻辑安全与不安全如何划分插件不会对每个mark_safe调用无脑告警而是先做一轮确定性过滤xss context.node.args[0] if not ( isinstance(xss, ast.Constant) and isinstance(xss.value, str) ): return check_risk(context.node)若第一个实参是字符串字面量如mark_safe(bHello/b)插件认为内容可信不报问题若实参是变量、函数调用、格式化表达式等动态值则进入check_risk做深度分析。check_risk 的三类实参分析check_riskbandit/plugins/django_xss.py根据实参的 AST 节点类型分三种路径1. 变量ast.Name向上回溯到最近的模块或函数定义层_bandit_parent链先判断该变量是否为函数参数is_param False if isinstance(parent, ast.FunctionDef): for name in parent.args.args: if name.arg xss_var.id: is_param True break函数参数一律视为不安全——因为参数由调用方传入无法在函数体内证明其来源。之后调用evaluate_var在函数体内做赋值追溯看该变量此前是否被赋值为可证明安全的值。2. 函数调用ast.Call调用evaluate_call分析目前主要支持对字符串常量调用format()的形式如b{}/b.format(x)。源码中明确以TODO标注了当前限制带关键字参数的format调用call.keywords会被直接判定为不评估即走不安全路径if ( isinstance(call.func.value, ast.Constant) and call.func.attr format ): evaluate True if call.keywords: evaluate False # TODO(??) get support for thisevaluate_call会逐个检查format的位置参数字符串字面量安全变量需递归调用evaluate_var验证嵌套调用需递归evaluate_call*[...]展开形式ast.Starred会把列表元素并入参数继续分析。只有全部参数都被证明安全时才判定整个调用安全。3. 二元运算 / 格式化表达式ast.BinOp针对b%s/b % var这类%格式化transform2callbandit/plugins/django_xss.py会把它等价转换为format()调用左侧必须是字符串字面量运算符必须是取模%AST 中为ast.Mod右侧是元组时取其元素作为参数否则单值作为参数转换后交给evaluate_call走同一套安全判定。变量追溯的核心DeepAssignationevaluate_var依赖DeepAssignation类bandit/plugins/django_xss.py做轻量级数据流分析它在函数/模块体内按行号顺序遍历语句寻找目标变量的赋值点并支持以下控制流结构AST 节点处理方式Assign直接赋值目标为变量或元组解包text, url ...时记录赋值来源AugAssign增量赋值记录右侧来源If / For / While同时检查body与orelse分支任一分支不安全则不安全Try / ExceptHandler依次检查body、handlers、orelse、finalbody四个部分With检查with ... as var绑定以及语句体内赋值FunctionDef若目标变量是函数参数则判定不受外部赋值影响保持不安全对每个赋值来源再递归评估字符串字面量 → 安全变量 → 递归追溯调用 → 交给 evaluate_call其他如文件读取、网络返回值→ 不安全。只有在所有可能分支都被证明安全时变量才被认定为安全。报告输出字段与严重级别当插件判定不安全时会返回一个bandit.Issuereturn bandit.Issue( severitybandit.MEDIUM, confidencebandit.HIGH, cweissue.Cwe.BASIC_XSS, textdescription, )对应到终端输出的形态源码 docstring 中的示例 Issue: [B703:django_mark_safe] Potential XSS on mark_safe function. Severity: Medium Confidence: High CWE: CWE-80 (https://cwe.mitre.org/data/definitions/80.html) Location: examples/mark_safe_insecure.py:159:4 More Info: https://bandit.readthedocs.io/en/latest/plugins/b703_django_mark_safe.html 158 str_arg could be insecure 159 safestring.mark_safe(str_arg)各字段含义Severity严重性 MediumXSS 属于常见但非直接 RCE 的中等级别风险Confidence置信度 HIGH调用点明确命中受影响的 API 名单判定可信度高CWE CWE-80即 Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)对应常量定义在 bandit/core/issue.pyBASIC_XSS 80text固定描述Potential XSS on mark_safe function.。实际运行命令与输出示例在仓库根目录执行 Bandit即可对示例文件进行扫描。仓库提供了三份配套示例examples/mark_safe.py模块级变量赋值后调用mark_safeexamples/mark_safe_secure.py各类安全写法的集合examples/mark_safe_insecure.py各类不安全写法的集合。基本命令bandit -r examples/ -ll-r递归扫描目录-ll将报告级别放宽到中等级别MEDIUM确保 B703 的 Medium 告警能被输出。仅针对单个文件bandit examples/mark_safe_insecure.py不安全示例速览examples/mark_safe_insecure.py 覆盖了插件能识别的大量危险场景包括def test_insecure(str_arg): safestring.mark_safe(str_arg) # 函数参数一律不安全 def test_insecure_with_assign(str_argNone): if not str_arg: str_arg could be insecure safestring.mark_safe(str_arg) # 有 if 分支赋值为字面量但另一分支仍是参数 def format_arg_insecure(cls onloadalert(\xss\)): my_insecure_str insecure_function(insecure, clscls) safestring.mark_safe(b{} {}/b.format(my_insecure_str, STR)) # format 含不安全实参 def with_insecure(path): with open(path) as f: safestring.mark_safe(f.read()) # 文件读取结果不安全 def some_insecure_case(): if ...: my_secure_str insecure_function(insecure, cls...) elif ...: my_secure_str Secure else: my_secure_str Secure safestring.mark_safe(my_secure_str) # 分支之一不安全 → 整体不安全此外还包括SafeText/SafeUnicode/SafeString/SafeBytes的同型调用、try/except/else/finally各分支的不安全赋值、%格式化b%s/b % var、*list展开、关键字参数与**dict形式、for/while循环累加、模块级外部变量shadow 场景、元组解包后取用不可信元素等约 29 个告警点。安全示例速览examples/mark_safe_secure.py 展示了不会触发告警的写法可视为修复参考safestring.mark_safe(bsecure/b) # 字符串字面量 my_secure_str bHello World/b safestring.mark_safe(my_secure_str) # 变量被赋值为字面量 my_secure_str, _ (bHello World/b, ) safestring.mark_safe(my_secure_str) # 元组解包字面量来源 also_secure_str my_secure_str safestring.mark_safe(also_secure_str) # 链式追溯 def format_secure(): safestring.mark_safe(b{}/b.format(secure)) my_secure_str secure safestring.mark_safe(b{}/b.format(my_secure_str)) # format 参数可证明安全 def all_secure_case(): if ...: my_secure_str Secure elif ...: my_secure_str Secure else: my_secure_str Secure safestring.mark_safe(my_secure_str) # 全部分支安全注意安全示例中format的关键字参数与**dict两种写法被显式标注了# nosec见format_secure内第 38、39 行percent_secure内第 49 行——这印证了插件当前对关键字参数形式format调用暂不支持评估对应源码中的TODO需要开发者用# nosec显式豁免。测试验证与版本沿革Bandit 功能测试 tests/functional/test_functional.py 对该插件有完整的回归保障test_mark_safe第 152 行扫描 examples/mark_safe.py期望结果为 1 个 MEDIUM/HIGH 告警test_django_xss_secure第 536 行扫描mark_safe_secure.py期望0 告警防止误报test_django_xss_insecure第 549 行扫描mark_safe_insecure.py期望29 个 MEDIUM/HIGH 告警防止漏报。两个方向一致的测试同时约束了插件既不漏报、也不过度误报的行为边界。版本信息方面插件源码 docstring 记载B703 自Bandit 1.5.0引入1.7.3版本起补充了 CWE 信息即告警输出中的CWE: CWE-80行。最佳实践如何规避与修复结合插件判定逻辑Django 项目中使用mark_safe时应遵循以下原则只对静态字面量使用 mark_safemark_safe(b标题/b)这类写法不会触发告警动态内容走 Django 的自动转义模板中直接用{{ var }}即可引擎默认转义不要为了省事先mark_safe再插入模板需要输出富文本时使用专门的净化库对不可信 HTML 先做白名单清洗如 bleach 类工具清洗后的结果再标记安全优先使用format_htmlDjango 官方推荐django.utils.html.format_html作为mark_safe的安全替代——它会先转义参数再拼接插件文档的 seealso 部分也明确列入了该 API明确给函数参数输入边界mark_safe的实参若是函数参数会被 B703 直接判为不安全应在调用前完成净化或转义对无法静态证明安全的format关键字参数形式需要在代码中显式加# nosec注释并人工复核对应插件源码中的 TODO 限制。小结B703django_mark_safe是 Bandit 针对 Django safestring 系列 API 的专用 XSS 检测插件它通过模块导入检测 受影响函数名单圈定调用点再以DeepAssignation变量追溯、format/%格式化调用分析等手段区分可证明安全与可能不安全的动态值最终以Medium 严重性 / High 置信度 / CWE-80的标准 Issue 格式输出告警。将其纳入 CI 流水线配合mark_safe_secure.py/mark_safe_insecure.py两组对照样例可以系统性地约束 Django 项目中的手动标记安全内容行为从源头降低 XSS 风险。赞分享SAST应用安全【免费下载链接】banditBandit is a tool designed to find common security issues in Python code.项目地址https://gitcode.com/gh_mirrors/ba/bandit点击查看免费下载相关推荐如何防范Django XSS漏洞Bandit安全检测插件的终极指南如何防范Django XSS漏洞Bandit安全检测插件的终极指南 Bandit是一款专为Python代码设计的安全检测工具能够帮助开发者发现常见的安全问题SAST应用安全UI-TARS 1.5部署实战从单条命令跑通到多副本稳定运行的完整路径UI TARS 1.5部署实战从单条命令跑通到多副本稳定运行的完整路径 这篇文章以开源 GUI agent 模型 UI TARS 1.5 为例走一遍从部署选SAST应用安全aisuite 语音转写如何调用 transcriptions.create 并让 language、prompt 在 OpenAI、Deepgram 与 Google 间自动映射aisuite 语音转写如何调用 transcriptions.create 并让 language、prompt 在 OpenAI、Deepgram 与 GoSAST应用安全上一篇如何快速上手OpenIPC10分钟搭建你的第一个IP摄像头系统下一篇ScudCloudLinux用户必备的终极Slack桌面客户端完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表