API 安全密钥交换#
我们将客户端分为两种主要类型:web/wap(轻量级客户端)和 android/ios(可信客户端)。
对于 web/wap 客户端: 过程首先通过 CSRF 保护获取 RSA 公钥,然后使用该 RSA 公钥解密服务器与当前会话关联的 AES Token。所有后续请求都使用此 AES Token 加密。
- CSRF token 必须直接嵌入页面中,而不是存储在 cookie 中 - 否则,安全收益很小。
- 这种方法有助于确保页面实际上由我们的服务器渲染。
- CSRF token 需要加密,并且每次返回都应该是唯一的(通过嵌入时间戳),尽管它们映射到相同的后端 CSRF token。
- 公钥轮换至关重要:我们运行定时任务定期更新存储的 wap/web 密钥对,之前的密钥在 1 个月后过期(我们的前端会话公钥在没有续订的情况下在 2 周内过期,所以 1 个月提供了安全缓冲)。
对于 android/ios 客户端: 每个应用版本在构建时嵌入唯一的公钥,服务器记录该公钥。这直接解密当前会话的 AES Token,所有后续请求都使用此 AES Token 加密。
- 由于移动应用通常是具有版本特定公钥的可信客户端,通常不需要 CSRF 保护。
- 此机制还可以促进强制版本升级。
为什么这很重要:
- HTTPS 防止 WiFi 劫持和 HTTP 标头和正文内容的数据包嗅探
- API 加密防止机器人抓取、请求伪造和客户端模拟
- Android/iOS 公钥有助于识别特定版本并验证可信客户端,而浏览器无法提供这种级别的保证 - 这就是为什么我们添加 CSRF token 以合理确保请求来自我们渲染的页面
安全标头#
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- 启用 HTTP 严格传输安全(HSTS),指示浏览器仅通过 HTTPS 访问网站
max-age=31536000告诉浏览器记住仅 HTTPS 访问 31,536,000 秒(1 年)includeSubDomains将此规则扩展到当前域的所有子域preload表示网站希望包含在浏览器的内置 HSTS 预加载列表中,即使在首次访问时也强制使用 HTTPS
X-Content-Type-Options: nosniff
- 防止浏览器尝试"嗅探"或猜测资源 MIME 类型的安全功能,强制遵守服务器提供的 Content-Type 标头
nosniff选项防止 MIME 类型混淆攻击,例如浏览器将非脚本文件解释为可执行脚本
X-Frame-Options: SAMEORIGIN
- 防止其他站点通过
<iframe>、<frame>、<embed>或<object>嵌入页面,防止点击劫持攻击 SAMEORIGIN仅允许来自同一源的页面在框架中嵌入当前页面
Content-Security-Policy: [此处省略大量内容]
- 此阻止机制可能导致某些第三方跟踪像素、图像等加载失败,需要监控(通过在末尾添加
report-uri /api/csp-report-endpoint?version=5)。报告包括所有被阻止的内容:JS、CSS、JPG、持久连接、视频资源等。 - 此标头经常更改 - 例如,当搜索引擎重定向到你的网站时,它们嵌入 JS,广告渠道合作伙伴也是如此
- 你需要自己实现
/api/csp-report-endpoint - 包含版本参数,以便在添加新的白名单条目并递增版本号后,你可以忽略来自旧版本的报告(因为此标头通常在 CDN 级别添加,如 Cloudflare,通常具有长缓存时间)
以下是允许所有 Google 和 Facebook 域的示例 Content-Security-Policy:
default-src 'self' data: 'unsafe-inline' blob: 'unsafe-eval' *.google-analytics.com *.googletagmanager.com *.gstatic.com *.googleapis.com *.google.co *.google.com *.google.ad *.google.ae *.google.com.af *.google.com.ag *.google.al *.google.am *.google.co.ao *.google.com.ar *.google.as *.google.at *.google.com.au *.google.az *.google.ba *.google.com.bd *.google.be *.google.bf *.google.bg *.google.com.bh *.google.bi *.google.bj *.google.com.bn *.google.com.bo *.google.com.br *.google.bs *.google.bt *.google.co.bw *.google.by *.google.com.bz *.google.ca *.google.cd *.google.cf *.google.cg *.google.ch *.google.ci *.google.co.ck *.google.cl *.google.cm *.google.cn *.google.com.co *.google.co.cr *.google.com.cu *.google.cv *.google.com.cy *.google.cz *.google.de *.google.dj *.google.dk *.google.dm *.google.com.do *.google.dz *.google.com.ec *.google.ee *.google.com.eg *.google.es *.google.com.et *.google.fi *.google.com.fj *.google.fm *.google.fr *.google.ga *.google.ge *.google.gg *.google.com.gh *.google.com.gi *.google.gl *.google.gm *.google.gr *.google.com.gt *.google.gy *.google.com.hk *.google.hn *.google.hr *.google.ht *.google.hu *.google.co.id *.google.ie *.google.co.il *.google.im *.google.co.in *.google.iq *.google.is *.google.it *.google.je *.google.com.jm *.google.jo *.google.co.jp *.google.co.ke *.google.com.kh *.google.ki *.google.kg *.google.co.kr *.google.com.kw *.google.kz *.google.la *.google.com.lb *.google.li *.google.lk *.google.co.ls *.google.lt *.google.lu *.google.lv *.google.com.ly *.google.co.ma *.google.md *.google.me *.google.mg *.google.mk *.google.ml *.google.com.mm *.google.mn *.google.com.mt *.google.mu *.google.mv *.google.mw *.google.com.mx *.google.com.my *.google.co.mz *.google.com.na *.google.com.ng *.google.com.ni *.google.ne *.google.nl *.google.no *.google.com.np *.google.nr *.google.nu *.google.co.nz *.google.com.om *.google.com.pa *.google.com.pe *.google.com.pg *.google.com.ph *.google.com.pk *.google.pl *.google.pn *.google.com.pr *.google.ps *.google.pt *.google.com.py *.google.com.qa *.google.ro *.google.ru *.google.rw *.google.com.sa *.google.com.sb *.google.sc *.google.se *.google.com.sg *.google.sh *.google.si *.google.sk *.google.com.sl *.google.sn *.google.so *.google.sm *.google.sr *.google.st *.google.com.sv *.google.td *.google.tg *.google.co.th *.google.com.tj *.google.tl *.google.tm *.google.tn *.google.to *.google.com.tr *.google.tt *.google.com.tw *.google.co.tz *.google.com.ua *.google.co.ug *.google.co.uk *.google.com.uy *.google.co.uz *.google.com.vc *.google.co.ve *.google.co.vi *.google.com.vn *.google.vu *.google.ws *.google.rs *.google.co.za *.google.co.zm *.google.co.zw *.google.cat *.googleadservices.com facebook.net *.facebook.net facebook.com *.facebook.com; report-uri /api/csp-report-endpoint?version=5
反机器人保护机制#
我们主要解决两种情况:
对于未注册或已注销的用户: 为注册、验证码和类似端点实施反机器人措施,同时最小化用户摩擦。
对于已认证的用户:
- 为活动设置必要的参与阈值(如最近的交易量)
- 引入 MFA 后,将活动参与限制为用户绑定的 MFA 设备
场景 2 主要面向业务。MFA 机制不仅提供安全保证,还有助于验证设备真实性,实现基于设备的业务限制。
对于场景 1,我们可以使用这些机制减少验证码摩擦:
- 为注册和短信 OTP 等敏感端点实施 Google reCAPTCHA Enterprise(reCAPTCHA v3)或 hCAPTCHA Enterprise 等服务。每个请求包括 Google reCAPTCHA Enterprise 分数:
- reCAPTCHA v3 在浏览时持续评估用户行为,包括交互模式(鼠标移动、滚动、点击)、设备和浏览器信息,以及跨多个页面的会话范围行为分析。
- 基于此行为分析,reCAPTCHA v3 为每个用户请求分配 0.0 到 1.0 的分数。接近 1.0 的分数表示系统相信行为来自真实人类,而较低分数表明自动化脚本或机器人。以下是示例分数分布:

你的后端根据此分数响应(我们要求所有得分低于 0.8 的请求提供验证码)。有许多验证码实现选项可用。
结果: 大多数用户甚至不需要在注册期间输入验证码。只有得分较低的用户面临挑战(验证码或其他挑战方法)。
为什么我们不推荐 IP + 设备阻止或速率限制(速率限制意味着重定向或显示验证码,而不是阻止访问):与上述方法相比,这会产生更多用户摩擦。此外,IP 和设备很容易被伪造(IP 通过 VPN,设备通过模拟),当前浏览器趋势朝着统一用户代理发展,暴露的信息更少:
https://developers.google.com/privacy-sandbox/blog/user-agent-reduction-android-model-and-version
