前端安全为何成为面试高频考点?
在现代Web应用中,前端代码直接暴露于用户浏览器环境,是攻击者实施客户端攻击的第一线。随着SPA(单页应用)、微前端架构的普及,前端承担了更多逻辑控制、数据处理与路由管理职责,安全风险显著增加。据OWASP《2023年Web应用安全风险Top 10》统计,XSS与CSRF仍居前列,且新增DOM-based XSS占比上升至37%。
企业招聘中,中高级前端工程师岗位普遍要求掌握基础安全防护知识。例如:
- 字节跳动:要求“熟悉常见Web安全漏洞及防御机制,能编写安全编码规范”
- 腾讯:在技术终面中常问“如何防止用户提交的富文本内容被注入恶意脚本?”
- 阿里:考察“在动态插入DOM时,如何避免DOM Clobbering攻击?”
本页面将从攻击原理、典型场景、防御方案、代码示例、面试高频问题五个维度,逐层深入解析前端安全核心知识,确保你不仅“知道”,更能“讲清”、“写出”、“用好”。
网友们还关心
Q:前端安全知识是否只适用于大厂?小公司开发中需要关注吗?
A:小公司更需警惕!因安全投入有限,攻击者常将小企业网站作为跳板。2023年某初创公司因未过滤用户评论中的)做编码处理,通过history.pushState注入脚本,在路由切换时触发。
用户在富文本中上传SVG图片,其内部含
防御三原则
① 输入过滤:对用户输入进行HTML实体编码;
② 输出编码:根据上下文选择编码方式(HTML上下文用<,JS上下文用\u003C);
③ 策略兜底:启用CSP策略限制脚本来源。
安全编码示例
// ❌ 危险:innerHTML直接解析HTML
document.getElementById('comment').innerHTML = userInput;
// ✅ 安全:使用textContent或innerText
document.getElementById('comment').textContent = userInput;
// ✅ 安全:使用DOM API动态创建元素
const div = document.createElement('div');
div.textContent = userInput; // 自动转义
document.getElementById('comment').appendChild(div);
// ✅ 富文本场景:使用DOMPurify库
const clean = DOMPurify.sanitize(dirtyInput, { ALLOWED_TAGS: ['b', 'i', 'u'] });
container.innerHTML = clean;
CSRF攻击:跨站请求伪造
CSRF(Cross-Site Request Forgery)指攻击者诱导用户访问恶意页面,利用用户已登录的Cookie向目标站点发起非预期请求(如转账、修改密码)。关键点在于:请求是“用户主动发起”,但用户不知情。
与XSS区别:XSS是“脚本执行”,CSRF是“请求伪造”;XSS需注入脚本,CSRF利用浏览器自动携带Cookie机制。
场景1:GET型CSRF
攻击页面含,用户访问该页面时,浏览器自动发送GET请求,携带银行Cookie,完成转账。
场景2:POST型CSRF
攻击页面自动提交隐藏表单:
<form action="https://bank.com/transfer" method="POST" id="csrf-form">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.getElementById('csrf-form').submit()</script>
Anti-CSRF Token
服务端生成随机Token,放入表单与Cookie,提交时比对Token。前端需在请求头中携带X-CSRF-Token。
SameSite Cookie属性
设置Cookie为Strict/Lax模式,禁止跨站请求携带Cookie(详见SameSite章节)。
双重Cookie验证
要求请求同时携带Cookie与自定义Header(如X-Request-With: XMLHttpRequest),攻击者无法修改Header。
安全编码示例
// ✅ 服务端生成Token并写入Cookie
res.cookie('csrf_token', generateRandomToken(), {
httpOnly: true,
sameSite: 'Strict'
});
// ✅ 前端请求时携带Token
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': getCookie('csrf_token')
},
body: JSON.stringify({ to: 'friend', amount: 100 })
});
// ✅ Vue/React中自动注入Token
axios.interceptors.request.use(config => {
config.headers['X-CSRF-Token'] = getCookie('csrf_token');
return config;
});
点击劫持:诱导用户点击隐藏元素
攻击者通过CSS将目标网站(如银行转账页)以透明iframe覆盖在恶意页面上,诱导用户点击“免费领取奖品”按钮,实际点击的是隐藏的转账按钮。
防御核心:使用X-Frame-Options或CSP的frame-ancestors指令限制页面被嵌套。
// ✅ 服务端响应头设置
X-Frame-Options: DENY // 禁止嵌套
X-Frame-Options: SAMEORIGIN // 仅同源可嵌套
Content-Security-Policy: frame-ancestors 'self';
HTTP劫持:中间人攻击(MITM)
在非HTTPS连接下,攻击者(如公共Wi-Fi)可篡改HTTP响应,注入广告、窃取数据或植入恶意脚本。例如:用户访问某博客,页面底部被插入“扫码领红包”JS脚本。
用户访问www.example.com,DNS被污染返回广告商IP,页面加载后注入弹窗代码。
Chrome扩展“免费VPN”在HTTP流量中插入跟踪脚本,收集用户浏览历史。
强制HTTPS
服务端配置HSTS(Strict-Transport-Security):响应头设为Strict-Transport-Security: max-age=31536000; includeSubDomains,浏览器强制使用HTTPS访问。
HTTPS证书校验
前端使用Certificate Pinning(如Android/iOS),Web端通过Expect-CT头部(已弃用)或HSTS+证书透明日志监控异常证书。
禁止HTTP资源
页面内所有资源(JS/CSS/图片)必须使用HTTPS链接,避免“混合内容”警告。
curl -I https://example.com验证HSTS头是否生效。
内容安全策略(CSP):最后一道防线
CSP通过HTTP响应头定义“允许加载哪些资源”,从源头阻断XSS/注入攻击。核心指令包括:
default-src:默认资源加载策略script-src:脚本加载限制(禁止eval、inline script)img-src:图片加载来源frame-ancestors:防止点击劫持
若未禁止inline script,攻击者可注入onclick="..."触发脚本;若允许'unsafe-eval',则eval()可能执行恶意代码。
// ✅ 安全的CSP配置(示例)
Content-Security-Policy:
default-src 'self';
script-src 'self' 'unsafe-inline' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.googleapis.com;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
关键说明:
'unsafe-inline':仅在无法移除inline script时临时使用(如旧版jQuery)data::允许Base64图片,但需谨慎(可被用于隐藏恶意脚本)- 避免使用
'unsafe-eval',改用Function构造器或JSON.parse
启用report-uri或report-to收集违规报告,及时发现XSS攻击尝试:
Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpoint;
前端可监听securitypolicyviolation事件:
document.addEventListener('securitypolicyviolation', (e) => {
console.log('CSP Violation:', e);
// 上报至监控系统
navigator.sendBeacon('/api/csp-violation', JSON.stringify({
blockedURI: e.blockedURI,
violatedDirective: e.violatedDirective
}));
});
HTTPS加密:不只是锁图标
HTTPS = HTTP + TLS/SSL,实现三重安全:
加密传输
对称加密(AES)保护数据内容,非对称加密(RSA)交换密钥。
身份验证
CA机构签发证书,浏览器验证证书链(根证书→中间证书→服务器证书)。
完整性校验
MAC(消息认证码)防止数据被篡改。
前端常见误区:
- “HTTPS完全防XSS” → 错!XSS在客户端执行,与传输层无关
- “自签名证书安全” → 错!浏览器会警告,用户可能忽略
chrome://net-internals/#hsts是否添加域名至HSTS列表;使用ssltest工具(如Qualys SSL Labs)检测配置强度。
SameSite Cookie:从源头防御CSRF
SameSite属性控制Cookie是否随跨站请求发送:
Strict
仅同站请求携带Cookie(如从a.com跳转到b.com的请求不携带)。
Lax
允许GET导航请求携带Cookie(如点击链接跳转),但禁止POST/iframe请求。
None
必须配合Secure属性(仅HTTPS发送),允许跨站请求携带Cookie。
服务端配置示例:
// ✅ Node.js (Express)
res.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'Lax' // 推荐默认值
});
// ✅ Java (Spring Security)
.setHeader("Set-Cookie", "JSESSIONID=xxx; HttpOnly; Secure; SameSite=Lax");
// ✅ PHP
setcookie("session", $token, [
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
DOM Clobbering:利用DOM特性覆盖JS变量
浏览器允许通过HTML注释或id属性覆盖全局变量。例如:
<!-- 恶意HTML注入 -->
<a id="config">
<img id="apiUrl" src="http://evil.com?x=">
若前端代码使用var config = window.config || {};,则config被覆盖为HTMLAnchorElement;若使用fetch(window.apiUrl),则apiUrl被劫持为图片标签,导致请求发送至攻击者服务器。
典型场景:富文本编辑器、用户生成内容(UGC)页面、动态路由参数未转义。
防御要点
- 避免使用全局变量存储配置(改用模块化作用域)
- 对DOM操作前进行类型检查:
if (typeof window.config === 'object') - 使用
document.querySelector('#config')而非document.getElementById('config')(后者返回元素而非DOM属性) - 在Vue/React中,避免直接操作DOM,改用框架数据绑定
// ✅ 安全读取配置
const config = JSON.parse(localStorage.getItem('config') || '{}');
// ✅ 防御DOM Clobbering
const apiUrl = document.getElementById('apiUrl');
const safeUrl = apiUrl && apiUrl.tagName === 'IMG'
? apiUrl.src
: '/api/default';
JSONP与CORS:跨域安全边界
JSONP原理:利用)
CORS安全配置:
Access-Control-Allow-Origin:禁止返回*(除非公开API)Access-Control-Allow-Credentials:仅当需要Cookie时设为true- 预检请求(OPTIONS)需验证Origin来源
// ✅ 安全的CORS响应头
Access-Control-Allow-Origin: https://trusted.example.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
前端注意事项:
- 使用
fetch(url, { credentials: 'include' })发送Cookie - 避免在请求中暴露敏感信息(如token放在URL参数)
JSONP易被CSRF利用!因