恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Wagtail 2.7.3 安全版本深度解析:CVE-2020-11037 密码保护页面时序攻击漏洞的修复原理与实践

  • 首页
  • 资讯中心
  • /
  • Wagtail 2.7.3 安全版本深度解析:CVE-2020-11037 密码保护页面时序攻击漏洞的修复原理与实践

相关资讯

正则灾难性回溯导致Agent内存爆炸:一次OOM事故复盘 2026/9/14 8:13:25
n8n-mcp 用 addConnection 给 IF 节点两个输出连线都落在同一输出怎么修正? 2026/9/14 8:13:25
aws-cli 中 appconfig get-environment 详解:查询 AWS AppConfig 环境详情与状态 2026/9/14 8:08:24

最新资讯

Apache Arrow PyArrow JSON 读取实战:read_json 类型推断、ParseOptions/ReadOptions 定制与 open_json 增量解析
在 OpenShift 4.x 上安装 Cilium:默认配置、环境要求与安装路径全解析
高校校车票务系统开发实战:SSM框架与高并发优化
太阳方位角计算与冰冻状态判定算法详解
从YOLOv8到YOLO26:电子元器件目标检测与DeepSeek融合质检
如何用 axum-macros 的 derive(FromRequest) 定义自定义提取器并控制 rejection

今日推荐

ASP+Access库存管理系统源码部署与IIS配置实战指南
基于SSM框架的毕业季旧物分类处理系统设计与实现
MATLAB FFT频谱仿真:从DFT原理到参数设置与窗函数选择

本周热门

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化
Flutter应用改名全指南:从Android到iOS的配置与工具实践

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

Wagtail 2.7.3 安全版本深度解析:CVE-2020-11037 密码保护页面时序攻击漏洞的修复原理与实践

发布时间:2026/9/14 8:13:25
Wagtail 2.7.3 安全版本深度解析:CVE-2020-11037 密码保护页面时序攻击漏洞的修复原理与实践 Wagtail 2.7.3 安全版本深度解析CVE-2020-11037 密码保护页面时序攻击漏洞的修复原理与实践【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtailWagtail 2.7.3发布于 2020 年 5 月 4 日是一个针对安全问题的紧急补丁版本其核心工作是修复 CVE-2020-11037通过 Wagtail Privacy隐私控件使用共享密码保护的页面与文档其密码校验存在潜在时序攻击timing attack风险。本文以 2.7.3 发布说明 为主线结合当前仓库中密码校验、视图限制与相关测试的源码实现完整还原漏洞成因、攻击面、修复手段与升级注意事项帮助开发者理解共享密码保护机制的正确打开方式。一、漏洞概述CVE-2020-11037 是什么发布说明docs/releases/2.7.3.rst明确给出了该漏洞的核心信息影响对象通过 Wagtail Privacy 控件以共享密码shared password保护的页面Page或文档Document漏洞性质密码校验使用逐字符字符串比较character-by-character string comparison攻击者若能以较高精度测量该校验耗时便可利用时间差异逐步推断出密码内容实际可行性官方评估认为该攻击在局域网local network上可行但在公网public internet上不可行——因为公网上的网络抖动会淹没纳秒/微秒级的时间差异致谢该问题由 Thibaud Colas 报告。值得注意的是这一修复随后被同步回滚到仍在维护的 2.8.x 分支见 2.8.2 发布说明 与 2.9 发布说明 中的同一条目意味着所有使用 2.7.x 及更早版本、并启用了密码保护功能的站点都应尽快升级。二、漏洞根源密码保护机制与逐字符比较的隐患要理解这个漏洞需要先搞清楚 Wagtail 中共享密码保护从模型到校验的完整链路。2.1 视图限制模型Privacy 控件的底层数据页面和文档的隐私控制统一由视图限制view restriction模型承载其基类定义在 wagtail/models/view_restrictions.pyclass BaseViewRestriction(models.Model): NONE none PASSWORD password GROUPS groups LOGIN login RESTRICTION_CHOICES ( (NONE, _(Public)), (PASSWORD, _(Private, accessible with a shared password)), (LOGIN, _(Private, accessible to any logged-in users)), (GROUPS, _(Private, accessible to users in specific groups)), ) restriction_type models.CharField(max_length20, choicesRESTRICTION_CHOICES) password models.CharField( verbose_name_(shared password), max_length255, blankTrue, help_text_( Shared passwords should not be used to protect sensitive content. Anyone who has this password will be able to view the content. ), ) groups models.ManyToManyField(Group, verbose_name_(groups), blankTrue)从源码可以确认三个事实密码保护只是 Privacy 控件的四种选项之一另外三种是 Public、登录用户可见、指定用户组可见password是一个最长 255 字符的共享明文密码即一把钥匙开多把锁任何知道该密码的人都能访问受保护内容——这决定了它只能用于轻量级的内容隔离不能保护敏感数据模型自身的help_text也明确提示了这一点模型是抽象的abstract True实际由PageViewRestriction页面等具体模型继承实现。2.2 密码校验表单漏洞所在的代码漏洞的根源位于密码校验表单 wagtail/forms.py 中的PasswordViewRestrictionFormclass PasswordViewRestrictionForm(forms.Form): password forms.CharField( labelgettext_lazy(Password), widgetforms.PasswordInput ) return_url forms.CharField(widgetforms.HiddenInput) def __init__(self, *args, **kwargs): self.restriction kwargs.pop(instance) super().__init__(*args, **kwargs) def clean_password(self): data self.cleaned_data[password] if not constant_time_compare(data, self.restriction.password): raise forms.ValidationError( _(The password you have entered is not correct. Please try again.) ) return data在 2.7.3 之前clean_password使用的是普通字符串相等比较data ! self.restriction.password或。Python 的字符串比较一旦发现第一个不同的字符就会提前返回比较消耗的时间与前多少个字符相同成正比。攻击者据此可以尝试第一位字符的所有可能取值记录每次校验的耗时耗时最长的那次说明该字符与真实密码第一位相同比较在第二位才失败逐位推进最终在不依赖字典、不暴力枚举完整密码的情况下仅靠时间侧信道还原整个密码。这正是发布说明所描述的逐字符比较 时间差异的组合攻击。修复后的代码改用 Django 提供的constant_time_compare来自django.utils.crypto该函数保证无论比较结果如何执行时间都恒定从而从根源上消除时间侧信道。2.3 校验入口authenticate_with_password 视图表单在何处被提交页面侧的入口是 wagtail/views.py 中的authenticate_with_password视图def authenticate_with_password(request, page_view_restriction_id, page_id): restriction get_object_or_404(PageViewRestriction, idpage_view_restriction_id) page get_object_or_404(Page, idpage_id).specific if request.method POST: form PasswordViewRestrictionForm(request.POST, instancerestriction) if form.is_valid(): return_url form.cleaned_data[return_url] if not url_has_allowed_host_and_scheme( return_url, request.get_host(), request.is_secure() ): return_url settings.LOGIN_REDIRECT_URL restriction.mark_as_passed(request) return redirect(return_url) else: form PasswordViewRestrictionForm(instancerestriction) action_url reverse( wagtailcore_authenticate_with_password, args[restriction.id, page.id] ) return page.serve_password_required_response(request, form, action_url)值得注意的细节视图通过url_has_allowed_host_and_scheme对return_url做了开放重定向防护校验失败时回退到settings.LOGIN_REDIRECT_URL校验成功后调用restriction.mark_as_passed(request)将限制 ID 写入 session见 wagtail/models/view_restrictions.py 的mark_as_passed实现后续访问直接放行若此前没有 session cookie还会把 session 设为浏览器会话结束时过期密码输入错误的响应仍渲染密码页测试中wrong password 应重新显示密码页的断言见 wagtail/tests/test_page_privacy.py避免泄露密码是否正确这一信息本身。文档侧的入口逻辑相同位于 wagtail/documents/views/serve.pyserve_password_required相关处理同样复用PasswordViewRestrictionForm因此页面与文档两类资源共享同一个漏洞与同一处修复。三、访问控制链路密码保护是如何挡在页面前的了解了校验点再看整条访问控制链路可以更完整地评估漏洞的攻击面。页面路由与限制判定围绕 wagtail/models/pages.py 展开def get_view_restrictions(self): Return a query set of all page view restrictions that apply to this page. This checks the current page and all ancestor pages for page view restrictions. ... page_ids_to_check set() def add_page_to_check_list(page): # If the page is an alias, add the source page to the check list instead if page.alias_of: add_page_to_check_list(page.alias_of) else: page_ids_to_check.add(page.id) # Check current page for view restrictions add_page_to_check_list(self) # Check each ancestor for view restrictions as well for page in self.get_ancestors().only(alias_of): add_page_to_check_list(page) return PageViewRestriction.objects.filter(page_id__inpage_ids_to_check)结合 BaseViewRestriction.accept_request 可以还原完整语义限制会向下继承get_view_restrictions同时检查当前页及其所有祖先页因此对父页面设置的密码保护会作用于整个子树对应测试test_view_restrictions_apply_to_subpages别名页沿用源页限制如果页面是别名alias会解析到源页面再查询限制别名页不能设置自己的限制对应测试test_view_restrictions_apply_to_aliases密码型限制的判定依赖 sessionaccept_request检查passed_view_restrictions_session_key中是否已记录该限制 ID未通过则返回False随后框架渲染密码输入页。Page上还提供了渲染密码页的钩子wagtail/models/pages.pypassword_required_template None def serve_password_required_response(self, request, form, action_url): password_required_template self.password_required_template or getattr( settings, WAGTAIL_PASSWORD_REQUIRED_TEMPLATE, wagtailcore/password_required.html, ) ... context self.get_context(request) context[form] form context[action_url] action_url return TemplateResponse(request, password_required_template, context)模板解析顺序为Page子类上的password_required_template属性如 wagtail/test/testapp/models.py 中EventPage的自定义模板tests/event_page_password_required.html→ 全局设置WAGTAIL_PASSWORD_REQUIRED_TEMPLATE→ 默认模板wagtailcore/password_required.html。文档侧对应WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE与wagtaildocs/password_required.html见 wagtail/documents/templates/wagtaildocs/password_required.html 与 wagtail/documents/views/serve.py。也就是说攻击者只要访问任一受密码保护的页面就会进入提交密码 → 表单clean_password校验这条路径而 2.7.3 之前的该校验正是时序攻击的靶点。四、修复验证源码与测试如何保证不再回归4.1 修复落点2.7.3 的实际修复就是把 wagtail/forms.py 中clean_password的普通比较替换为constant_time_comparefrom django.utils.crypto import constant_time_compare if not constant_time_compare(data, self.restriction.password): raise forms.ValidationError(...)django.utils.crypto.constant_time_compare底层使用hmac.compare_digest实现而后者在 CPython 中基于恒定时间的 C 实现不依赖 Python 字符串比较的短路特性。同一模式也用于其他安全敏感的比较例如 wagtail/images/utils.py 中的校验逻辑同样导入constant_time_compare可作为团队在 Wagtail 生态内处理此类问题的参照。4.2 测试佐证密码保护的完整行为由测试锁定见 wagtail/tests/test_page_privacy.pytest_anonymous_user_must_authenticate匿名用户访问受保护页返回wagtailcore/password_required.html提交错误密码wrongpassword后仍重新渲染密码页且页面保持在原模板提交正确密码swordfish后重定向回return_url并可通过后续请求验证 session 已放行test_view_restrictions_apply_to_subpages/test_view_restrictions_apply_to_aliases验证限制的子树继承与别名解析行为test_password_protected_page_headers验证密码页的 HTTP 头行为。文档侧对应的测试位于 wagtail/documents/tests/test_collection_privacy.py其中还包含自定义模板WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE的覆盖测试。这些测试共同保证修复后密码校验的外部行为错误提示、重定向、模板渲染完全不变变化的只有内部比较方式的恒定时间复杂度——这正是安全补丁的典型形态对用户零感知对攻击者关上门。五、升级与实践建议5.1 升级路径受影响范围使用 2.7.x 及更早版本、且通过 Privacy 控件启用了共享密码保护的站点。未使用密码保护的站点不暴露在该漏洞下修复版本2.7.3本版本以及后续同步修复的 2.8.2、2.9 等维护分支执行方式按常规方式升级 Wagtail 依赖即可例如pip install wagtail2.7.3本次修复不涉及数据库迁移或配置变更无破坏性改动。5.2 安全使用密码保护的建议结合源码与发布说明建议如下不要用共享密码保护敏感内容BaseViewRestriction.password字段的help_text已明确指出共享密码不应被用于保护敏感内容因为知道密码即可访问、无法审计到个人敏感内容应改用登录用户可见LOGIN或指定用户组可见GROUPS限制保持版本更新安全修复通常以点版本patch release发布应关注 docs/releases/index.rst 与各版本发布说明及时跟进 2.7.3 这类安全补丁理解攻击边界该时序攻击在局域网场景下才被认为可行公网环境下网络噪声会掩盖时间差异但这不构成延后升级的理由——部署在网络内部的 Wagtail 实例内网 CMS、企业站点同样在攻击面内如需定制密码页通过WAGTAIL_PASSWORD_REQUIRED_TEMPLATE页面或WAGTAILDOCS_PASSWORD_REQUIRED_TEMPLATE文档全局配置或为特定页面类型设置password_required_template属性注意自定义模板仍应输出表单与action_url参考默认模板 wagtail/templates/wagtailcore/password_required.html。六、小结CVE-2020-11037 是 Wagtail 历史上一次典型的小洞大修漏洞点只有一行字符串比较但其背后是完整的共享密码保护链路——BaseViewRestriction模型存储共享密码、PasswordViewRestrictionForm执行校验、authenticate_with_password视图处理提交、Page与文档 serve 逻辑决定渲染与放行。Wagtail 2.7.3 通过引入constant_time_compare消除了比较时间与密码内容的关联并以 wagtail/tests/test_page_privacy.py 与 wagtail/documents/tests/test_collection_privacy.py 锁定行为在保持用户体验不变的前提下关闭了时间侧信道。对于任何启用了 Privacy 密码保护的 Wagtail 站点将版本升级到 2.7.3或 2.8.2 / 2.9是最直接、最稳妥的处置方式。【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号