介绍
Harbor 会员门户允许忘记密码的用户恢复账户。重置邮件携带临时令牌:持有令牌应当只允许为特定账户修改一次密码。支持团队发现,链接可以重复使用,还能修改其他账户的密码。
你将先正常恢复一个虚构账户,观察不安全行为,再修复一个小型 Python 处理函数。无需 Python 经验;课程在编辑前解释函数与条件。你应能输入终端命令并切换标签页。
Portal Web 是应用,Mailbox Web 是模拟邮件,App Server 是预置代码。它们位于同一台独立训练实例中,不发送外部邮件。令牌、时间比较、数据库事务、密码修改与登录都真实执行;这个小应用并非生产身份系统。
正常恢复账户
本步骤中,你将通过重置邮件修改密码并登录,建立修复后必须保留的正常业务行为。
账户标识用户,密码证明其可以登录。忘记密码时,重置链接临时授权修改密码,其中的令牌不是密码。本地邮箱允许你扮演两个虚构账户 maya 和 noah 的所有者,初始密码均为 StartPass42!。仅使用这些训练账户。
在 Portal Web 的 Request a reset link 中,将 Recovery account 填为 maya,点击 Send reset link。页面显示 Link requested. Open Local Mailbox.。刷新 Mailbox Web,最新邮件在顶部,显示收件人、Open reset link 和令牌字符串。
点击邮件中的 Open reset link,同一标签页打开重置表单,自动填写 Token 和 Reset account。保持两者不变,在 New password 输入 FirstPass42!,点击 Reset password。预期出现 Reset accepted 和 HTTP 200。
HTTP 是浏览器与服务器之间的请求和响应对话。此处 200 表示接受,403 表示请求被拒绝,500 表示应用错误。连接错误与安全拒绝不同。
打开 Portal Web,必要时刷新,在 Sign in 中填写 Login account 为 maya、Password 为 FirstPass42!,点击 Sign in。预期出现 Login accepted。有效重置必须修改真实密码,而不仅仅更换页面文字。
若丢失链接,按相同步骤重新申请并使用最新邮件。链接有效期为 30 分钟。过期或输错无需重启环境。
观察失效的边界
本步骤中,你将进行可丢弃的演示修改,区分令牌的所有者、有效期与一次性权限。
浏览器发送具有名称和值的表单字段。用户能够修改字段,因此服务器不能因为邮件最初填写了 Reset account 就信任它。服务器另有令牌记录:
| 字段 | 含义 |
|---|---|
account |
可以使用此令牌的账户 |
expires_at |
允许使用的截止时间,以固定时间起点后的秒数存储 |
used |
使用前为 0,使用后为 1 |
新令牌只有在到期前才对其所有者有效。成功重置后变成已使用,不能恢复可用状态。未使用的令牌也会随时间过期。已使用或过期的令牌都不能授权再次重置。
在 Portal Web 为 maya 申请新链接,刷新 Mailbox Web,打开最新邮件,将 maya 的密码改为 DemoPass42!。重新打开同一封邮件的链接,再改为 ReplayDemo42!。两次均显示 Reset accepted。第二次本应拒绝:这就是重放,重复使用本应耗尽的权限。
再次打开同一链接,将 Reset account 改为 noah、New password 改为 CrossDemo42!,点击 Reset password,请求竟被接受。在 Portal Web 用 noah 和 CrossDemo42! 登录。为 maya 签发的链接修改了 noah,说明服务器没有将令牌绑定到账户。
最后回到 Portal Web 的重置表单,将 Token 填为 expired-demo、Reset account 填为 maya、New password 填为 ExpiredDemo42!,点击 Reset password。该预置令牌在环境准备前一小时就已过期,却仍被接受,说明服务器忽略了时间。不必等待计时器。
这些账户用于可丢弃的演示,后续修复测试使用干净的账户副本。保留当前观察到的状态,无需直接编辑数据库。
绑定并消费令牌
本步骤中,你将修复服务器的小函数,在不改变演示账户的前提下测试它。
切换到 App Server,初始目录为 /home/labex/project/reset。cat 显示文本文件,其参数是文件名。读取处理函数:
cat handler.py
第一行导入两个预置辅助函数。def 定义函数;括号中的名称是参数,即一次调用的输入。eligible(row, account, now) 接收存储的令牌记录、提交的账户与服务器当前时间。row["account"] 读取记录字段。row is None 表示没有找到令牌。缩进的 if 代码块仅在条件成立时执行;return False 拒绝权限,return True 授予权限。Python 用四个前导空格定义此处的代码块。
当前函数对所有存在的令牌返回 True,解释了三种漏洞。reset 还调用了只改密码、不消费令牌的 unsafe_reset。
修复要求所有条件同时成立。== 比较值,> 表示大于,and 要求两侧都成立;or 只要求一侧成立,会让一个有效属性掩盖另一个无效属性。未来的到期时间大于 now,等于当前时间就已经过期。used == 0 仅允许未使用的令牌。
提供的 consume_and_reset 启动 SQLite 事务,读取当前令牌,调用你的 eligible,然后一起提交密码修改与 used = 1。这叫原子性:两项修改一起成功。它先锁定再读取,避免竞争请求都看到未使用状态。你无需实现锁或密码哈希。
用终端文本编辑器 nano 打开小文件:
nano handler.py
将文件内容替换为以下代码。可以用方向键和 Backspace/Delete 删除旧文本,再粘贴完整内容。准确保留空格和标点;括号允许长条件跨行书写。
from framework import unsafe_reset, consume_and_reset
def eligible(row, account, now):
if row is None:
return False
return (row["account"] == account
and row["expires_at"] > now
and row["used"] == 0)
def reset(db, token, account, password):
return consume_and_reset(db, token, account, password, eligible)
按 Ctrl+O 保存,按 Enter 确认 handler.py,再按 Ctrl+X 退出。每个 Web 请求都会读取保存后的函数,无需重启服务。如果出现 HTTP 500,重新打开文件,检查缩进、括号和引号。
python3 运行 Python 程序。预置检查器接收处理函数路径,在临时 SQLite 数据上启动独立 HTTP 应用,测试真实函数,不修复代码,也不改变现有账户:
python3 /opt/reset-lab/check.py handler.py
预期出现十一行 PASS,包括 valid reset accepted、replay denied、competing use has exactly one winner。不安全的编辑会显示 FAIL 和失败属性。拒绝所有请求会导致合法重置与登录检查失败。修改浏览器字段或现有数据库无法修复针对新数据测试的代码。需要时重新打开 nano handler.py,保存修正,再运行同一检查器。
证明账户恢复仍可用
本步骤中,你将操作修复后的实际服务,并将结果与隔离安全检查联系起来。
在 Portal Web 为 maya 申请新链接,刷新 Mailbox Web,打开最新邮件,将密码改为 ResetSafe42!。预期 Reset accepted 和 HTTP 200。再次打开同一封邮件的链接,尝试为 maya 设置 ReplayAgain42!。预期 Reset denied 和 HTTP 403。
再申请一个新的 maya 链接,打开后将 Reset account 改为 noah,尝试 BlockedCross42!,预期拒绝。拒绝不应消费所有者尚未使用的链接:再次打开同一邮件,保留 maya,设置 ResetSafe42!,预期成功。
在重置表单中尝试 Token expired-demo、Reset account maya、New password BlockedExpired42!,预期拒绝。然后在 Portal Web 用 maya 和 ResetSafe42! 登录,必须显示 Login accepted,说明失败尝试未改变密码。也可用演示密码 CrossDemo42! 登录 noah;修复后的端点保留其他账户状态。
在 App Server 重新运行可丢弃的回归检查:
python3 /opt/reset-lab/check.py handler.py
十一项应全部通过,包括两个同时请求仅有一个成功。重复检查不会改变现有账户记录。这同时验证了允许恢复和拒绝滥用;关闭重置页面或始终拒绝不是合格修复。
训练应用使用模拟投递和简化的身份处理。生产恢复还需安全邮件、速率限制、会话处理与密码策略等,不在此次修复范围内。OWASP 重置指南 提供更广泛的背景。
总结
你完成了真实密码重置,演示重放、跨账户和过期漏洞,并修复一个小型 Python 处理函数。存储的账户归属、未来到期时间、未使用状态与原子消费现在共同生效。可丢弃的 HTTP 测试证明合法恢复、拒绝滥用、其他账户不变,以及竞争使用时只有一个成功。员工门户挑战要求你将同样的规则应用于另一个错误条件。



