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)问题。

浏览器安全模型与凭证传递链 浏览器(源 A) 页面 JS / DOM 受同源策略约束 Cookie 存储 Secure / HttpOnly / SameSite CORS 策略引擎 预检 / 响应头校验 攻击面 XSS · CSRF · 会话固定 CORS 误配 · 缓存投毒 HTTP(S) 传输层 请求头:Origin / Cookie / Referer 响应头:Set-Cookie / ACAO / CSP TLS 提供机密性与完整性 HSTS 强制加密 服务器(源 B) 路由与鉴权中间件 校验 Session / Token Session 存储 Redis / DB · 超时销毁 CORS 响应头生成 白名单精确匹配 服务端风险 会话固定 · 越权 · 走私 反射 Origin · 弱随机数 请求 转发 响应 返回 信任边界 源隔离 · 凭证最小暴露 · 显式授权

五、工程落地检查清单

将上述机制转化为可执行的检查项,建议在每次迭代或上线前逐项确认:

  1. 所有会话Cookie是否同时具备Secure、HttpOnly、SameSite=Lax或Strict?
  2. 登录成功后是否重新生成Session ID,登出是否销毁服务端会话?
  3. CORS是否使用精确白名单,而非通配符或后缀匹配?
  4. 是否配置了HSTS、CSP、X-Content-Type-Options等安全响应头?
  5. 是否存在将用户输入拼入响应头或缓存键的代码路径?
  6. JWT是否校验alg、exp、iss,并支持吊销?
验证顺序建议:先用浏览器DevTools的Network面板观察实际请求头与响应头,再用curl复现跨源与畸形请求,最后结合服务端日志确认是否存在解析分叉或凭证泄露。安全配置的正确性,最终要以"攻击者视角能否读取或伪造"为判断标准,而非仅看配置项是否存在。