
一、 同源策略同源策略Same-Origin Policy是浏览器最核心的安全机制。你可以把它理解为浏览器的“隔离病房”制度。1. 怎么才算“同源”浏览器规定只有当两个网页的协议、域名、端口这三项完全一模一样时才算“同源”。实际例子假设你当前在http://www.myblog.com这个网页上对比 URL是否同源原因http://www.myblog.com/about✅ 同源只是路径不同https://www.myblog.com❌ 跨域协议不同http vs httpshttp://api.myblog.com❌ 跨域域名不同主域 vs 子域http://www.myblog.com:8080❌ 跨域端口不同默认80 vs 80802. 为什么要有这个策略为了防止恶意网站搞破坏。比如你一边登录着网银一边打开了一个黑客网站。如果没有同源策略黑客网站就能用 JS 偷偷读取你网银的 Cookie或者读取你网页上的余额甚至直接操作你的 DOM 节点。有了这个策略浏览器就会把不同来源的网页隔离开来。二、 怎么解决跨域问题在实际的前后端分离开发中前端比如跑在localhost:3000去请求后端接口比如跑在localhost:8080是常态这时候必然会遇到跨域。业界有几种主流的解决方案我按实际开发中的使用频率给你排个序1. 开发环境首选前端代理Proxy通俗解释前端开发服务器比如 Vite 或 Webpack充当一个“跑腿小弟”。浏览器把请求发给前端服务器前端服务器再去请求后端然后把数据原封不动地返回给浏览器。因为浏览器只跟前端服务器打交道所以不存在跨域。实际例子在 Vue/React 项目的配置文件里写几行代码// vite.config.js 或 vue.config.js 中的 proxy 配置proxy:{/api:{target:http://localhost:8080,// 后端真实地址changeOrigin:true}}前端代码里直接请求/api/user代理会自动把它转发到http://localhost:8080/api/user。适用场景本地开发阶段极其方便不需要后端做任何改动。2. 生产环境首选CORS跨域资源共享通俗解释这是 W3C 制定的官方标准。相当于后端服务器在门口挂了个牌子“我允许http://localhost:3000这个网站来访问我的数据”。实际例子后端比如 Node.js 或 Java在响应头里加上Access-Control-Allow-Origin: http://localhost:3000Node.jsExpress示例app.use((req,res,next){res.setHeader(Access-Control-Allow-Origin,http://localhost:3000);res.setHeader(Access-Control-Allow-Methods,GET, POST, PUT, DELETE);res.setHeader(Access-Control-Allow-Headers,Content-Type);next();});适用场景前后端分离项目的生产环境。只要后端配合加上响应头前端什么都不用改这是目前最推荐、最标准的做法。3. 生产环境备选Nginx 反向代理通俗解释和开发环境的 Proxy 原理一样只不过这次“跑腿小弟”换成了专业的 Nginx 服务器。实际例子把前端代码和 Nginx 部署在一起Nginx 监听 80 端口。前端请求/api/user时Nginx 拦截请求转发给后端的 8080 端口再把结果返回给浏览器。server { listen 80; server_name www.myblog.com; location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { root /usr/share/nginx/html; index index.html; } }适用场景项目上线部署时。如果后端因为某些原因不方便配置 CORS或者需要统一处理多个后端服务的请求运维同学通常会用 Nginx 来解决。4. 老旧方案JSONP了解即可通俗解释利用script标签加载外部 JS 文件不受同源策略限制的特点。前端定义一个回调函数后端把数据包裹在这个函数里返回。实际例子前端请求http://api.com/data?callbackhandleData后端返回handleData({name: 张三})。scriptfunctionhandleData(data){console.log(收到数据,data);}/scriptscriptsrchttp://api.com/data?callbackhandleData/script适用场景只支持 GET 请求且存在 XSS 安全风险。现在基本已经被淘汰除非对接一些非常老旧的第三方接口否则不推荐使用。三、 什么是 XSS 攻击XSSCross-Site Scripting跨站脚本攻击通俗地说就是黑客在你的网页里“插了一段恶意的 JavaScript 代码”。当其他正常用户访问这个页面时浏览器不知道这是黑客的代码会老老实实地执行它从而导致用户的账号被盗、页面被篡改等严重后果。实际开发中的例子假设你开发了一个博客网站允许用户在评论区留言。黑客在评论区输入了一段代码script偷取Cookie并发送给黑客的代码/script。如果你的前端代码没有做任何处理直接把这段评论原封不动地渲染到了页面上比如使用了innerHTML。当其他用户打开这篇博客时浏览器就会把这段代码当成正常的 JS 执行黑客就能悄悄偷走这些用户的登录凭证Cookie从而盗取他们的账号。根据黑客“作案”的手法XSS 主要分为三种类型反射型 XSS钓鱼常用黑客把恶意代码藏在 URL 链接里骗你点击。你一点链接代码就执行了像“一次性炮弹”。存储型 XSS最危险就像上面博客评论区的例子黑客把恶意代码存到了数据库里。只要有人访问这个页面就会中招像“埋在业务数据里的地雷”。DOM 型 XSS前端最容易犯完全由前端 JS 处理不当造成。比如前端直接把 URL 里的参数拼接到了页面上没做任何过滤和后端服务器无关。四、 怎么防护 XSS防御 XSS 不能只靠一招我们需要一套“组合拳”。作为前端开发你需要掌握以下 5 个核心防御手段1. 输出转义最核心、最有效的一招通俗解释在把用户输入的内容显示到页面上之前把里面危险的特殊字符比如、、等替换成普通的文本。实际开发比如把替换成lt;把替换成gt;。这样浏览器看到lt;scriptgt;时只会把它当成一段普通的文字显示出来而不会当成代码去执行。常见需要转义的特殊字符及其对应的 HTML 实体字符实体编码说明lt;小于号防止标签注入gt;大于号防止标签注入quot;双引号防止属性闭合#39;或apos;单引号防止属性闭合amp; 符号防止实体解析注意输出转义要在输出时做而不是在输入时做。不同场景HTML 内容、HTML 属性、JavaScript 字符串、URL 参数需要的转义规则不同建议使用成熟的转义库而不是自己手写正则。2. 使用现代前端框架避开“危险 API”通俗解释不要手动去拼接 HTML 字符串实际开发推荐做法使用 Vue、React、Angular 等现代框架。它们默认会对绑定的数据进行自动转义天然防 XSS。绝对禁止在前端代码中尽量不要使用innerHTML、document.write或eval()这些危险的 API。如果非要插入 HTML请使用安全的纯文本方法textContent或者使用成熟的净化库如 DOMPurify。// ❌ 危险写法直接插入用户输入document.getElementById(comment).innerHTMLuserInput;// ✅ 安全写法使用 textContentdocument.getElementById(comment).textContentuserInput;// ✅ 如果必须使用 HTML先用 DOMPurify 净化importDOMPurifyfromdompurify;document.getElementById(comment).innerHTMLDOMPurify.sanitize(userInput);3. 校验用户输入第一道防线通俗解释永远不要相信用户的输入在数据进入系统时就把“坏人”挡在门外。实际开发比如用户填手机号你就用正则校验是不是纯数字填邮箱就校验邮箱格式。对于昵称等字段限制长度并禁止输入、等特殊字符。// 前端输入校验示例functionvalidateNickname(nickname){// 长度限制if(nickname.length20)returnfalse;// 禁止特殊字符if(/[]/.test(nickname))returnfalse;returntrue;}functionvalidatePhone(phone){// 只允许纯数字11 位return/^\d{11}$/.test(phone);}重要补充前端校验只是提升用户体验不能作为安全防线。后端也必须做同样的校验因为黑客可以绕过前端直接向后端发送恶意数据。4. 配置 CSP内容安全策略通俗解释相当于给浏览器下发一份“白名单”。实际开发通过配置 HTTP 响应头告诉浏览器“这个页面只允许加载我们自己域名下的 JS 脚本”。这样一来就算黑客成功注入了代码因为他的代码来源不在白名单里浏览器也会直接拦截不让它执行。Content-Security-Policy: default-src self; script-src self unsafe-inline; style-src self unsafe-inline; img-src self data:;也可以在 HTML 的meta标签中配置metahttp-equivContent-Security-Policycontentdefault-src self; script-src self https://api.example.com;常见 CSP 指令说明指令作用default-src默认加载策略所有资源类型的兜底规则script-src限制 JavaScript 脚本的加载来源style-src限制 CSS 样式表的加载来源img-src限制图片的加载来源connect-src限制 XHR、WebSocket 等请求的目标地址5. 给 Cookie 加上 HttpOnly 属性最后一道防线通俗解释给存放登录凭证的 Cookie 加一把锁让 JS 代码“看得到摸不着”。实际开发在后端设置 Cookie 时加上HttpOnly标记。这样即使黑客的 XSS 脚本成功执行了当他试图用document.cookie偷取登录凭证时会发现是空的从而保护了用户的账号安全。后端设置 Cookie 示例Node.js / Expressres.cookie(sessionId,abc123,{httpOnly:true,// 禁止 JS 读取secure:true,// 仅在 HTTPS 下传输sameSite:Strict// 防止 CSRF});补充建议除了HttpOnly还建议同时设置Secure仅 HTTPS 传输和SameSite防止跨站请求伪造属性构建多层 Cookie 防护。五、 什么是 CSRF 攻击CSRFCross-Site Request Forgery跨站请求伪造通俗地说就是黑客“狐假虎威”。黑客不需要知道你的密码也不需要窃取你的Cookie他只需要诱导你点击一个链接利用你当前已经登录的身份以你的名义向网站发送恶意请求。实际开发中的例子假设你开发了一个电商网站用户登录后可以修改收货地址。用户小明登录了你的电商网站浏览器保存了有效的登录Cookie。这时小明收到一封邮件里面写着“点击领取免费优惠券”并附带了一个链接。小明在没退出登录的情况下点击了链接这个链接其实是一个恶意网站里面隐藏了一段自动提交的表单代码悄悄向你的电商网站发送了“修改收货地址为黑客仓库”的请求。因为小明的浏览器在请求时会自动带上你的电商网站的登录Cookie你的服务器一看Cookie是对的就以为是小明自己操作的于是成功修改了地址。核心区别对比维度XSSCSRF利用对象用户对网站的信任网站对用户浏览器的信任攻击方式在网页中注入并执行恶意脚本诱导用户发起恶意请求是否需窃取 Cookie可能需要不需要直接利用浏览器自动携带的 Cookie典型危害窃取 Cookie、篡改页面、钓鱼以用户身份执行非预期操作转账、修改地址等六、 怎么防护 CSRF防御 CSRF 的核心思想是让服务器能区分这个请求到底是用户主动发的还是被黑客诱导发的。业界有以下几种主流方案1. 使用 CSRF Token最核心、最可靠的防御通俗解释相当于给用户的每次敏感操作发一张“一次性防伪验证码”。实际开发用户登录或打开页面时服务器生成一个随机且不可预测的 Token返回给前端。前端在发送敏感请求如修改密码、转账时必须把这个 Token 放在请求头如X-CSRF-Token或表单的隐藏字段里。服务器收到请求后对比请求里的 Token 和服务器存的 Token 是否一致。黑客在第三方网站根本拿不到这个 Token所以请求会被服务器直接拒绝。传统表单中嵌入 Tokenformaction/change-addressmethodPOSTinputtypehiddennamecsrf_tokenvalue随机生成的Tokeninputtypetextnameaddressplaceholder新地址buttontypesubmit提交/button/formAjax 请求中携带 Tokenfetch(/api/change-address,{method:POST,headers:{Content-Type:application/json,X-CSRF-Token:getCsrfToken()// 从 cookie 或 meta 标签中获取},body:JSON.stringify({address:新地址})});2. 设置 SameSite Cookie 属性现代浏览器利器通俗解释给 Cookie 加个“防跨界”标签告诉浏览器“这个 Cookie 只能在本站用去别的网站不能带上它”。实际开发后端在设置 Cookie 时加上SameSite属性SameSiteStrict最严格完全禁止跨站携带 Cookie。但可能会影响用户体验比如从外部链接点进来时无法自动登录。SameSiteLax推荐做法允许部分安全的跨站 GET 请求比如从外部链接点进来但禁止跨站 POST 请求携带 Cookie能防住绝大多数 CSRF 攻击。取值行为适用场景Strict完全禁止跨站时携带 Cookie对安全性要求极高的场景Lax允许安全的跨站 GET 请求如点击链接禁止跨站 POST 携带 Cookie大多数网站推荐默认选择None允许跨站携带 Cookie需配合Secure跨站嵌入场景如第三方登录Node.jsExpress示例res.cookie(sessionId,abc123,{sameSite:Lax,// 推荐httpOnly:true,secure:true});补充说明现代浏览器Chrome 80已经默认将SameSite设置为Lax但为了兼容旧版浏览器建议显式设置。3. 验证 HTTP Referer / Origin 字段辅助防御通俗解释检查请求的“快递单号”看看这个请求到底是从哪个网站发过来的。实际开发服务器检查请求头里的Referer或Origin如果不是来自自己的合法域名就直接拦截。后端校验示例Node.jsapp.use((req,res,next){constoriginreq.get(Origin);constrefererreq.get(Referer);constallowedOrigins[https://www.myblog.com,http://localhost:3000];if(req.methodPOST(!origin||!allowedOrigins.includes(origin))){returnres.status(403).json({error:非法请求来源});}next();});注意这只能作为辅助手段因为有些浏览器或隐私插件可能会隐藏Referer字段或者某些情况下Origin为空不能作为唯一防线。4. 关键操作增加二次验证终极防线通俗解释对于极其重要的操作必须让用户再证明一次“我是我”。实际开发比如大额转账、修改绑定手机号、删除账号等场景除了常规校验前端必须强制弹出验证码输入框、短信验证码或人脸识别。即使黑客触发了 CSRF 请求因为没有第二层验证操作也无法完成。实践建议在修改关键信息前要求用户输入当前密码或验证码。对于支付操作要求二次确认指纹、Face ID、短信验证码。后端在处理这些请求时除了校验 CSRF Token还要验证二次验证凭证的有效性。七、 CSP内容安全策略1. CSP 是什么CSP 的全称是 Content Security Policy。你可以把它理解为浏览器里的一位“铁面无私的安检员”。在以前浏览器是个“老好人”网页让它加载什么脚本、样式它就乖乖加载这就给了黑客可乘之机。有了 CSP 之后网站管理员可以提前给这位安检员发一份“白名单”。安检员在放行任何资源JS、CSS、图片等之前都会严格核对这份名单不在名单上的一律拦截2. 它能实现哪些防护在实际开发中CSP 主要能帮你挡住以下三大类攻击防范跨站脚本攻击XSS实际场景黑客在你的网页里偷偷注入了一段script恶意代码。CSP 的作用因为这段代码的来源不在白名单里或者它是一段“内联代码”安检员会直接把它拦下不让它执行。防止数据被偷偷窃取数据注入防护实际场景黑客的代码虽然没执行但他试图用fetch或ajax把用户的 Cookie 发送到他的黑客服务器。CSP 的作用CSP 里有个指令专门管网络请求。如果黑客的服务器不在白名单里浏览器会直接切断这个请求数据根本发不出去。防止“点击劫持”Clickjacking实际场景黑客用一个透明的iframe把你的网页套在他的钓鱼网站里诱导用户点击。CSP 的作用通过frame-ancestors指令你可以规定“只有我自己的网站才能用 iframe 嵌套我”其他网站想套壳直接拒绝3. CSP 配置使用在实际工作中CSP 主要有两种配置方式。通常推荐第一种第二种作为备用。1. 服务器端配置最推荐最安全由后端或运维同学在服务器比如 Nginx、Apache上配置 HTTP 响应头。Nginx 实际例子在nginx.conf配置文件里加一行add_header Content-Security-Policy default-src self; script-src self https://cdn.jsdelivr.net; img-src self data:; upgrade-insecure-requests;;(翻译默认只允许同源资源JS 允许同源和 jsdelivr CDN图片允许同源和 base64强制把 HTTP 升级为 HTTPS。)2. 前端 HTML 配置适用于纯静态页面如果你没有权限改服务器配置比如托管在 GitHub Pages 上的博客可以直接写在 HTML 的head标签里。实际例子metahttp-equivContent-Security-Policycontentdefault-src self; script-src self https://cdn.bootcdn.net; img-src * data:; upgrade-insecure-requests⚠️新手避坑指南这个meta标签必须放在所有其他资源加载标签如script、link的前面如果放在后面有些资源可能在 CSP 规则生效前就已经加载了配置就失效了。4. CSP防护原理CSP 实现防护的底层逻辑并不是什么神秘的魔法而是浏览器内部的一套“拦截与校验机制”。第一步下发“安保手册”服务器下发策略原理当用户访问你的网站时服务器会在 HTTP 响应头Content-Security-Policy中把一份“白名单规则”发给浏览器。实际场景这就像物业经理服务器给小区大门的保安浏览器发了一本《安保手册》上面写着“只允许业主同源和顺丰快递白名单域名进小区其他人一律不准进。”第二步浏览器“接管”资源请求浏览器拦截原理浏览器拿到这份手册后会把它保存在内存中。接下来当网页的 HTML 开始解析或者 JavaScript 代码试图发起任何资源请求比如加载一个script、发一个fetch请求、甚至执行一段内联代码时浏览器都会强制暂停这个动作先去查阅一下《安保手册》。实际场景小区里有人网页代码要带一个包裹资源进小区保安浏览器会立刻伸手拦住“等一下我先查查手册上有没有你的名字。”第三步严格“比对白名单”策略匹配原理浏览器会提取出当前请求的“来源”Origin或“类型”与手册上的指令进行精确匹配。如果请求的域名在白名单里或者带有合法的nonce随机令牌浏览器就会放行。如果不在白名单里或者试图执行被禁止的内联代码unsafe-inline浏览器就会判定为“违规”。实际场景保安翻开手册一看发现这个包裹的寄件人是“黑客公司”根本不在允许名单上。第四步执行“拦截与上报”阻断执行原理一旦判定违规浏览器会从网络层或执行层直接掐断这个请求。被拦截的 JS 代码绝对不会被执行被拦截的图片绝对不会被渲染。同时如果配置了report-uri浏览器还会偷偷给服务器发一个 JSON 格式的违规报告。实际场景保安直接把包裹扔出小区拦截执行然后拿起对讲机向物业经理汇报“经理刚刚有个黑客公司的包裹想混进来被我拦下了”上报违规日志。总结你以前可能会觉得防 XSS 靠的是“把用户输入转义”。但 CSP 的实现机制告诉你即使黑客的代码成功混进了 HTML 里只要它不符合浏览器的白名单规则浏览器就会在底层直接把它“废掉”。这就是为什么 CSP 被称为前端的“兜底防线”——它把安全的控制权从“不可靠的用户输入”转移到了“绝对权威的浏览器引擎”手里。