前端安全基础:XSS、CSRF、CSP 与敏感信息泄露
本文目录
前端安全不是只靠后端兜底。只要页面渲染用户内容、保存 token、调用管理接口,就要考虑攻击面。
XSS:按输出上下文处理不可信数据
- 默认不要把用户输入当 HTML 渲染。
- Markdown 渲染要走白名单 sanitize。
- 评论、弹幕、划线批注只用 textContent 注入。
- 富文本编辑器上传的 HTML 要在服务端再清洗。
CSRF:Cookie 会被浏览器自动携带
如果鉴权依赖 Cookie,跨站请求可能携带用户身份。常见防护包括 SameSite、CSRF Token、关键操作二次确认。若使用 Authorization Header,也要防止 token 被 XSS 拿走。
CSP:纵深防御,不替代修漏洞
CSP 可以限制脚本、样式、图片和连接来源。个人博客可以先从报告模式开始,逐步收紧:禁止未知脚本、限制 iframe、限制 connect-src。
其它数据边界
- 不在前端包里写密钥。
- 不在公开文章里写内部域名和真实 token。
- 错误日志不要带完整请求头。
- 用户上传文件要控制类型、大小和访问权限。
存储型、反射型和 DOM 型 XSS 的共同点,是不可信数据最终进入了会按 HTML、脚本、URL 等规则解释它的上下文。框架模板默认转义应保留;innerHTML、insertAdjacentHTML、React dangerouslySetInnerHTML、Vue v-html 都是需要审查的边界。简单评论、弹幕和批注应作为文本插入;只有确实要展示富文本时,才用维护中的白名单 sanitizer。
// 普通评论按纯文本显示。
commentNode.textContent = untrustedComment;
// 确实要展示格式化 HTML 时,使用维护中的 allow-list sanitizer。
richTextNode.innerHTML = DOMPurify.sanitize(untrustedHtml, {
ALLOWED_TAGS: ["p", "a", "strong", "em", "ul", "ol", "li", "code", "pre"],
ALLOWED_ATTR: ["href", "title"],
});
清洗不是任意上下文的通用编码器;HTML 文本、属性、脚本字符串、CSS 和 URL 的规则不同。不要用正则替代解析型 sanitizer,也别只在浏览器显示前处理而忘记服务端导出、邮件模板和后台预览。
如果会话凭证放在 Cookie 里,浏览器可能在用户访问攻击者页面时自动附带目标站点 Cookie。改变状态的操作不要使用 GET,并结合合适的 SameSite、CSRF token、Origin/Referer 校验和明确的授权检查。SameSite 是防御层;CORS 主要控制跨源读取,不能单独证明请求不是伪造的。
下面只展示同源请求形态。token 的生成、轮换和服务端验证必须由应用后端实现;若 token 缺失,不应静默发送写请求。
const csrfToken = document.querySelector("meta[name=csrf-token]")?.content;
if (!csrfToken) throw new Error("缺少 CSRF token");
const response = await fetch("/api/profile", {
method: "PATCH",
credentials: "same-origin",
headers: { "Content-Type": "application/json", "X-CSRF-Token": csrfToken },
body: JSON.stringify(profile),
});
if (!response.ok) throw new Error(`保存失败:HTTP ${response.status}`);
把 Bearer token 放进 localStorage 不能抵御 XSS,注入脚本也能读取它。HttpOnly Cookie 降低脚本直接读取凭证的风险,但 XSS 仍可能以当前用户身份发请求;所有接口仍要检查身份、资源所有权和角色。
CSP 限制浏览器可以加载和执行的资源。先盘点脚本、图片、字体、媒体、API 和 iframe 来源,使用 Report-Only 收集误报,再逐步收紧。Nonce 必须由服务端针对每个响应生成高熵随机值,不能写成常量或跨响应复用。
Content-Security-Policy-Report-Only: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; script-src 'self' 'nonce-{每次响应独立生成}'; connect-src 'self' https://api.example;
上面只是策略讨论的占位示例,不是可直接复制到生产的通用配置。第三方脚本、统计 SDK、内联样式和媒体源都可能受影响;先检查真实 CSP 报告,再切换强制模式。CSP 是纵深防御,不能替代修复注入漏洞。
浏览器代码、构建产物和 source map 都可以被用户下载,前端环境变量不是秘密。上传的扩展名和 accept 也不是安全证明:后端需复核大小、格式特征、用户权限、存储路径和恶意内容。错误遥测只记录排障必要信息,并过滤 Cookie、Authorization、验证码、个人信息和正文。
安全回归清单
- 用尖括号、引号和事件属性测试文章、评论、搜索词和错误提示是否只显示文本。
- 检查 HTML sink、Markdown/富文本、URL 跳转、iframe 和
postMessage来源校验。 - 对登录、修改、删除、上传等写请求验证 CSRF、授权、频率和重放边界。
- 在浏览器检查真实响应头、Cookie 属性、CSP 报告和 source map 发布策略。
- 扫描日志、错误上报、前端构建产物和仓库历史,确认没有密钥及多余个人数据。
官方参考
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。