接口安全设计:HTTPS、TLS、CORS 配置、内容安全策略
接口一暴露到公网,各种攻击手段就盯上来了。你的数据在传输过程中会不会被偷看?别人能不能冒充你的服务器?有人通过浏览器跨域请求能不能搞到敏感信息?你需要一套组合拳来守住这几道防线。
我个人习惯,每次设计新接口时,都会先画一张安全防护层次图,然后逐层落实。下面这张图展示了我常挂在墙上的接口安全框架,咱们一步步拆开讲。
HTTPS 与 TLS:加密通道是地基
先说说 HTTPS。它不是在 HTTP 外面包一层壳,而是用 TLS 协议对通信内容做加密和完整性校验。
我记得有一次做支付接口,对方要求必须用 TLS 1.2 以上。我一开始图省事留了个 TLS 1.0 兼容,结果安全审计直接亮了红灯。从那以后,我定了条规矩:所有生产接口强制 TLS 1.2+,禁用 TLS 1.0/1.1,禁用 SSLv3。
简单概括一下 HTTPS 的握手过程(你心里有个概念就行):
- 客户端发起 ClientHello,带上支持的最高 TLS 版本和密码套件列表。
- 服务端回复 ServerHello,选定版本和套件,并发送证书链。
- 客户端验证证书链有效性(信任锚、域名、有效期、吊销状态)。
- 双方交换密钥参数,生成会话密钥,之后所有数据对称加密传输。
核心要点: 证书必须由受信任的 CA 签发,不要自签名证书用于生产环境。密钥长度建议 RSA 2048 位以上或 ECDSA P-256。
你可能会问:“我系统内部服务之间也需要 HTTPS 吗?”我的建议是:只要跨网络边界,一律 HTTPS。哪怕是内网,也可能被 ARP 欺骗或中间人攻击。我遇到过某团队内网接口用明文,被运维误操作插入抓包工具,结果认证 全部泄露——教训惨痛。
CORS 配置:别让浏览器成为攻击跳板
CORS(跨域资源共享)是浏览器的安全机制。你的接口如果被另一个域的网页通过 Ajax 访问,浏览器默认会拦截响应——除非你的接口明确“同意”。
配置 CORS 其实不复杂,但坑很多。先看一个标准的服务端响应头:
Access-Control-Allow-Origin: https://trusted-frontend.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 3600
我曾经在项目里图省事,把 Access-Control-Allow-Origin 设置成了 *,同时又把 Access-Control-Allow-Credentials 设为了 true。你知道后果吗?浏览器直接报错——通配符不能与凭据一起使用。这是规范硬性规定,很多人第一次碰到都会懵。
| 配置项 | 说明 | 常见陷阱 |
|---|---|---|
Allow-Origin | 允许的源,可填具体域名 | 不要用 * 加凭据;多域名需动态逻辑 |
Allow-Methods | 允许的 HTTP 方法 | 记得加 OPTIONS(预检请求) |
Allow-Headers | 允许的自定义头 | 如果前端传了自定义头,这里必须提前声明 |
Allow-Credentials | 是否允许携带 cookie | 开启后 Origin 不能是 * |
Max-Age | 预检结果缓存秒数 | 太短会增加请求,太长更新不及时 |
避坑指南: 我曾经遇到一个微服务网关统一加了 CORS 头,但某些后端服务自己又加了一组 CORS 头,结果浏览器收到多个同名头按最严格的那个解析,导致跨域失败。所以最好在网关层统一处理,后端服务不要重复设置。
内容安全策略 (CSP):给页面戴上紧箍咒
CSP 是通过 HTTP 响应头 Content-Security-Policy 来告诉浏览器:这个页面只能加载哪些来源的资源。一旦你配置了 CSP,浏览器就像戴上了紧箍咒——任何不在白名单里的脚本、样式、图片都会被阻止。
它的核心价值是防御 XSS 攻击。举个例子,你写了一个博客评论功能,攻击者在评论区插入 <script>alert('xss')</script>,如果没有 CSP,这段脚本就会在用户浏览器里执行。但如果你的 CSP 配置了 script-src 'self',浏览器就会拒绝执行所有非同源脚本,包括内联的 <script> 标签。
我会给你一个生产级别的 CSP 配置参考:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-随机数' https://cdn.trusted.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https://*.img.com;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
report-uri /csp-violation-report;
这里有几个关键点我单独说一下:
- nonce 机制:每个响应生成一个随机 nonce,并嵌入到
<script nonce="...”>中,这样即使攻击者注入脚本,也无法预知 nonce 值。 - report-uri:设置一个收集违规报告的接口。我强烈建议你先开启
Content-Security-Policy-Report-Only模式,收集一周的违规日志后再切换到强制模式——直接上强制可能会把业务搞崩。 - frame-ancestors:防止页面被嵌入到恶意 iframe 中进行点击劫持。设成
'none'你就谁都别想嵌,安全但可能影响功能;需要开放的话就配具体域名。
⚠️ 特别提醒: CSP 一旦生效,eval()、setTimeout(string)、new Function() 等动态执行代码的 API 都会被阻止(除非你加了 'unsafe-eval')。如果你项目里有这类用法,迁移时一定要测试。我见过一个团队用了大量动态模板渲染,上线 CSP 后页面白屏,排查了半天才发现是 eval 被拦截了。
三种策略如何协同
你可能会想:这三个东西是独立的,还是需要一起工作的?我的经验是:它们互补但不冲突。
- HTTPS/TLS 保护传输通道——防止中间人窃听和篡改。
- CORS 保护浏览器侧的跨域访问——防止其他网站滥用你的 API。
- CSP 保护页面自身——限制脚本和资源加载,防御 XSS 和数据泄露。
举个例子,你的接口部署在 https://api.example.com,前端在 https://app.example.com。HTTPS 保证了前端到后端的数据加密;CORS 允许 app.example.com 的请求,同时拒绝其他域;前端页面自身的 CSP 限制了即使有 XSS 漏洞,攻击者也无法加载外部恶意脚本窃取 。三层防护各司其职,缺一不可。
一句话总结: 接口安全不是点状修补,而是体系化防守。从传输层到浏览器层,再到内容层,每多一层防护,你就多一分安心。