2. Web安全工程基础
Web安全工程的第一性原理是:浏览器与服务器之间的一切信任关系,都建立在HTTP协议、源(Origin)边界和凭证(Cookie/Session)传递机制之上。理解这三者的交互方式,才能判断一个漏洞是"协议层缺陷"、"浏览器策略绕过"还是"服务端凭证管理失误"。本章从工程视角梳理这条信任链,并给出可落地的配置与验证方法。
一、HTTP协议的安全属性与攻击面
HTTP是明文、无状态、可被中间人读写的请求-响应协议。它的安全属性完全依赖外部机制补齐:传输机密性靠TLS,身份连续性靠Cookie,请求来源可信度靠Origin/Referer头,而请求方法语义(GET安全幂等、POST非幂等)则决定了CSRF与缓存投毒的风险边界。
工程上需要重点关注的HTTP攻击面包括:
- 请求头注入:用户输入被拼进
Location、Set-Cookie等响应头,攻击者用%0d%0a注入换行,伪造响应头或响应体。 - 请求走私:前端代理与后端服务器对
Content-Length和Transfer-Encoding解析不一致,导致请求边界错位,可绕过前端ACL访问后端接口。 - 方法覆盖与语义滥用:部分框架支持
X-HTTP-Method-Override,若未限制,GET请求可被改写为DELETE。 - 缓存投毒:未将
Host、X-Forwarded-Host纳入缓存键,攻击者可污染CDN缓存,向其他用户返回恶意内容。
一个典型的响应头加固配置如下,可直接作为基线:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'none'
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Set-Cookie: SESSIONID=abc123; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=1800
验证方式:用curl -I https://target/检查响应头是否齐全;用curl -H "Host: evil.com" https://target/观察是否被回显到Location或缓存键中;对走私风险,可用Content-Length与Transfer-Encoding: chunked同时存在的畸形请求,观察前后端响应是否分叉。
二、同源策略:浏览器最核心的隔离边界
同源策略(Same-Origin Policy, SOP)规定:只有当协议、主机、端口三者完全一致时,两个资源才属于同一源。它约束的是"读取"而非"发送"——跨源请求可以发出,但响应默认不能被JavaScript读取。这一区分是理解CSRF与CORS的关键。
SOP的例外与放宽点包括:<script>、<img>、<link>等标签可跨源加载;document.domain可放宽子域隔离(已废弃);postMessage可跨源通信但需校验event.origin。工程中最常见的SOP绕过是JSONP和postMessage未校验来源。
三、CORS:受控的跨源读取
CORS不是"关闭同源策略",而是在服务端明确授权下,允许特定源读取响应。它分为简单请求与预检请求两条路径:
- 简单请求:方法为GET/HEAD/POST,且Content-Type限于
application/x-www-form-urlencoded、multipart/form-data、text/plain,无自定义头。浏览器直接发送,服务端用Access-Control-Allow-Origin决定是否放行读取。 - 预检请求:使用PUT/DELETE、自定义头或JSON Content-Type时,浏览器先发
OPTIONS,服务端需返回Access-Control-Allow-Methods、Access-Control-Allow-Headers,并通过Access-Control-Max-Age缓存预检结果。
高危配置是Access-Control-Allow-Origin: *与Access-Control-Allow-Credentials: true同时出现——浏览器会直接拒绝该组合,但若服务端动态反射Origin头且未做白名单校验,则等价于对任意站点开放带凭证读取,攻击者页面即可读取受害者敏感数据。
Origin.endsWith("trusted.com")做校验,会被eviltrusted.com绕过;应使用精确匹配或解析后的主机名白名单。四、Cookie与Session:凭证的存储与传递
Cookie是HTTP状态保持的载体,Session是服务端保存的会话状态。安全设计要点在于:Cookie属性决定凭证暴露面,Session标识决定会话可预测性与生命周期。
| 属性 | 作用 | 缺失风险 |
|---|---|---|
| Secure | 仅HTTPS传输 | 明文链路泄露会话 |
| HttpOnly | 禁止JS读取 | XSS直接窃取Cookie |
| SameSite=Lax/Strict | 限制跨站携带 | CSRF可利用 |
| Path/Domain | 限定作用范围 | 子域或路径越权读取 |
| Max-Age/Expires | 控制生命周期 | 会话长期有效 |
Session标识必须使用密码学安全随机数(如128位以上),登录成功后应重新生成Session ID以防会话固定攻击;服务端需设置空闲超时与绝对超时,并在登出时销毁服务端状态。若采用JWT等无状态令牌,则需额外处理吊销、时钟偏移与算法混淆(alg:none)问题。
五、工程落地检查清单
将上述机制转化为可执行的检查项,建议在每次迭代或上线前逐项确认:
- 所有会话Cookie是否同时具备
Secure、HttpOnly、SameSite=Lax或Strict? - 登录成功后是否重新生成Session ID,登出是否销毁服务端会话?
- CORS是否使用精确白名单,而非通配符或后缀匹配?
- 是否配置了HSTS、CSP、
X-Content-Type-Options等安全响应头? - 是否存在将用户输入拼入响应头或缓存键的代码路径?
- JWT是否校验
alg、exp、iss,并支持吊销?
curl复现跨源与畸形请求,最后结合服务端日志确认是否存在解析分叉或凭证泄露。安全配置的正确性,最终要以"攻击者视角能否读取或伪造"为判断标准,而非仅看配置项是否存在。