Skip to content

第 24 章 · 网络与安全 · 高频面试题

这一章是前后端边界、安全岗硬考点。几乎每场面试都会问 1~3 题


1. HTTP/1.1、HTTP/2、HTTP/3 的核心区别?

维度HTTP/1.1HTTP/2HTTP/3
传输层TCPTCPUDP(QUIC)
多路复用❌(队头阻塞)✅ + 无 TCP 队头阻塞
头部压缩HPACKQPACK
服务器推送有(已被 Chrome 弃)减弱
加密可选事实强制 HTTPS强制
0-RTT 重连支持

关键演进逻辑

  1. HTTP/1.1 痛点:队头阻塞 → 浏览器只能并发 6 个连接,资源多就慢。
  2. HTTP/2 解决:一个 TCP 多路复用 → 但TCP 层丢包仍然会阻塞所有 stream。
  3. 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. 只精确到,1 秒内多次修改检测不到。
  2. 文件重新生成但内容不变(如发布脚本),mtime 变了但其实没变。
  3. 分布式系统多机器 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-TokenAuthorization

复杂请求:先发 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 件套

  1. 输出编码:用 textContent 替代 innerHTML;模板引擎默认转义
  2. 输入校验:只允许预期字符集(不要黑名单,要白名单)
  3. CSP:HTTP 响应头 Content-Security-Policy: script-src 'self'
  4. HttpOnly Cookie:JS 读不到,偷不走 token
  5. 避免危险 APIevalnew FunctioninnerHTMLdocument.writev-htmldangerouslySetInnerHTML 都要谨慎

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 解码,别放敏感信息

无状态的坑

  1. 注销难:服务端不存状态,签发出去的 token 在过期前都有效。
    • 解法:维护黑名单 / 缩短有效期 + refresh token / 用 jti(JWT ID)+ 版本号
  2. 不能改权限:用户角色变了,新权限要等 token 过期才生效。
  3. 体积大:payload + header + 签名,比 sessionId 大很多。
  4. 存哪里
    • 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 给前端

为什么不直接给 tokenclient_secret 不能暴露在前端,必须后端中转。

state 参数:防 CSRF——确保回调的请求是自己发起的。

SPA 推荐 PKCE 模式:在浏览器里用 code_verifier 替代 client_secret。


11. 301、302、304 分别是什么?

含义浏览器行为
301永久重定向缓存重定向,下次直接跳新地址
302临时重定向不缓存,每次都先请求原地址
303See OtherPOST 后跳 GET 用
307临时重定向(保留方法)比 302 严格,要求重定向后保持原 HTTP 方法
308永久重定向(保留方法)类似 301 但保留方法
304Not Modified协商缓存命中,无 body

实战

  • 域名换了 → 301
  • 维护期临时跳维护页 → 302
  • 静态资源协商缓存 → 304

12. 502 vs 504 vs 503?

含义场景
502Bad Gateway网关收到上游"无效响应"(上游崩了/连不上)
503Service Unavailable服务暂时不可用(限流、维护中)
504Gateway 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)——能把这条线讲完整,安全岗都能拿。