第 4 篇:HTTP 和浏览器------HTTPS 不只是「把 HTTP 包一层 TLS」那么简单
标签 :
#HTTPS#浏览器#Web安全#HTTP
你的 HTTPS 网站,可能还是「不安全」的
HTTPS 给你做了三件事:
- 加密------传输内容不被偷看
- 完整性------传输内容不被篡改
- 认证------你连的就是你声称的那个网站
听起来很完美。但协议上的安全只是基础------真正决定你网站安不安全的,是 HTTP 协议层、浏览器行为、Cookie 配置、跨域策略......所有这些「握手之后」的事。
今天我们来聊这些「协议之外的安全」。
混合内容(Mixed Content):HTTPS 网站里的「裸奔」
「我的网站已经是 HTTPS 了,为什么浏览器还是报不安全?」
经典原因:页面是 HTTPS,但里面加载的资源不是。
<!-- 这个页面是 HTTPS -->
<html>
<img src="http://example.com/logo.png"> <!-- 但图片是 HTTP -->
<script src="http://example.com/app.js"> <!-- 脚本是 HTTP -->
</html>
这叫混合内容 。后果很直接:HTTP 资源可以被中间人任意篡改 ------攻击者把 <script> 换成自己的 JS,等于拿到你整页的控制权。
浏览器怎么应对
| 时代 | 浏览器处理 |
|---|---|
| 2010 前 | 只显示警告,不阻止 |
| 2015 前后 | 主动阻止活跃混合内容(脚本、iframe、对象) |
| 2020 起 | 升级为只阻止被动混合内容(图片、音频)的最后一批 |
| 2026(今天) | 完全禁止任何混合内容------Chrome / Firefox 直接静默拦截,地址栏仍显示锁标 |
冷知识 :Chrome 在 2019-2020 年的「混合内容大扫除」中,逐步把图片、音频、视频都纳入了强制 HTTPS 范围。今天你的网站只要有任何 HTTP 资源加载,用户根本看不到它们。
如何彻底避免
- 所有资源用协议相对 URL :
//cdn.example.com/app.js(自动跟随页面协议) - 或者强制 HTTPS:
https://cdn.example.com/app.js - 加 CSP(Content-Security-Policy) 头:
upgrade-insecure-requests,告诉浏览器自动把所有 HTTP 资源升级为 HTTPS
Cookie:被低估的安全边界
Cookie 是浏览器和服务端之间的「凭据」。一旦被偷,攻击者就假装你是你。
Cookie 的三个标记决定了它的安全性:
Secure
只有通过 HTTPS 发送。没有这个标记,Cookie 会在 HTTP 请求中被明文发送------一次未加密的请求就可能泄露。
http
Set-Cookie: session=abc123; Secure
HttpOnly
JavaScript 无法读取这个 Cookie。防 XSS 偷 Cookie。
http
Set-Cookie: session=abc123; HttpOnly
SameSite
控制跨站请求时是否发送 Cookie。值有:
| 值 | 行为 |
|---|---|
Strict |
完全不跨站发送(最严) |
Lax |
仅在「顶级导航的 GET 请求」时发送(默认) |
None |
任何时候都发送(必须配 Secure) |
关键洞察 :Chrome 80 起默认所有 Cookie 为
SameSite=Lax------这是 2020 年最重大的浏览器安全变化之一。从那天起,「第三方 Cookie 跟踪」的玩法直接被打掉了一半。
推荐的 Cookie 配置
http
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=3600
没有银弹------具体怎么设取决于业务:
- 银行系统 →
SameSite=Strict - 普通电商 →
SameSite=Lax - 嵌入第三方 iframe 的场景 →
None; Secure(必须 HTTPS)
HSTS:把「不安全升级降级」这条路堵死
攻击路径(SSL Stripping)
用户访问 http://bank.com → 浏览器自动 重定向到 https://bank.com → 中间人拦截重定向 ,保持 HTTP 连接,让用户在不知情下用 HTTP 通信。
问题根源:第一次访问时(还没建立 HTTPS 连接),用户和服务器之间是明文。攻击者可以卡在中间,假装是 HTTPS。
HSTS 怎么解决
HSTS(HTTP Strict Transport Security)让服务器告诉浏览器 :「未来一段时间内,我这个域名永远用 HTTPS」。
http
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
| 参数 | 作用 |
|---|---|
max-age=31536000 |
1 年内强制 HTTPS |
includeSubDomains |
所有子域名都强制 |
preload |
申请加入浏览器预置 HSTS 列表 |
HSTS 预置列表
预置列表 才是 HSTS 真正「封死 SSL Stripping」的关键:Chrome 内置了一个列表,里面的域名第一次访问就是 HTTPS------根本不存在明文 HTTP 这一步。
要进这个列表:去 hstspreload.org 提交申请。Google、Apple、Mozilla、Microsoft 四家浏览器共享这个列表。
冷知识 :HSTS 最大的副作用是缓存了策略 ------一旦发出去就要按
max-age等待过期。如果中间出错想关掉 HSTS,要么max-age=0慢慢等用户过期,要么联系浏览器厂商从预置列表删除。
CSP、CORS、Referrer-Policy:「HTTP 头里的安全」
现代 HTTP 协议有一整套安全响应头,每个都在对抗特定的攻击。今天我们挑最重要的讲。
CSP(Content-Security-Policy)------ 限制页面能加载什么
CSP 让页面声明「我可以加载哪些资源、可以执行哪些脚本」,主动防御 XSS。
http
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src *;
report-uri /csp-report
含义:
- 默认资源只允许同源
- 脚本只能来自同源 + 指定 CDN
- 样式允许内联(兼容老代码)
- 图片不限制
- 违规时上报到
/csp-report
冷知识 :CSP 在防 XSS 上极其强大,但调试极痛苦 。推荐先用
Content-Security-Policy-Report-Only模式跑一段时间(只报告不阻止),再切到强制。
CORS(Cross-Origin Resource Sharing)------ 控制谁能跨域读我
默认情况下,跨域请求只能读「简单响应」------HTTP 头里那几个安全字段。
如果你的前端要跨域调用 API,服务器必须显式返回:
http
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, OPTIONS
注意 :Allow-Origin: * 和 Allow-Credentials: true 不能同时出现------这是浏览器强制规则,防 CSRF。
Referrer-Policy ------ 控制 Referrer 头泄露
Referrer 头告诉目标网站「你从哪里来」。泄露敏感信息:
Referer: https://internal-admin.company.com/user/12345
设个策略让它不泄露:
http
Referrer-Policy: strict-origin-when-cross-origin
| 值 | 行为 |
|---|---|
no-referrer |
完全不发送 |
same-origin |
同源请求才发送 |
strict-origin |
只发送域名(不带路径),HTTPS → HTTP 时不发送 |
strict-origin-when-cross-origin(默认) |
同源发完整,跨源只发域名 |
X-Frame-Options / CSP frame-ancestors ------ 防点击劫持
不让你的页面被嵌入到别人的 iframe:
http
X-Frame-Options: DENY
# 或者用 CSP
Content-Security-Policy: frame-ancestors 'none'
DENY(禁止任何嵌入)、SAMEORIGIN(只允许同源嵌入)、ALLOW-FROM uri(已废弃,不推荐)。
HTTP/2 和 HTTP/3 的安全含义
HTTP/2(2015,RFC 7540)
HTTP/2 强制 TLS(最初计划是可选,实际所有浏览器实现都强制)。两个常见的安全坑:
- HPACK 压缩 + 加密流 → BREACH 攻击面(详见下)
- 多路复用让单个 TCP 连接的失败会影响所有请求(但与 TLS 安全关系不大)
HTTP/3(2018-2022 标准化,RFC 9114)
HTTP/3 跑在 QUIC 上(基于 UDP),整个加密层用 TLS 1.3。这带来两个变化:
- 0-RTT 握手 ------重连时第一个数据包就带应用数据,但有重放风险(攻击者捕获后重发)
- 加密的握手元数据------连接 ID、流量特性都加密,对流量分析更友好
冷知识 :HTTP/3 的 0-RTT 数据绝对不能用于非幂等操作(如下单、转账)。苹果、Google 的 HTTP/3 客户端实现都对 0-RTT 的使用做了限制。
TLS + HTTP 压缩的化学反应:CRIME / BREACH
TLS 加密 HTTP 数据 ,但不加密压缩。攻击者利用这个特性:
攻击者能控制请求的部分内容(比如用户名搜索框)
↓
压缩算法会「缩短」重复的内容
↓
攻击者通过观察压缩后的密文长度,推断出请求中包含了什么
这就是 CRIME(2012) 和 BREACH(2013) 攻击。
今天的应对
- TLS 1.3 默认完全禁用压缩
- 现代浏览器默认关闭 HTTP 压缩(不是完全解决,但缓解了)
- 应用层手动禁用敏感接口的压缩
- 重要参数加随机填充 ------
?search=foo&csrf=abc&dummy=<random>
给开发者的三条建议
① 一个 Strict-Transport-Security 头是最低门槛
nginx
# Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
配合 HSTS 预置列表,让用户第一次访问就是 HTTPS。
② 把 cookie 收紧到「最小权限」
http
Set-Cookie: session=xxx; Secure; HttpOnly; SameSite=Lax
任何没带 Secure 的 cookie,今天就应该改。
③ 用 CSP 把 XSS 影响降到最低
不要追求一步到位的 Content-Security-Policy: default-src 'none'。从 Content-Security-Policy-Report-Only 起步:
http
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
先观察违规报告,再逐步收紧。一旦发现违规立即阻止,XSS 的杀伤半径会大幅缩小。
延伸阅读
- MDN --- Web 安全 --- 浏览器安全的权威参考
- OWASP Secure Headers Project --- 推荐的安全响应头配置
- HSTS Preload List --- 申请你的域名进预置列表
- CSP Evaluator --- Google 的 CSP 评估工具
- Mixed Content --- W3C 规范