主题
第 24 章 · 网络与安全 · 高频面试题
这一章是前后端边界、安全岗硬考点。几乎每场面试都会问 1~3 题。
1. HTTP/1.1、HTTP/2、HTTP/3 的核心区别?
答:
| 维度 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP | UDP(QUIC) |
| 多路复用 | ❌(队头阻塞) | ✅ | ✅ + 无 TCP 队头阻塞 |
| 头部压缩 | 无 | HPACK | QPACK |
| 服务器推送 | 无 | 有(已被 Chrome 弃) | 减弱 |
| 加密 | 可选 | 事实强制 HTTPS | 强制 |
| 0-RTT 重连 | 无 | 无 | 支持 |
关键演进逻辑:
- HTTP/1.1 痛点:队头阻塞 → 浏览器只能并发 6 个连接,资源多就慢。
- HTTP/2 解决:一个 TCP 多路复用 → 但TCP 层丢包仍然会阻塞所有 stream。
- HTTP/3 解决:底层换成 UDP(QUIC),每个 stream 独立 ACK,单 stream 丢包不影响其他。
2. 详细说说 HTTPS 的握手过程。
答:
1. ClientHello:客户端发支持的 TLS 版本、密码套件、随机数 R1
2. ServerHello:服务端选定版本/套件,发随机数 R2 + 证书 + 公钥
3. 客户端:验证证书(CA 链 / 域名 / 有效期),生成预主密钥 R3,
用证书的公钥加密 R3 发回去
4. 服务端用私钥解出 R3
5. 双方根据 R1 + R2 + R3 计算出对称密钥
6. 后续通信用对称加密为什么用对称 + 非对称两套:
- 非对称(如 RSA/ECDHE)慢但能"安全交换密钥"
- 对称(如 AES)快但需要"提前共享密钥"
- 组合:用非对称协商出对称密钥,后续用对称加密——又快又安全
TLS 1.3 改进:握手从 2-RTT 变 1-RTT;支持 0-RTT 重连;废弃了不安全的密码套件。
3. 强缓存 vs 协商缓存?请画完整决策图。
答:
请求资源
│
▼
强缓存(Cache-Control: max-age=... 或 Expires)
│
├── 命中且未过期 → 200 (from cache),不发请求
│
└── 未命中/已过期 ↓
│
▼
协商缓存(带 If-None-Match / If-Modified-Since)
│
├── 资源未变 → 304 Not Modified(无 body)
│
└── 资源已变 → 200 + 新内容 + 新 ETag| Header | 类型 | 说明 |
|---|---|---|
Cache-Control | 强缓存 | 优先级最高,相对时间 |
Expires | 强缓存 | 绝对时间,已过时(被 CC 取代) |
ETag | 协商缓存 | 内容指纹(哈希) |
Last-Modified | 协商缓存 | 最后修改时间 |
实战策略:
- HTML:
Cache-Control: no-cache(必须协商) - 带 hash 的静态资源:
Cache-Control: max-age=31536000, immutable - 动态 API:
Cache-Control: no-store
4. ETag 和 Last-Modified 哪个更可靠?
答:ETag 更可靠。
Last-Modified 的局限:
- 只精确到秒,1 秒内多次修改检测不到。
- 文件重新生成但内容不变(如发布脚本),mtime 变了但其实没变。
- 分布式系统多机器 mtime 不一致。
ETag 一般是内容哈希,只要内容相同就一致,跨机器、跨时间都没问题。
优先级:服务端优先使用 ETag。两者都存在时,浏览器都会发,服务端可以选其一。
5. 跨域是什么?同源策略限制了什么?
答:
同源:协议 + 域名 + 端口三者完全一致。任何一项不同 = 跨域。
同源策略限制(限制的是 JS 读取,不是请求发送):
- ❌ 不能读取跨域响应的 body / Header(除非 CORS 放行)
- ❌ 不能读取跨域的 Cookie / localStorage
- ❌ 不能操作跨域的 iframe contentWindow(部分允许 location)
不限制:
- ✅
<script><img><link><video>等可跨域加载 - ✅ 跨域表单提交(但拿不到响应)
- ✅ WebSocket 不受同源策略限制(自有 Origin 校验)
6. CORS 简单请求 vs 复杂请求?预检流程?
答:
简单请求(满足全部条件,不预检):
- 方法是
GET/HEAD/POST - Content-Type 是
text/plain/application/x-www-form-urlencoded/multipart/form-data - 不带自定义 Header(如
X-Token、Authorization)
复杂请求:先发 OPTIONS 预检:
浏览器 → OPTIONS /api/x
Origin: https://a.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Token
服务器 ← 204
Access-Control-Allow-Origin: https://a.com
Access-Control-Allow-Methods: PUT, POST
Access-Control-Allow-Headers: X-Token
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 600 ← 预检结果缓存坑:
Access-Control-Allow-Origin: *配Allow-Credentials: true→ 浏览器拒绝- OPTIONS 不带 Cookie,鉴权中间件如果挡了就失败
- 预检每次都发 → 性能差。设
Access-Control-Max-Age缓存
7. XSS 三种类型?怎么防御?
答:
| 类型 | 攻击代码位置 | 例子 |
|---|---|---|
| 存储型 | 服务端数据库 | 评论里写 <script> 被存数据库 |
| 反射型 | URL 参数(一次性) | ?q=<script>... 服务端拼到页面 |
| DOM 型 | 完全在前端 | location.hash 直接写入 innerHTML |
防御 5 件套:
- 输出编码:用
textContent替代innerHTML;模板引擎默认转义 - 输入校验:只允许预期字符集(不要黑名单,要白名单)
- CSP:HTTP 响应头
Content-Security-Policy: script-src 'self' - HttpOnly Cookie:JS 读不到,偷不走 token
- 避免危险 API:
eval、new Function、innerHTML、document.write、v-html、dangerouslySetInnerHTML都要谨慎
8. CSRF 原理?三种防御方案?
答:
原理:用户在 A 站登录后有 Cookie,被诱导访问 B 站,B 站偷偷向 A 站发请求,浏览器自动带 Cookie,A 站以为是用户操作。
防御:
1. SameSite Cookie(最有效,现代默认)
Set-Cookie: token=xxx; SameSite=Lax; Secure; HttpOnly| 值 | 行为 |
|---|---|
Strict | 完全不带跨站 |
Lax | 顶级导航带,POST/iframe/img 不带(默认) |
None | 跨站也带(必须 Secure) |
2. CSRF Token
服务端在表单/响应里塞随机 token,提交时带上,服务端校验。攻击者无法预测。
3. 双重 Cookie
请求时同时把 csrf token 放在 Cookie 和 Header/Body 里,服务端校验两者相等。第三方域名拿不到第一个值,所以伪造不出来。
4. 校验 Referer / Origin(兜底)
9. JWT 的结构?无状态有什么坑?
答:
结构:Header.Payload.Signature
Header = base64({ "alg": "HS256", "typ": "JWT" })
Payload = base64({ "sub": "123", "exp": 1700000000 })
Signature = HMAC(密钥, Header + "." + Payload)签名只是为了防篡改,不是加密!payload 任何人都能 base64 解码,别放敏感信息。
无状态的坑:
- 注销难:服务端不存状态,签发出去的 token 在过期前都有效。
- 解法:维护黑名单 / 缩短有效期 + refresh token / 用 jti(JWT ID)+ 版本号
- 不能改权限:用户角色变了,新权限要等 token 过期才生效。
- 体积大:payload + header + 签名,比 sessionId 大很多。
- 存哪里:
- localStorage:易被 XSS 偷
- Cookie + HttpOnly + Secure + SameSite:相对最安全(但有 CSRF 风险,要配 token)
- 不建议存 sessionStorage(同 XSS 风险)
10. 谈谈 OAuth 2.0 的授权码模式(GitHub 登录流程)。
答:
1. 用户点"GitHub 登录",前端跳转:
https://github.com/login/oauth/authorize
?client_id=xxx
&redirect_uri=https://app.com/callback
&scope=user:email
&state=随机串 ← 防 CSRF
2. GitHub 弹出"是否授权" → 用户同意
3. GitHub 跳转回:
https://app.com/callback?code=ABC&state=随机串
4. 前端把 code 给后端
5. 后端拿 code + client_secret POST GitHub:
/login/oauth/access_token
6. GitHub 返回 access_token
7. 后端用 access_token 调 /user 接口拿用户信息
8. 后端创建本地 session/jwt 给前端为什么不直接给 token:client_secret 不能暴露在前端,必须后端中转。
state 参数:防 CSRF——确保回调的请求是自己发起的。
SPA 推荐 PKCE 模式:在浏览器里用 code_verifier 替代 client_secret。
11. 301、302、304 分别是什么?
答:
| 码 | 含义 | 浏览器行为 |
|---|---|---|
| 301 | 永久重定向 | 缓存重定向,下次直接跳新地址 |
| 302 | 临时重定向 | 不缓存,每次都先请求原地址 |
| 303 | See Other | POST 后跳 GET 用 |
| 307 | 临时重定向(保留方法) | 比 302 严格,要求重定向后保持原 HTTP 方法 |
| 308 | 永久重定向(保留方法) | 类似 301 但保留方法 |
| 304 | Not Modified | 协商缓存命中,无 body |
实战:
- 域名换了 → 301
- 维护期临时跳维护页 → 302
- 静态资源协商缓存 → 304
12. 502 vs 504 vs 503?
答:
| 码 | 含义 | 场景 |
|---|---|---|
| 502 | Bad Gateway | 网关收到上游"无效响应"(上游崩了/连不上) |
| 503 | Service Unavailable | 服务暂时不可用(限流、维护中) |
| 504 | Gateway Timeout | 网关等了上游超时 |
500 是"服务端代码出错"(异常未捕获),通常服务端代码 bug。
面试加分:能说出"502 一般是 Nginx 上游 PHP-FPM/Node 进程挂了;504 一般是上游处理太慢"。
13. CSP 是什么?怎么用?为什么 unsafe-inline 不安全?
答:
CSP(Content Security Policy):通过 HTTP 响应头限制浏览器只能加载白名单的资源。即使你的页面被注入了 <script>,浏览器也拒绝执行。
Content-Security-Policy: default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
object-src 'none';
report-uri /csp-report;为什么 unsafe-inline 不安全:
XSS 注入的脚本通常就是内联的(如 <img src=x onerror="...">、<script>...</script>)。允许 inline = 等于允许任意脚本执行 = CSP 形同虚设。
正确做法:把内联代码改写成外部 JS 文件 + 'self';或用 nonce-xxx/sha256-xxx 白名单。
14. (进阶)什么是 SSL Strip 攻击?怎么防?
答:
攻击:用户访问 http://bank.com(自动跳 https),中间人在 HTTP 阶段拦截,把"跳 HTTPS"剥离,自己和服务器走 HTTPS,和用户走 HTTP,用户全程感觉不出来。
防御:HSTS(HTTP Strict Transport Security)
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload浏览器记住"这个域名只能 HTTPS",下次连 HTTP 都不发,直接本地升级 HTTPS。
preload 还能加入浏览器内置的 HSTS 列表,第一次访问就用 HTTPS。
15. (进阶)为什么 CDN/反向代理后,后端要识别真实客户端 IP?
答:
请求经过 CDN/Nginx,后端看到的 remoteAddr 是 CDN/Nginx 的 IP,不是真实用户 IP。
CDN/Nginx 会在请求 Header 加:
X-Forwarded-For: 用户IP, CDN1IP, CDN2IP
X-Real-IP: 用户IP后端取 X-Forwarded-For 第一个作为真实 IP。
坑:这个 Header 可被伪造,如果不经过反代直接访问后端,攻击者可以随便填。生产做法:在 Nginx 强制覆盖:
nginx
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;一句话总结
网络与安全面试套路:协议演进(1.1→2→3)→ HTTPS 握手 → 缓存(强+协商)→ 跨域(CORS 流程)→ 攻防(XSS/CSRF/CSP/SameSite)→ 鉴权(Cookie/JWT/OAuth)——能把这条线讲完整,安全岗都能拿。