如何防止 Proxy 捕获凭据、支付及敏感数据?
结论
如果你配置了 Burp Suite,并让浏览器信任 Burp 的 CA 证书,代理就能看到明文形式的账号、密码和支付字段。这是正常现象。
HTTPS/TLS 防止的是未经授权的网络窃听和篡改,以下主体仍然可以看到数据:
- 用户正在操作的浏览器;
- 浏览器主动信任的 HTTPS 拦截代理;
- 已取得设备控制权并能安装受信任根证书的攻击者;
- 接收和处理请求的服务器。
如果 Burp 无需安装或信任证书,仅接入同一网络就能读取 HTTPS 请求正文,则需要警惕。网站可能没有正确使用 HTTPS,证书校验可能已被关闭,设备的信任体系也可能已经遭到破坏。
为什么 Burp 能看到密码
正常的 HTTPS 连接大致如下:
Browser ── TLS encrypted connection ── Website
网络中的普通代理、路由器或 Wi-Fi 管理者只能看到有限的连接元数据,无法直接读取经过 TLS 加密的请求正文。
进行 HTTPS 拦截时,Burp 实际上建立了两条连接:
Browser ── TLS ── Burp ── TLS ── Website
Burp 会为目标网站动态签发证书。只有当浏览器信任 Burp CA,或者证书验证已被关闭时,浏览器才会接受这张证书。此时,Burp 是浏览器认可的 TLS 终点,因此可以:
- 解密浏览器发送的数据;
- 显示或修改请求;
- 重新加密请求并转发给真实网站。
这并不是 Burp 破解了 HTTPS,而是测试人员主动将 Burp 加入了设备的信任链。
开发者应采取的标准措施
1. 所有敏感通信必须使用 HTTPS
登录、支付、密码重置、会话接口及相关页面都应只允许通过 HTTPS 访问,并将 HTTP 请求重定向到 HTTPS。
例如,可以在响应中启用 HSTS:
Strict-Transport-Security: max-age=31536000; includeSubDomains
只有确认所有子域都能持续支持 HTTPS 后,才应使用 includeSubDomains。将域名提交到浏览器的 HSTS preload list 也是一项长期决策,需要先评估整个域名体系。
同时还应做到:
- 使用有效且配置正确的 TLS 证书;
- 禁止 mixed content;
- 不在生产配置中提供忽略证书错误的选项;
- 客户端遇到证书错误时终止连接;
- 定期检查 TLS 配置和证书续期情况。
2. 密码应放在请求正文中,而不是 URL 中
登录请求通常使用 POST,并将密码放在 HTTPS 加密的请求正文中:
POST /api/login HTTP/1.1
Content-Type: application/json
{
"email": "user@example.com",
"password": "correct horse battery staple"
}
不要将密码、支付凭据或访问令牌放入 URL:
https://example.com/login?email=user@example.com&password=secret
URL 更容易被写入浏览器历史、代理日志、服务器访问日志、监控系统及 Referer 等记录。
POST 本身不提供加密,数据的安全性来自 HTTPS。
3. 不要记录敏感字段
代理可以在授权测试环境中看到请求,不代表应用日志也应该保存这些内容。服务器、网关和可观测性平台应删除敏感字段,或对其进行脱敏。
function redact(body) {
const copy = { ...body };
for (const key of [
"password",
"newPassword",
"accessToken",
"refreshToken",
"cardNumber",
"cvv"
]) {
if (key in copy) copy[key] = "[REDACTED]";
}
return copy;
}
还需要检查:
- Web server 和 API gateway 的请求日志;
- APM、错误追踪和 session replay;
- 调试日志与异常对象;
- 前端埋点和分析平台;
- 消息队列、审计记录及数据库备份。
生产环境通常不应记录完整的请求体。
4. 正确保存密码
服务器必须接收用户提交的密码才能验证登录,但不应长期以明文或可逆加密形式保存密码。应使用专门的密码哈希算法,并为每个密码生成独立的随机 salt,例如 Argon2id、scrypt 或 bcrypt。
下面的代码只演示接口形式。具体参数需要根据使用的库、服务器资源和当前安全基线进行评估:
import argon2 from "argon2";
const passwordHash = await argon2.hash(password, {
type: argon2.argon2id
});
const valid = await argon2.verify(passwordHash, submittedPassword);
不要用普通的快速哈希代替密码哈希:
// 不适合存储密码
sha256(password);
5. 安全管理会话
认证成功后,应使用短期、随机且不可预测的会话标识,避免在后续请求中反复发送密码。
Set-Cookie: session=<random-session-id>; Secure; HttpOnly; SameSite=Lax
还应采取以下措施:
- 登录后轮换 session ID;
- 注销或修改密码后撤销相关会话;
- 设置合理的空闲有效期和绝对有效期;
- 对高风险操作要求重新认证;
- 配置 CSRF 防护;
- 防止令牌进入 URL、日志和前端持久化存储。
HttpOnly 可以降低 JavaScript 直接读取 Cookie 的风险,但无法防御已经控制设备或受信任代理的攻击者。
6. 尽量不要让支付数据经过自己的服务器
支付场景通常应使用支付服务商托管页面、嵌入式安全字段或 tokenization。应用服务器接收支付 token,而不是完整的卡号和 CVV:
const payment = await paymentProvider.charge({
paymentMethodToken,
amount: 4999,
currency: "USD"
});
具体的集成方式取决于支付服务商。即使使用第三方组件,仍需检查脚本供应链、CSP、日志以及适用的 PCI DSS 范围。
CVV 等敏感认证数据不应被持久化保存。
7. 降低密码泄露造成的损失
除 HTTPS 外,还需要采取纵深防御措施:
- 支持 WebAuthn/passkeys;
- 为账号提供 MFA;
- 检测异常登录;
- 设置速率限制并防范撞库;
- 发现凭据泄露后撤销会话并要求用户修改密码;
- 对转账、修改邮箱等关键操作进行二次确认。
这些措施无法让受控设备上的密码变得”不可见”,但可以减少单次泄露造成的影响。
是否应在前端再次加密密码
通常不应尝试在 JavaScript 中使用一层”自定义加密”来解决这个问题。
例如:
const encrypted = customEncrypt(password, publicKey);
这种做法不能取代 TLS,原因包括:
- 浏览器在加密前仍然能接触明文;
- 控制设备的人可以修改 JavaScript 或读取输入内容;
- 拦截代理可能替换页面、脚本或公钥;
- 如果加密后的值可以直接用于登录,它可能成为可重放的新凭据;
- 自定义协议容易产生密钥管理、完整性和重放方面的漏洞。
某些专用认证协议会使用 challenge-response、PAKE 或硬件密钥,但必须采用经过审查的标准协议,不能临时设计一套”前端加密密码”方案。
证书固定是否能阻止 Burp
原生移动应用可以在特定威胁模型下考虑 certificate pinning,但这会增加证书轮换、灾难恢复和调试的成本。在已经 root、越狱或被完全控制的设备上,pinning 仍可能被绕过。
普通网站无法依靠常规页面 JavaScript 可靠地实现证书固定。浏览器曾经提供的 HPKP 也已被主流浏览器弃用,不应再作为新方案使用。
Pinning 用来应对特定的中间人风险,并不是为了阻止用户检查自己设备发出的数据。
从用户或管理员角度如何防范恶意代理
如果你并非主动进行安全测试,应检查:
- 系统和浏览器的 proxy 设置;
- 根证书存储中是否存在未知 CA;
- 企业设备管理、杀毒软件或 VPN 是否启用了 TLS inspection;
- 浏览器扩展和本地安全软件是否可信;
- 是否出现证书警告或证书颁发者异常;
- 设备是否感染恶意软件。
不要忽略证书警告,也不要安装来源不明的根证书。Burp 测试结束后,应删除其 CA 证书并恢复代理设置。如果攻击者已经取得管理员权限并能修改系统信任库,应将其视为设备失陷事件。这类问题无法仅靠网站代码解决。
判断是否正常
可以根据以下边界快速判断:
- 主动设置 Burp 并信任其 CA 后看到明文:正常。
- 未信任 Burp CA 时浏览器显示证书错误:正常。
- 未信任任何额外 CA,也未出现警告,却被普通网络代理读取 HTTPS 正文:不正常,需要检查设备、证书链和 TLS 配置。
- 代理能读取 HTTP 请求:正常,但说明敏感数据不应通过该连接发送。
- 网站或第三方服务在日志中保存密码、CVV 或完整认证令牌:这是需要修复的数据处理问题。
安全目标不是让密码在用户自己的浏览器和受信任的 TLS 终点中永远不可见,而是防止未经授权的第三方在传输途中窃听数据,避免服务器进行不必要的存储,并在终端受控或凭据泄露后尽量限制损失。
备注:内容仅供参考。
