ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

前端安全实战:防爬虫、源码保护与登录上传防护指南

前端安全实战:防爬虫、源码保护与登录上传防护指南 今天 Day 4终于聊到前端基础知识里最容易被忽略、但又特别值得花时间的一块内容前端安全。做前端的时间长了你会发现功能能做出来只是及格线代码能不能在真实环境里顶住爬虫、顶住抓包、顶住恶意请求才是决定一个项目能否上线的分水岭。我这两天在整理一个纯前端项目顺便梳理了防爬虫、防源码查看、前端登录、大文件上传这些场景里踩过的坑今天把 Day 4 的学习笔记完整写出来。内容围绕 Web 前端不涉及 Linux 桌面环境那类“输入法前端”咱们说的是浏览器里的这一层。这套笔记适合谁刚学完 Vue 或 React 基础、准备做第一个完整项目的人打算面前端岗位、想补安全面试题的人以及已经在写页面但总被后端同事吐槽“前端也能被刷接口”的人都可以直接参考。1. 为什么 Day 4 先聊安全而不是动画和性能1.1 前端的“裸奔”属性代码天生就暴露在用户手里很多初学者有个误区觉得前端代码跟后端代码一样是藏在服务器里的。实际上不是这样。前端所有的 HTML、CSS、JavaScript 最终都会被浏览器下载到本地用户按 F12 就能看到源码用抓包工具就能看到请求和响应。你的页面在用户眼里像一扇透明的橱窗商品摆在里面客户看得见别有用心的人也看得见。我经常用一个类比来解释这个概念后端是保险柜前端是保安室。保险柜本身有锁有密码但保安室是玻璃墙谁路过都能看到里面有多少人、几个人值班、换岗时间规律。你要是把重要文件放在保安室的桌面上那再强的保险柜也没用。这个类比放到代码里就是后端可以藏密钥前端只能藏门槛。所以前端的核心安全思路不是“让代码不可见”而是“让代码不可见也没关系或者让破解成本大于收益”。这也是为什么 Day 4 不先讲动画、不讲性能优化而是先讲安全——因为只要你的项目准备上线安全问题就会第一个找上门爬虫来抓数据、有人点开控制台改请求、脚本批量刷你的接口。功能做完了再补安全往往要重构一开始就想清楚成本低得多。1.2 从这两天的热搜词看一线前端在担心什么我在整理笔记的时候顺手看了一眼最近前端圈子里大家讨论比较多的话题发现一个规律大家关心的场景基本都能归到安全这一件事上。比如“前端 SDK 怎么做”本质上是你的代码要在别的站点上运行别人完全可以看到你的代码怎么防止 SDK 被二次打包、被绕过鉴权“直播的前端 H5 怎么做”涉及播放地址加密、防盗链“uni-app 前端用户登录实现”核心是 token 怎么安全存储、怎么防止越权“前端使用 worker 上传大文件”要解决文件被篡改、重复请求、非法类型绕过“纯前端部署”要考虑防盗链和接口防刷。再加上“前端面试题”里高频率出现的安全类问题比如 XSS、CSRF、点击劫持你会发现前端安全不是一个“额外加分项”而是工程化的基础设施。今天这篇 Day 4 就把这些点串起来从“防爬虫与防源码查看”这个最直观的话题展开再延伸到登录、传参、上传这些业务场景最后复盘排查技巧。2. 前端安全全景先分清威胁模型再动手2.1 前端安全不等于“藏源码”新手最容易踩的第一个坑就是把前端安全等同于“别让别人看到我的代码”。于是去网上搜“如何禁止查看源码”找到一堆禁用右键菜单、禁用 F12 的代码抄上去之后长出一口气觉得安全了。但实际上这层防护在专业爬虫面前约等于没有。为什么因为爬虫和恶意用户根本不需要打开浏览器。爬虫是用程序直接模拟 HTTP 请求的它不渲染 JavaScript、不执行页面逻辑、不关心你有没有禁用右键。它只需要拿到你的接口地址然后用 Python 或者 curl 直接发请求你的“禁止查看源码”对它来说就像在门把手上贴了一张“请勿入内”的纸条——正常人会看一眼机器人根本不理你。真正的前端安全应该先做一次威胁建模把事情拆开看。我把日常项目里最常见的威胁分成三大类第一类是被动暴露类。包括源码被直接阅读、页面内容被抓取、图片和接口被跨站盗用。这类问题的特点是对方没有“攻击”你的服务器只是利用了信息公然可见的特点。防爬虫、防源码查看、防盗链都属于这一类。第二类是主动攻击类。包括 XSS 注入脚本窃取用户信息、CSRF 伪造用户请求、点击劫持诱导用户操作。这一类不是防“读”而是防“改”属于必须从代码层面防范的漏洞。第三类是业务风险类。包括接口被批量刷、用户越权访问他人数据、提交数据被篡改比如把商品价格改成 1 分钱。这一类往往是前后端配合才能解决的问题前端能做的是“增加门槛”后端必须做的是“最终校验”。你看如果只是“藏源码”你只能覆盖第一类里很小的一块。所以我的建议是不要追求“让代码完全不可读”那是做不到的要追求“让破解这件事的成本变高、收益变低”这才是可行的目标。2.2 一个可复用的防护思路为每类威胁建一道闸门我习惯用一个“三道闸门”的框架来规划前端安全第一道闸门是身份闸门。先确认访问者是真的用户在正常操作而不是脚本。手段包括登录鉴权、设备指纹、验证码、频率限制。对应到前端就是 token 的携带、SDK 的设备信息采集、交互时的人机校验。第二道闸门是内容闸门。尽量让关键业务数据不要直接在 HTML 源码里暴露。手段包括动态渲染、接口按需加载、对关键数据做不可逆混淆。对应到前端就是减少“一次性把所有数据塞进页面”的做法改成用户真正需要时才向后端请求。第三道闸门是校验闸门。所有从客户端来的数据默认都是不可信的前端只负责格式校验和体验校验真正的权限校验和业务校验必须到后端。对应到前端就是不要在前端写“我是管理员”这样的判断而是每次请求都带上凭证让后端自己判断。这个框架的好处是它不会让你在“防爬虫”这个局部问题上钻牛角尖而是告诉你就算爬虫拿到了你的 HTML只要接口层面有鉴权和风控他能拿到的也只是公开数据。下面几个章节我会按这个框架展开实操。3. 防爬虫与防源码查看的核心实操3.1 禁用右键和禁 F12能防君子不能防脚本这部分是很多初学者第一个搜到的方案我先给出代码再说明局限。下面这段代码可以做到“普通用户按 F12 没反应、右键菜单被禁用”document.addEventListener(contextmenu, function(e) { e.preventDefault(); }); document.addEventListener(keydown, function(e) { if ( e.key F12 || (e.ctrlKey e.shiftKey e.key I) || (e.ctrlKey e.shiftKey e.key J) || (e.ctrlKey e.key U) ) { e.preventDefault(); } });这段代码放在一个独立的安全模块里页面加载时自动执行。它的真实作用是防普通用户误操作比如用户在页面上不小心按到 F12看到一堆控制台报错反而产生困惑或者用户想复制你的内容不知道右键被禁用还以为网站坏了。对于保护你的源码它基本没有效果。我实测过这种禁用在 Chrome 里可以通过“设置 - 更多工具 - 开发者工具”绕过而且爬虫根本不执行 JavaScript 也能拿到渲染后的 HTML。更尴尬的是一些合法用户为了复制联系方式会先右键发现没反应就直接关掉页面了反而伤到了转化率。所以我现在的建议是不要全站禁用右键只在需要保护的核心内容区域禁用复制并且做好提示。比如给内容区加一层的user-select: none配合一句“内容已做版权保护”的提示比全局禁用友好得多。3.2 JS 混淆与压缩提高门槛不是绝对安全既然源码必然要下发到浏览器那能不能让源码变得难以阅读能手段是混淆和压缩。现在主流的构建工具基本都内置了混淆能力Webpack 的terser-webpack-plugin、Vite 的esbuild都能在压缩的同时把变量名变成短名、去掉注释和空格。我做纯前端部署项目时一般会在生产构建配置里加上这些参数// vite.config.js 中针对生产环境的示例 import { defineConfig } from vite; export default defineConfig({ build: { minify: terser, terserOptions: { compress: { drop_console: true, // 移除 console.log drop_debugger: true // 移除 debugger }, mangle: true // 混淆变量名 }, sourcemap: false // 生产环境关闭 sourcemap } });注意drop_console这个配置比较关键。很多项目上线后忘了关掉console.log结果用户打开控制台能看到一堆业务日志包括接口地址、参数名等于给爬虫递了地图。生产环境移除 console 是成本最低的安全操作。但我要强调一点混淆只能让代码难读不能让代码不可读。现在有反混淆工具再加上 ChatGPT 这类 AI 辅助分析只要对方有耐心任何混淆过的 JS 都能被还原个七七八八。所以我把混淆归为“提高门槛”的手段真正的关键数据不应该出现在前端代码里尤其是 API 密钥、加密盐、后端地址这些敏感信息一旦打进包里混淆得再花哨也等于公开。3.3 动态渲染与字体反爬真正能挡住一部分爬虫的手段那么什么样的手段能真正挡住爬虫我实测下来最有效的是动态渲染和字体反爬它们解决的问题不是“源码可读”而是“内容获取成本”。动态渲染的思路是不把核心数据写死在 HTML 里而是让页面加载后再异步请求接口拿到数据再渲染到 DOM 上。这样爬虫如果只抓curl https://your-site.com/拿到的 HTML 里只有空壳没有数据它必须像正常人一样去解析 JavaScript、去请求接口成本上来了很多“低端爬虫”就放弃了。我做一个新闻类纯前端项目时用过这个方案列表页首次请求只返回一个 loading 框架然后用 axios 请求/api/articles渲染完成后再把数据填充进去。爬虫拿到首页源码看不到文章内容只有一片div idapp。当然爬虫进阶一点会直接模拟这个接口请求所以动态渲染必须配合接口层风控才能构成防线。字体反爬是很多电商和内容平台在用的方案把页面上展示的多个数字映射成自定义字体里的特殊字形。比如页面显示的“1234”实际拿到的 DOM 文本是“abcd”但浏览器用自定义字体把它们渲染成了数字。爬虫直接抓文本会得到一堆乱码虽然真实用户看起来完全正常。这个方案实现起来要前端和后端一起配合后端动态生成字体文件和映射表前端加载对应字体并应用类名。适合数字敏感的场景比如价格、销量、验证码但不适合大段正文。3.4 前端 SDK 的设备指纹给每个访问者建立档案最近有人总问前端 SDK 怎么设计我举个跟安全强相关的例子设备指纹采集。很多站点之所以能识别爬虫靠的就是设备指纹。设备指纹的思路是通过前端采集浏览器和设备的一些特征信息组合出一个大概率唯一的字符串作为访问者的“身份证”。常见可采集的信息包括User-Agent、屏幕分辨率、时区、语言、Canvas 指纹、WebGL 渲染器信息、CPU 核数、内存大小。把这些信息做一次哈希就能得到一个稳定的指纹值。下面是一个 Web 端简化版的设备指纹生成逻辑我在自己的项目里用过类似方案function getDeviceFingerprint() { const canvas document.createElement(canvas); const ctx canvas.getContext(2d); ctx.textBaseline top; ctx.font 14px Arial; ctx.fillText(frontend-security-day4, 2, 2); const canvasData canvas.toDataURL(); const ua navigator.userAgent; const screen ${window.screen.width}x${window.screen.height}x${window.screen.colorDepth}; const timezone Intl.DateTimeFormat().resolvedOptions().timeZone; const cores navigator.hardwareConcurrency || unknown; const deviceMemory navigator.deviceMemory || unknown; const raw [ua, screen, timezone, canvasData, cores, deviceMemory].join(|); return hashString(raw); // 可选用 SHA-256 对 raw 做哈希 }指纹生成以后每次请求都把它带在请求头里后端就能看到一个访问者短时间内是否频繁更换指纹。正常用户指纹稳定爬虫为了伪装往往频繁更换 UA 或者使用无头浏览器指纹要么缺失要么异常后台就可以针对性地限制。不过我要提醒一点设备指纹能提高风控能力但不能作为唯一鉴权依据因为它本身可以被伪造。更稳妥的做法是“设备指纹 用户登录态 行为频率”三个维度一起判断。4. 前端业务场景中的安全细节4.1 uni-app 登录实现token 别乱放过期要处理前端安全不能只停留在防爬虫业务场景里的细节同样重要。就拿“uni-app 前端用户登录实现”来说我见过很多项目把 token 直接存在本地还顺手把用户 Id、角色权限都存在了 localStorage 里。这种做法一旦遇到 XSS攻击者可以轻而易举地把 token 和用户信息一起偷走。登录态存储这块我的实践建议是第一分清敏感级别。token 属于敏感凭证但如果你的应用是纯前端部署、没有后端页面托管那么 HttpOnly Cookie 用不了这时候 localStorage 是可行方案但必须配合“token 有效期短 刷新 token 机制”。简单说就是accessToken 设置为 2 小时过期refreshToken 用于过期后重新换取accessToken 不要长时间有效。第二不要把用户权限完全信任前端。界面上隐藏按钮叫“权限控制”但真正的权限校验必须发生在后端。经常看到有人在前端代码里写if (user.role admin)来放行按钮但这只是隐藏懂行的用户直接模拟一个管理员请求就能绕过前端。正确的做法是按钮隐藏与否根据权限字段决定但每个后台接口自己会校验当前用户是否有权限执行操作前端隐藏只是体验后端校验才是边界。第三登录接口要做防重放与频率限制。这是前端安全里很少人提但很实用的细节。我在写 uni-app 登录时会在提交按钮上做一个 1 秒内只能点击一次的互斥锁同时配合一个简单的计数器超过 5 次失败就要求输入验证码。这样能挡住一部分简单的暴力破解和脚本刷接口。4.2 前端传参的安全隐患GET 和 POST 都可能被改前端传参是另一块非常容易泄露信息的重灾区。很多初学者习惯把数据放到 URL query 里比如https://api.example.com/getInfo?id123tokenxxx。这么做的问题不在“参数长短”而在于 URL 会被浏览器历史记录、服务器访问日志、代理日志完整保存。一旦被人拿到日志token 和业务参数就全暴露了。所以敏感信息一律要走 POST/请求体或者放在自定义请求头里。更重要的一个点是参数篡改。前端的任何参数最终都会被浏览器提交也都可以被修改。最常见的案例是提交订单时前端把商品单价和总价一起提交后端没有重新计算结果用户用抓包工具把价格改成 1 分钱提交订单就能以错误价格成交。前端有没有办法阻止很遗憾完全没有。你可以在前端校验金额格式、校验 ID 类型但最终校验一定是在后端后端必须根据商品 ID 重新查价格重新计算总金额忽略前端传过来的价格字段。前端能做的事只有一条——尽量减少后端需要信任的前端参数。我给前端新手们的建议是前端传参一律遵循“传 ID不传业务结果”的原则。要下单就传商品 ID 和数量不要传“总价”要改资料就传用户选择的数据不要传“操作者身份”。身份信息从 token 里解出来业务数值由后端重新算。这样即使参数被改了后端也能拦住。4.3 Worker 大文件上传前端校验和完整性校验怎么做“前端使用 worker 上传大文件”是这几天很多人讨论的话题。大文件上传本身用 Web Worker 是为了避免主线程卡顿把切片、哈希计算这些耗 CPU 的操作放到 worker 里。但从安全角度看上传环节有两个细节值得注意。第一是类型与大小的前端校验不能替代后端校验。Worker 里可以获取文件的name、size、type做初步校验但这些属性都是可以伪造的一个爬虫脚本完全可以声称一个可执行文件是image/png。所以前端校验的意义在于给普通用户即时反馈比如超过 500MB 直接提示而最终的病毒检查、文件类型校验必须由后端完成。第二是切片完整性校验。大文件上传会切成很多块每一块在网络传输过程中可能出错也可能被篡改。我在项目里会用 Worker 计算整个文件的 MD5 或 SHA-256 值然后后端合并切片时重新计算整个文件的哈希并比对。不匹配的直接拒绝合并。下面是 Worker 里一个简化的哈希计算思路// 在 worker 中读取文件并计算哈希简化示例 importScripts(https://cdn.example.com/spark-md5.min.js); self.onmessage function(e) { const file e.data.file; const chunkSize 2 * 1024 * 1024; // 2MB 切片 const chunks Math.ceil(file.size / chunkSize); const spark new SparkMD5.ArrayBuffer(); let currentChunk 0; const fileReader new FileReader(); function loadNext() { const start currentChunk * chunkSize; const end Math.min(start chunkSize, file.size); fileReader.readAsArrayBuffer(file.slice(start, end)); } fileReader.onload function(event) { spark.append(event.target.result); currentChunk; if (currentChunk chunks) { loadNext(); self.postMessage({ progress: currentChunk / chunks }); } else { self.postMessage({ hash: spark.end() }); } }; loadNext(); };哈希比对不是加密它只能保证“文件没有被改得内容不同”不能防止“文件本来就是恶意文件”。所以完整的链路是前端算哈希 → 后端合并后重新算哈希 → 比对一致性 → 再进行病毒扫描和内容审核。每一步各管一件事缺一个都不完整。5. 常见问题与排查技巧实录5.1 为什么我做了禁用右键和防 F12数据还是被爬走了这个问题我几乎每次写安全类内容都会遇到。原因是很多人把“浏览器内的防护”当成了“网络层的防护”。页面禁用右键、禁 F12影响的是“真人用户用浏览器打开页面时的体验”而爬虫是另一个世界它用代码直接发 HTTP 请求根本不关心你有没有渲染 DOM也不关心你有没有绑定键盘事件。你禁用右键之后爬虫照样拿到接口、照样解析 JSON、照样存数据。所以我常跟人讲排查防护是否有效不要只看浏览器里按 F12 有没有反应要直接模拟爬虫视角打开 Chrome 的开发者工具找到一条关键接口请求右键点击它选择“Copy as cURL”然后把这条命令丢到终端里跑一次。如果这条命令能成功拿到数据说明你的接口对所有爬虫都是裸奔的。5.2 加了 Referer 防盗链后图片突然加载不出来了防盗链是个经常引发误伤的操作。我把静态资源放到 CDN 后在 Nginx 里配置了只允许本站域名引用图片结果发现从微信里打开页面时图片全部加载失败。排查了半天才发现微信内置浏览器的请求头里Referer字段是空的而我在防盗链配置里把“无 Referer”的请求也拦截了。这个案例反映了一个防盗链经典取舍问题是要严格拦截一切非本域引用还是放行空 Referer我的建议是对于图片和静态资源可以放行空 Referer真正要严格的是接口防盗不能放行空 Referer否则爬虫去掉 Referer 就能绕过。图片被外站盗链只是流量损耗接口被刷才是安全问题优先级不一样配置策略也不该一样。5.3 生产环境关掉 sourcemap 后线上报错定位变得很难这是一个很真实的体验关掉 sourcemap 之后控制台里全是混淆后的变量名报错堆栈全是a.js:1这种信息根本不知道哪个模块出错了。很多人为了好调试就重新打开了 sourcemap结果把源码暴露给了所有人又回到安全问题上。我现在的折中方案是生产环境不向公网暴露 sourcemap但把 sourcemap 单独上传到错误监控平台比如 Sentry。这样前端代码不会泄漏开发者在监控后台却能看到映射后的原始报错位置。如果你没有用监控平台至少可以在构建流程里把 sourcemap 文件上传到私有服务器存储不给浏览器加载既满足调试又保住源码。提示不要以为关掉 sourcemap 是在防高手这个动作主要是防“顺手捡漏”。一个细心一点的开发者看到网络面板里请求了app.js.map就能还原出几乎完整的源码。5.4 前端面试安全题这 4 个问题最常被问最后整理一下前端安全面试题的高频考点。虽然我写的是 Day 4 学习笔记但顺带复盘面试题能帮你把知识点串起来第一题什么是 XSS如何防范XSS 是恶意脚本注入防范的核心是“永远不要把用户输入当 HTML 插入”转义输出、使用textContent而不是innerHTML、对不可信内容做 CSP 限制。第二题什么是 CSRF为什么纯前端项目容易忽视它CSRF 是跨站请求伪造本质是浏览器自动携带 Cookie 导致接口被第三方利用。纯前端项目如果只用 localStorage 存 token反而天然免疫一部分 CSRF但要注意携带 token 的方式不能放在 URL 里。第三题前端如何防止接口被刷答案是前端不能根本性防住只能配合后端做设备指纹 登录态 接口限频 验证码层层叠加。第四题如何防止爬虫爬你的页面数据分四层回答动态渲染让静态 HTML 无内容、接口风控限制异常请求、字体反爬增加解析成本、法律声明和 robots 协议作为最后兜底。这四类题都不难但如果你只答“禁用右键和混淆”面试官基本就知道你还没踩过生产环境的坑。我在 Day 4 的学习里最大的体会是前端安全不是“做一个防护措施就完事”而是“每一层都做一点让攻击者需要绕过多个关卡”。当我把自己当成一个爬虫、用 curl 去测试自己的接口时才发现原来很多防护在真实攻击面前都是纸糊的。这也是我想分享给你的第一个自测技巧别用浏览器看效果用终端直接打你的接口那才是爬虫看到的真实世界。
返回列表