安全技能1:不要滥用Storage,应将 Token 添加到cookie 并且做httponly、samesite、secure限制
-
场景复现:今天试了一下,在Chrome和Firefox打开自家项目网站,先Chrome登录后,将其sessionStorage的所有数据,复制一遍,放到Firefox上的
-
结果:刷新一遍无需登录也能进入网站(诸位也可以试试自家网站)
-
危险性漏洞:当有人使用跨站脚本攻击获取到你的本地缓存数据后,那么无需账密也能进入你的网站
-
分析:这是因为token等登陆状态信息都放在了会话存储中
-
解决方案:
- 因为本地缓存localStorage或sessionStorage,都没有加密或限制功能,都能用js脚本随意获取到storage数据,进而存在一旦被跨站脚本攻击成功则GG。
- 而客户端数据存储方案,还有cookie技术,且cookie正有限制选项功能,能够限制是否能够被读取。
- 当然,前端只需要配置Credentials即可,无需去操作cookie,后端发送过来的响应头的Set-Cookie,将会在下一次HTTP请求头的Cookie自动一起发送过去。
- cookie主要配置项:
httpOnly: true/false;- 设置了 HttpOnly 属性为true的 cookie,将不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequest 和 Request APIs 进行访问,以防范跨站脚本攻击。
secure: true/false;- 一个带有 secure 安全属性的 cookie, 只有在请求使用SSL和HTTPS协议的时候才会被发送到服务器。
sameSite: None/Strict/Lax;- SameSite属性用来限制第三方 Cookie,使cookie不能随着跨域请求一起发送,从而防范跨站请求伪造攻击(CSRF),减少安全风险。
- 注意: 如果
sameSite属性值为None,则cookie的secure必须为true,否则将会遭到客户端的Block拒绝,无法写入cookie
- cookie主要配置项:
-
延伸知识:XSS & CSRF
- XSS跨站脚本攻击盗取用户信息手段
- 非持久型 XSS 漏洞攻击
- 通过 URL (类似:
https://xxx.com/xxx?default=<script>alert(sessionStorage)</script>) 注入可执行的脚本代码,从而盗取数据
- 通过 URL (类似:
- 持久型 XSS 漏洞攻击
- Form 表单提交,将内容提交进入数据库保存,当前端页面获得后端从数据库中读出的注入代码时,恰好将其渲染执行,比如类似在文本域或富文本输入
<script>alert(sessionStorage)</script>
- Form 表单提交,将内容提交进入数据库保存,当前端页面获得后端从数据库中读出的注入代码时,恰好将其渲染执行,比如类似在文本域或富文本输入
防御以上这两种XSS攻击,一般是转义字符,或设置CSP白名单
- 非持久型 XSS 漏洞攻击
- CSRF:跨站请求伪造
- 完成 CSRF 攻击的三个条件:
- 用户已经登录了站点 A,并在本地记录了 cookie
- 在用户没有登出站点 A 的情况下(也就是 cookie 生效的情况下),访问了恶意攻击者提供的引诱危险站点 B (B 站点要求访问站点A)。
- 站点 A 没有做任何 CSRF 防御
防御CSRF:Cookie SameSite、 Referer Check、 Anti CSRF Token、 验证码
- 完成 CSRF 攻击的三个条件:
- XSS跨站脚本攻击盗取用户信息手段
安全技能2:代码混淆压缩:
- 使用Vue开发的话,要进行代码混淆压缩,可借助插件
uglifyjs-webpack-plugin或者terser-webpack-plugin - vue-cli3打包压缩、混淆、去注释 参考文档
安全技能3:JS签名规则加密
-
场景案例参考:再解决不了前端加密我就吃shi
- 亲测了一遍这位狠人的方法,虽然该Tmall网站的加密规则及字段参数已有所改变,但获取流程依然不变,debug拿到加密规则rsaPassword和Local.e,便能获得password2值
-
问题:既然能被破解,要你加密何用?
- 虽然前端经过混淆和压缩,以及函数加密,依然无法彻底有效保护代码,但能提高攻击者进行攻击的难度,使之增加攻击者成本,而不是直接爬取js脚本后就能一眼得知加密规则,那么我们的防护目的也就达到了
-
JS签名规则加密
虽然前端经过混淆和压缩,但如前文案例,依然可被debug一步步查找后拿到签名的相应生成规则。
所以,为了给攻击者添堵,使攻击者看到的是“一堆乱码”,看不出任何实际的内容,将签名规则函数进一步加密是很有必要的防护手段!- 加密方式:一种或多种一起使用(前后端保持一致加密规则对比各自签名是否一致)
- base64:
window.btoa/window.atob - md5:
hex_md5/b64_md5/str_md5(加密不可逆) - sha1 (加密不可逆)
- escape
- AES
- ……
- base64:
- 其他加密方式: jsEncrypto 加密脚本工具
借助该工具,自己再进行调整使之适用自己当前的项目,使代码得到更深度的加密,甚至我们可以将整个脚本进行加密,如果你为了安全性完全不考虑服务器带宽压力的话。
- 示例:
- 未加密前的sign规则函数:
function sign(obj, util, md5, APP_SECRET) { let str = util.addStr(util.objKeySort(obj)); str = encodeURIComponent(str); str = str.replace(/\'/g, "%27"); str = str.replace(/\!/g, "%21"); str = str.replace(/\*/g, "%2A"); str = str.replace(/@/g, "%40"); str = str.replace(/\(/g, "%28"); str = str.replace(/\)/g, "%29"); str = APP_SECRET + str.toUpperCase() + APP_SECRET; str = md5.hex_md5(str); return str; } - 加密后的sign规则函数:
let dF = new Function('return '+ unescape('%66%75%6E%63%74%69%6F%6E%20%64%46%28%73%29%7B%76%61%72%20%73%31%3D%75%6E%65%73%63%61%70%65%28%73%2E%73%75%62%73%74%72%28%30%2C%73%2E%6C%65%6E%67%74%68%2D%31%29%29%3B%20%76%61%72%20%74%3D%27%27%3B%66%6F%72%28%6C%65%74%20%69%3D%30%3B%69%3C%73%31%2E%6C%65%6E%67%74%68%3B%69%2B%2B%29%74%2B%3D%53%74%72%69%6E%67%2E%66%72%6F%6D%43%68%61%72%43%6F%64%65%28%73%31%2E%63%68%61%72%43%6F%64%65%41%74%28%69%29%2D%73%2E%73%75%62%73%74%72%28%73%2E%6C%65%6E%67%74%68%2D%31%2C%31%29%29%3B%72%65%74%75%72%6E%28%75%6E%65%73%63%61%70%65%28%74%29%29%3B%7D'))() let sign = new Function('return '+ dF('kzshynts*75xnls*7%3Dtgo*7H*75zynq*7H*75ri%3A*7H*75FUUdXJHWJY*7%3E*75*%3CG*5F*75*75qjy*75xyw*75*8I*75zynq3fiiXyw*7%3Dzynq3tgoPj%7EXtwy*7%3Dtgo*7%3E*7%3E*8G*5F*75*75xyw*75*8I*75jshtijZWNHtrutsjsy*7%3Dxyw*7%3E*8G*5F*75*75xyw*75*8I*75xyw3wjuqfhj*7%3D4*%3AH*7%3C4l*7H*75*77*7%3A7%3C*77*7%3E*8G*5F*75*75xyw*75*8I*75xyw3wjuqfhj*7%3D4*%3AH*764l*7H*75*77*7%3A76*77*7%3E*8G*5F*75*75xyw*75*8I*75xyw3wjuqfhj*7%3D4*%3AH/4l*7H*75*77*7%3A7F*77*7%3E*8G*5F*75*75xyw*75*8I*75xyw3wjuqfhj*7%3D4E4l*7H*75*77*7%3A95*77*7%3E*8G*5F*75*75xyw*75*8I*75xyw3wjuqfhj*7%3D4*%3AH*7%3D4l*7H*75*77*7%3A7%3D*77*7%3E*8G*5F*75*75xyw*75*8I*75xyw3wjuqfhj*7%3D4*%3AH*7%3E4l*7H*75*77*7%3A7%3E*77*7%3E*8G*5F*75*75xyw*75*8I*75FUUdXJHWJY*750*75xyw3ytZuujwHfxj*7%3D*7%3E*750*75FUUdXJHWJY*8G*5F*75*75xyw*75*8I*75ri%3A3mj%7Ddri%3A*7%3Dxyw*7%3E*8G*5F*75*75wjyzws*75xyw*8G*5F*%3CI5'))()
- 未加密前的sign规则函数:
- 示例:
- 加密方式:一种或多种一起使用(前后端保持一致加密规则对比各自签名是否一致)
-
延伸:Token、Sing与加密的作用
- Token的作用:
- 判断用户身份的标识
- 签名Sign的作用:
- 保证API接口通信时数据的安全性,进行数据校验是否有不安全的字段提交,防止信息被篡改和伪造
- 加密的作用:
- 将数据或代码加密,使明文变成暗文,让攻击者看不出任何实际的内容
- Token的作用:
代码漏洞扫描优化
安全尚无银弹,狼人依旧猖狂
-
Vue 安全
-
NPM第三方依赖的安全隐患
- npm@6.0新特性:
npm auditnpm audit需要当前项目存在package.json和package-lock.json文件。- 如果没有这两个文件,将会报相应
EAUDITNOPJSON和EAUDITNOLOCK错误提示 - 生成
package.json文件:npm init - 生成
package-lock.json文件:npm i --package-lock-only
- 如果没有这两个文件,将会报相应
npm audit相关命令-
npm audit [--json | --parseable]: 默认输出table格式,--json输出json格式,--parseable输出纯文本格式-
如下图第一行:
- Severity: 有四个等级:critical(立即处理), High(尽快处理), Moderate(有时间就处理), Low(自行决定要不要处理)
- Description:漏洞的描述
-
Package: 哪个包有问题
-
Patched in: 打补丁,哪些版本可修复漏洞的版本范围
-
Dependency of: 你的项目中哪个包依赖了这个有问题的包
-
Path: 项目中的这个包到有问题的包之间的依赖路径
-
More info: 安全报告的链接, 查看这个问题的更多详细信息
注意1: 该
npm audit命令在 npm@6 中可用。要升级,请运行npm install npm@latest -g.
注意2:npm audit检查内容包含直接依赖项、devDependencies、bundledDependencies 和 optionalDependencies,但不检查 peerDependencies.注意3:
npm audit命令在淘宝镜像不支持使用,必须是npm初始源https://registry.npmjs.org/.
-
-
npm audit fixnpm aduit [fix]扫描并会自动更新到semver兼容的版本来消除漏洞
-
npm audit fix --force--force强制更新到安全版本, 即使semantic versioning不兼容也会更新- 但不一定能帮你修复所有的漏洞,有些需要手动去根据audit提示进行安装或卸载相应依赖
- 有时修复后的依赖包不符合当前项目,导致运行崩溃,所以切记修复前做好备份,以便回滚
注意:
npm audit fix [--force]强制修复后,记得重新npm i加载依赖,再重新运行项目npm run dev,避免因为依赖丢失而导致项目报错无法运行-
- npm相关命令:
- 查看当前npm源:
npm config get registry - 配置npm源:
-
原始镜像:
npm config set registry https://registry.npmjs.org -
淘宝镜像:
npm config set registry https://registry.npm.taobao.org
-
- 查看当前npm源:
- npm@6.0新特性: