前端秋招复习:XSS、CSRF 与常见 Web 安全问题
前言
前端安全面试中,最常见的两个问题是:
什么是 XSS?
什么是 CSRF?
很多同学背得出全称,却说不清攻击过程,也容易把 HttpOnly、SameSite、CORS、CSP 等防护手段混在一起。
这篇文章面向前端秋招复盘,不深入攻击绕过和服务端安全框架,重点是理解:
攻击为什么能够发生;
前端代码哪里容易出问题;
常见防护手段分别解决什么问题;
面试时怎样用几句话回答清楚。
先记住核心区别:
XSS 是让不可信内容在页面中作为代码执行;CSRF 是借用用户已经登录的身份发送非本人意愿的请求。
一、先看 XSS 和 CSRF 的区别
| 对比项 | XSS | CSRF |
|---|---|---|
| 中文 | 跨站脚本攻击 | 跨站请求伪造 |
| 核心问题 | 不可信内容被当成代码执行 | 用户身份被借用发送请求 |
| 是否依赖用户登录 | 不一定 | 通常依赖已有登录态 |
| 攻击者是否需要知道 Cookie | 不一定 | 通常不需要知道,只利用浏览器自动携带 |
| 主要防护 | 转义、净化、安全 DOM API、CSP | CSRF Token、SameSite、来源校验 |
可以简单记成:
XSS → 注入并执行代码
CSRF → 伪造用户请求
二、XSS 是什么
XSS 全称是:
Cross-Site Scripting
跨站脚本攻击
它的核心是:
网站把不可信数据放进了危险位置,浏览器将其当成 HTML 或 JavaScript 执行。
假设评论区原本只想显示用户输入的文本:
这篇文章写得很好
如果页面直接把评论作为 HTML 插入,攻击者可能提交带有事件代码的内容:
<img src="invalid" onerror="alert('XSS')">
浏览器解析这段 HTML 后,就可能执行其中的代码。
攻击流程可以简化为:
攻击者提供恶意内容
↓
网站没有正确转义或净化
↓
内容进入 innerHTML 等危险位置
↓
浏览器将内容当成代码执行
XSS 可能造成什么影响
恶意脚本运行在目标网站页面中,因此可能:
- 读取页面中的敏感信息;
- 修改页面内容;
- 冒充用户调用接口;
- 读取 JavaScript 可以访问的本地数据;
- 诱导用户输入账号或密码。
三、XSS 有哪些常见类型
秋招面试通常掌握三种即可。
1. 存储型 XSS
恶意内容被保存到数据库,其他用户访问页面时又被读取并执行。
攻击者提交恶意评论
↓
服务器保存到数据库
↓
其他用户查看评论
↓
浏览器执行恶意内容
常见场景:
评论、论坛帖子、用户昵称、个人简介、后台工单
它的特点是影响可能比较持久,并且可能波及多个用户。
2. 反射型 XSS
恶意内容没有长期保存,而是通过本次请求进入服务器响应。
例如服务器把搜索参数直接拼进 HTML:
用户点击特殊链接
↓
参数被发送给服务器
↓
服务器把参数直接返回到页面
↓
浏览器执行危险内容
它通常依赖攻击者诱导用户点击特制链接。
3. DOM 型 XSS
DOM 型 XSS 主要发生在浏览器端。
例如前端读取 URL 参数后直接写入 innerHTML:
const keyword = new URLSearchParams(location.search).get('keyword')
document.querySelector('.result').innerHTML = keyword
更安全的文本展示方式是:
document.querySelector('.result').textContent = keyword
三种类型对比:
| 类型 | 恶意内容主要来自哪里 | 是否通常保存到数据库 | 常见场景 |
|---|---|---|---|
| 存储型 | 数据库等持久化数据 | 是 | 评论、帖子、用户资料 |
| 反射型 | 当前请求参数 | 否 | 搜索页、错误页、特殊链接 |
| DOM 型 | URL、DOM 等前端数据 | 不一定 | innerHTML、document.write |
三者虽然来源不同,但最终问题相同:
不可信数据进入了危险的执行位置。
四、Vue 项目中为什么仍然可能有 XSS
Vue 的普通插值会把数据作为文本处理:
<p>{{ comment }}</p>
假设 comment 是:
<strong>你好</strong>
普通插值通常会显示标签文本,而不是把它作为真正的 HTML 标签渲染。
但是下面的写法需要特别小心:
<div v-html="comment"></div>
v-html 会把字符串作为 HTML 插入页面。如果内容来自用户输入,就可能产生风险。
同样需要谨慎使用:
element.innerHTML = userInput
element.outerHTML = userInput
document.write(userInput)
所以不能认为“使用 Vue 就绝对不会出现 XSS”。框架提供了默认保护,但开发者仍可能通过 v-html 或手动 DOM 操作绕开保护。
五、怎样防御 XSS
1. 把不可信数据作为文本
原生 DOM 操作优先使用:
element.textContent = userInput
而不是直接使用:
element.innerHTML = userInput
Vue 中普通文本优先使用:
<p>{{ userInput }}</p>
核心思想是:
用户输入应该被当成数据,而不是代码。
2. 根据输出位置进行转义
例如将 HTML 中具有特殊含义的字符转换成实体:
< → <
> → >
这样浏览器会显示标签文本,而不会把它解析成真正的标签。
HTML、属性、URL、JavaScript 的解析规则不同,因此应该根据数据最终进入的位置进行安全处理,不能只靠一条简单正则替换所有危险内容。
3. 富文本先净化再渲染
文章编辑器等业务需要保留 <p>、<strong> 等标签,不能把所有 HTML 都转义。
这时应使用经过维护的 HTML Sanitizer,按照允许列表移除危险标签、属性和协议:
const safeHTML = sanitize(untrustedHTML)
element.innerHTML = safeHTML
不要尝试自己用一条正则完整过滤 HTML。
4. 给敏感 Cookie 设置 HttpOnly
服务端可以设置:
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax
HttpOnly 会阻止 JavaScript 通过 document.cookie 读取该 Cookie,可以降低会话 Cookie 被直接窃取的风险。
但它不能阻止 XSS 代码执行,恶意脚本仍可能修改页面或者以当前用户身份调用接口。
5. 使用 CSP 作为额外防线
CSP 全称是内容安全策略,服务器可以通过响应头限制页面允许加载和执行的资源来源:
Content-Security-Policy: default-src 'self'; script-src 'self'
CSP 可以降低部分恶意脚本执行和外部资源加载风险,但它属于纵深防御,不能代替转义和 HTML 净化。
6. 避免执行不可信字符串
谨慎使用:
eval(userInput)
new Function(userInput)
setTimeout(userInput, 1000)
如果字符串来自不可信来源,就可能被当成 JavaScript 执行。
六、CSRF 是什么
CSRF 全称是:
Cross-Site Request Forgery
跨站请求伪造
它的核心是:
攻击者诱导已经登录目标网站的用户,从其他网站向目标网站发送一个非本人意愿的请求。
假设用户已经登录银行网站,浏览器保存了登录 Cookie:
Cookie: sessionId=abc123
用户随后打开了攻击网站,攻击网站构造了一个指向银行接口的表单:
<form action="https://bank.example/api/change-email" method="post">
<input type="hidden" name="email" value="other@example.com">
</form>
在满足 Cookie 发送规则的情况下,浏览器可能自动携带银行网站的登录 Cookie。服务器如果只检查 Cookie,就可能误以为这是用户主动操作。
完整流程是:
用户登录目标网站
↓
浏览器保存登录 Cookie
↓
用户访问攻击网站
↓
攻击网站诱导浏览器向目标网站发送请求
↓
浏览器可能自动带上目标网站的 Cookie
↓
服务器误以为这是用户主动操作
攻击者通常不需要知道 Cookie 的具体值,它利用的是浏览器自动携带 Cookie 的机制。
CSRF 通常需要哪些条件
可以简单记住三个条件:
用户已经登录目标网站;
浏览器会自动携带身份凭证;
目标接口没有验证请求是否来自用户真实意愿。
七、同源策略为什么没有自动阻止 CSRF
同源策略主要限制攻击页面读取其他源的数据,但不代表所有跨站请求都无法发送。
浏览器允许一些跨站行为,例如:
<img src="https://other.example/image.png">
<form action="https://other.example/api/action" method="post">
所以可以简单理解为:
发送某些跨站请求:可能允许
读取跨站响应内容:通常受到同源策略或 CORS 限制
CSRF 通常只需要让修改操作成功,并不一定需要读取响应。例如修改邮箱、提交订单或者关注用户。
因此:
攻击网站看不到响应,不代表伪造请求没有产生影响。
八、怎样防御 CSRF
1. 使用 CSRF Token
服务器为正常用户会话生成一个攻击者难以获得的随机 Token:
正常页面获得 CSRF Token
↓
提交敏感请求时携带 Token
↓
服务器同时校验登录态和 Token
例如:
POST /api/change-email HTTP/1.1
X-CSRF-Token: random-value
攻击网站可能诱导浏览器自动携带 Cookie,但通常无法读取正常网站页面中的 Token,也不能通过普通跨站表单设置这个自定义请求头。
2. 设置 SameSite Cookie
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax
三个常见取值可以简单理解为:
| 值 | 基础理解 |
|---|---|
Strict | 跨站场景限制最严格,但可能影响正常跳转体验 |
Lax | 允许部分顶层安全导航,限制许多跨站请求携带 Cookie |
None | 允许跨站携带,通常必须同时设置 Secure |
SameSite 是重要防线,但复杂业务中通常还需要结合其他措施。
3. 校验请求来源
服务端可以检查 Origin 等请求头,确认修改状态的请求来自允许的站点:
Origin: https://app.example.com
4. 敏感操作再次验证
转账、修改密码等高风险操作可以要求:
重新输入密码;
短信或邮箱验证码;
多因素认证;
二次确认。
5. 不要使用 GET 修改数据
下面的接口设计不合理:
GET /api/delete-user?id=1001
GET 应主要用于获取资源,不应该执行删除、转账等修改状态的操作。
但要注意:
只把 GET 改成 POST 也不能彻底防止 CSRF,因为攻击者可以通过 HTML 表单构造部分 POST 请求。
九、Cookie 三个安全属性分别有什么用
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax
| 属性 | 作用 | 主要关联风险 |
|---|---|---|
HttpOnly | 禁止 JavaScript 读取 Cookie | 降低 XSS 窃取 Cookie 的风险 |
Secure | 只通过 HTTPS 发送 Cookie | 降低不安全传输风险 |
SameSite | 限制部分跨站请求携带 Cookie | 降低 CSRF 风险 |
注意它们都不是万能开关:
HttpOnly 不能阻止 XSS 代码执行;
Secure 不能单独防止 XSS 或 CSRF;
SameSite 也不应该成为唯一的 CSRF 防线。
十、使用 Authorization Token 就没有 CSRF 了吗
假设前端手动把 Token 放进请求头:
fetch('/api/user', {
headers: {
Authorization: `Bearer ${token}`
}
})
攻击网站通常不能让浏览器自动为目标网站添加这个 Authorization 请求头,所以它一般不容易受到传统 Cookie 型 CSRF。
但这不等于 Token 一定安全:
Token 放在 localStorage 中 → XSS 脚本可能读取
Token 放在自动发送的 Cookie 中 → 仍要考虑 CSRF
因此不能简单背成“Cookie 不安全,Token 安全”。不同存放和发送方式面对的风险不同。
十一、几个容易混淆的问题
1. CORS 能防止 CSRF 吗
不能直接画等号。
CORS 主要决定某个源的 JavaScript 是否可以跨源读取响应,以及能否发送某些跨源请求;CSRF 则可能利用表单等浏览器已有能力发送请求,而且不需要读取响应。
所以防御 CSRF 仍应依靠:
CSRF Token、SameSite、Origin 校验等措施。
2. HTTPS 能防止 XSS 和 CSRF 吗
不能。
HTTPS 主要保护浏览器和服务器之间的传输,降低数据被窃听和篡改的风险。
XSS 和 CSRF 属于应用逻辑问题,即使网站使用 HTTPS,也仍然可能存在。不过 HTTPS 是其他安全措施可靠工作的基础。
3. 什么是点击劫持
攻击者把目标网站放进透明或伪装的 iframe,诱导用户点击看似正常的按钮,实际点击了目标网站中的敏感操作。
常见防护响应头:
Content-Security-Policy: frame-ancestors 'self'
也可以使用兼容方案:
X-Frame-Options: SAMEORIGIN
4. 前端路由守卫能代替后端鉴权吗
不能。
路由守卫只能控制页面跳转和界面显示,用户仍然可以绕过页面直接发送请求。服务器必须重新验证身份、权限和数据归属。
前端权限控制 → 改善界面和交互体验
后端权限校验 → 决定操作是否真正允许
5. 前端环境变量能保存秘密吗
前端代码最终会下载到用户浏览器,因此构建进前端产物的变量可以被用户查看。
数据库密码、服务端私钥和高权限密钥不能因为写进 .env 就认为安全。
6. 前端输入校验能阻止攻击吗
前端校验可以改善用户体验,但攻击者能够绕开页面直接构造请求,所以服务端仍需校验参数、权限和业务规则。
十二、XSS 和 CSRF 有什么关系
如果网站已经存在 XSS,恶意脚本会运行在目标网站自己的页面中,因此可能:
读取页面中的 CSRF Token;
直接调用同源接口;
模拟用户完成操作。
所以严重的 XSS 往往能够绕过很多 CSRF 防护。
这也是为什么不能只处理 CSRF,而忽略页面中的 XSS 漏洞。
十三、常见面试追问
1. innerHTML 一定不能用吗
不是完全不能用,而是不能直接插入不可信内容。普通文本优先使用 textContent,必须渲染富文本时先使用可靠方案进行 HTML 净化。
2. Vue 的插值能防 XSS 吗
Vue 普通插值会转义文本,能够降低常见 HTML 注入风险。但 v-html、不安全 URL、第三方组件和手动 DOM 操作仍可能带来风险。
3. HttpOnly 能彻底防止 XSS 吗
不能。它只阻止 JavaScript 读取该 Cookie,不能阻止恶意脚本执行、修改页面或调用接口。
4. SameSite 能彻底防止 CSRF 吗
不能简单这样说。SameSite 能限制很多跨站请求携带 Cookie,但实际项目通常还会结合 CSRF Token、来源校验和敏感操作二次验证。
5. 只允许 POST 能防 CSRF 吗
不能。攻击者可以使用 HTML 表单构造部分 POST 请求。
6. XSS 能绕过 CSRF Token 吗
有可能。因为 XSS 代码运行在目标网站页面中,可能读取页面里的 Token,或者直接调用同源接口。
7. Token 应该放 localStorage 还是 Cookie
没有脱离场景的绝对答案:
localStorage 中的 Token → 不会自动随请求发送,但可能被 XSS 读取
HttpOnly Cookie → JavaScript 不能读取,但要考虑自动携带带来的 CSRF
项目需要结合 XSS 防护、SameSite、CSRF Token、跨域和登录刷新机制统一设计。
8. SQL 注入应该怎样防
SQL 注入主要由服务端使用参数化查询、预编译语句或正确使用 ORM 进行防护。前端校验容易被绕过,不能作为安全边界。
十四、面试回答模板
XSS
XSS 是跨站脚本攻击,本质是不可信数据进入了页面的危险位置,被浏览器当成代码执行。常见类型有存储型、反射型和 DOM 型。防护上应使用框架默认转义和
textContent等安全 API,避免直接插入不可信 HTML;必须渲染富文本时进行净化,并使用 CSP、HttpOnly 等措施降低风险。
CSRF
CSRF 是跨站请求伪造。用户登录目标网站后,攻击者诱导用户从其他网站发送请求,浏览器可能自动携带登录 Cookie,服务器如果没有验证请求意图,就可能执行敏感操作。常见防护包括 CSRF Token、SameSite Cookie、来源校验和敏感操作二次验证。
二者区别
XSS 关注恶意代码在可信页面中执行,CSRF 关注借用用户已有身份发送伪造请求。XSS 主要通过转义、净化和安全 DOM API 防护;CSRF 主要通过 Token、SameSite 和来源校验防护。
十五、秋招复习重点
必须掌握
XSS 和 CSRF 的核心原理;
存储型、反射型和 DOM 型 XSS;
XSS 与 CSRF 的区别;
二者的主要防护方式;
HttpOnly、Secure、SameSite 的作用。
常见追问
为什么 innerHTML、v-html 有风险;
为什么只允许 POST 不能防 CSRF;
CORS 为什么不等于 CSRF 防护;
HTTPS 为什么不能直接防 XSS、CSRF;
Token 放 Cookie 和 localStorage 的安全侧重点;
前端路由守卫为什么不能代替后端鉴权。
最后记住:
XSS 重点防止“不可信数据变成代码”,CSRF 重点防止“登录凭证被用于伪造请求”。前端负责安全渲染,服务端负责身份、权限和请求真实性校验。