一定让你了解全面的【前端安全】干货

1,770 阅读13分钟

通此此文学到的是:

  • session、token、JWT、cookie之间的基本区别
  • 同源策略
  • cookie 的 SameSite 属性
  • XSS、CSRF、点击劫持等攻击方式、防御原理、防御方式
  • HTML5引入的安全措施和问题

总结之后,联系工作中解决方案:

  • 服务端html片段,都是编码后的
  • 跨域设置
  • JWT认证(Authorization 头部传递给后端做验证)
  • get请求只用在获取数据

我们总感觉在工作中很少用到,前端安全性问题容易忽视,总结完之后其实发现前端接触不少,比如:浏览器的SameSite、Referer、同源策略、浏览器自带防XSS,不管怎么说,安全防护是必不可少的。

面试的时候多次被问到安全问题,但是自己只能在平时学习的基础上回答上几点,覆盖不全面,所以下班回来花时间全面总结这方面的知识,当然除了应付面试,对自己也是个学习帮助。

每次看到网站,不仅看到的是样式、适配性、兼容性、性能等问题,可能还有安全问题。得做一个有温度的开发者。

看了很多blog,写得很不详细,通过不断拷问自己的方式,顺藤摸瓜去找到对应的解决办法。

我觉得不管写blog还是搞科研都要有专研素质,记得毕业论文搞算法研究,那真叫各种场景梦现。

先简单介绍下sessionId、token、cookie之间的区别,为了熟悉认证怎么防止CSRF。

session、token、JWT、cookie之间的区别

session

  • session 是另一种记录服务器和客户端会话状态的机制
  • session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中
  • SessionID 是连接 Cookie 和 Session 的一道桥梁

token

  • 服务端无状态化、可扩展性好
  • 支持移动端设备
  • 安全
  • 支持跨程序调用

JWT(JSON Web Token)

  • 目前最流行的跨域认证解决方案,是一种认证授权机制。
  • JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上。
  • 可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的。

cookie

  • HTTP 是无状态的协议
  • cookie 存储在客户端
  • cookie 是不可跨域的

同源策略

同源策略其实是浏览器约定的一种最基本的安全功能。

不受同源策略的标签:<script>、<img>、<iframe>、<link>、<video>、<audio>

同源策略的限制:

  1. ajax请求
  2. DOM操作
  3. Cookie的获取
  4. Session Storage 和 Local Storage

Cookie 的 SameSite 属性

SameSite属性有三种取值:

  • None:不禁止第三方 Cookie
  • Lax:部分禁止第三方 Cookie,只会在使用危险 HTTP 方法发送跨站 Cookie 的时候进行阻止
  • Strict:完全禁止第三方 Cookie,浏览器不允许将 Cookie 从 A 站发送到 B 站

各大浏览器之前的默认值都是 None,因此上面的 POST 请求会被成功执行,后来 Chrome 把默认值改成 Lax 了,于是上面的 POST 请求就不会携带 Cookie,从而有效阻止 CSRF 攻击。下面列举了当 SameSite 取值为 Lax 时是否发送 Cookie 的场景:

请求类型示例正常情况Lax
链接<a href="..."></a>发送 Cookie发送 Cookie
预加载<link rel="prerender" href="..."/>发送 Cookie发送 Cookie
GET表单<form method="GET" action="...">发送 Cookie发送 Cookie
POST表单<form method="POST" action="...">发送 Cookie不发送
iframe<iframe src="..."></iframe>发送 Cookie不发送
AJAX$.get("...")发送 Cookie不发送
Image<img src="...">发送 Cookie不发送

XSS全称是跨站脚本攻击(Cross Site Scripting)

攻击原理:被植入了不安全的脚本

### XSS攻击类型

基于存储的XSS攻击(也称“持久型XSS”)

原理:把用户输入的数据“存储”在服务器,当用户访问网页的时候,从服务器拿到攻击代码并执行。

例:访问黑客写下的一篇含有恶意 JavaScript 代码的博客文章,黑客把恶意脚本保存到服务端。

特点:很强的稳定性

 <table background="javascript:alert(/xss/)"></table>

基于反射的XSS攻击(也称“非持久型XSS”)

原理:攻击者需要将带有攻击脚本的URL(攻击代码放在url上)发送给目标用户。用户点击访问后,在本地的浏览器解析执行了攻击代码。

特点:非持久型

http://www.***.com?xss=<script>alert(document.cookie)</script>

<script>、<img>、<iframe>、<link>、<video>、<audio>、<base> 都可以注入

基于DOM的XSS攻击

原理:指通过修改页面的 DOM 节点形成的 XSS

特点:非持久型,从效果上来说,也是反射型XSS,单独划分出来,和其它两个类型的差别在于DOM ,XSS代码并不需要服务器解析响应的参与

比如修改dom的xss代码

XSS攻击技巧

  • 钓鱼

    主要用在窃取密码,利用 Javascript 在当前页面“画出”一个伪造的登录框,当用户在登录框中输入用户名与密码后,其密码将被发送至黑客的服务器上。

  • 传播跨站脚本蠕虫

  • 窃取用户cookies资料,从而获得用户隐私信息,或利用用户身份进一步对网站执行操作;

  • 劫持用户会话,进行非法转账、强制发表日志、发送电子邮件等;

  • 强制弹出广告、刷流量等;

  • 恶意操作,删除文章等;

  • 网页挂马

  • Http Header攻击

    url中重定向到地址,比如微信授权登录就有这种操作(跟第三方打通,基本上有这些操作),被重定向的地址中包含script等恶意代码(可以编码或者过滤)

XSS的防御

  • 对于反射型XSS,不要从URL等这些途径获取数据直接展示到页面中

  • Cookie 设置 HttpOnly 和 严格设置Cookie的域。

    防止cookie劫持攻击

  • 在请求头的cookie中添加恶意代码,可能会超过8k,所以限制请求头大小(限制request headers的大小),把超过8k的header链接发给受害者,就会被服务器拒绝访问。解决办法就是检查cookie的大小,限制新cookie的总大小,减少因header过大而产生的拒绝访问攻击

  • 通过限制长度,来限制攻击者能输入的长度

  • 对于三种XSS,输入检查(验证)、过滤、转义

    限制用户的输入格式和规范,比如限制只能为数字和字母的组合,从而让一些基于特殊字符的攻击失效。

    XSS过滤:检查是否包含 JavaScript,<script>等敏感字符,并对其进行过滤。但其对 “<” 和 ">" 等字符的处理,可能会改变用户的数据语义,比如用户输入:2+1 < 5。

    输入检查的逻辑,必须在服务端实现,因为客户端的检查也是狠容易被攻击者绕过,现有的普遍做法是两端都做同样的检查,客户端的检查可以阻挡大部分误操作的正常用户,从而节约服务器资源。

  • 对于三种XSS,输出检查(验证)、过滤、转义

    一般来说,除了富文本的输出外,在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。

    当数据添加到DOM时候,可以对内容进行HtmlEncode或JavaScriptEncode,以预防XSS攻击

    1)安全的编码函数(HtmlEncode编码

    如:&(&amp;),< (&lt;),>(&gt;),"(&quot;)

    2)JavaScript 的编码(JavaScriptEncode)

    使用“\”对特殊字符进行转义,除数字字母之外,小于127的字符编码使用16进制“\xHH”的方式进行编码,大于用unicode(非常严格模式)。在对抗 XSS 是要输出的变量必须在引号内部

    var x = 1;alert(1);      // 执行了 alert
    var y = " 1;alert(1)";   // 安全 
    
    <button onclick='alert("1\x29\x3balert\x282\u54c8\u54c8\x29")'>测试JavaScriptEncode值</button>
    <div>&lt;script&gt;alert(&#x27;1&#x54c8;&#x54c8;&#x27;&nbsp;&#x2F;);&lt;&#x2F;script&gt;</div>
    

    常识:浏览器自带的抗 XSS 的措施有 Firefox 的 CSP、Noscript 扩展等。

CSRF全称是跨站请求伪造(Cross Site Request Forgery)

攻击案例:大家最怕的就是担心转账问题,此处以此来作为案例分析。

网站地址说明:

安全网站WebA: http://secure:3000
攻击网站WebB: http://hacker:4000

api:

http://secure:3000/login 登录接口,用户访问后会自动设置 Cookie 
http://secure:3000/transfer 转账请求接口,通过 to 和 money 参数控制向谁转钱、转多少钱

攻击原理:同一浏览器下,利用用户有效身份,伪造用户请求,实现恶意目的

  1. 安全用户A登录目标网站A,保存了该网站的登录状态
  2. 攻击者B诱导受害者进入第三方网站,带着A的登录信息,向被攻击A网站发送跨站请求
  3. 网站A在不知情情况下,项B返回数据,导致安全问题

CSRF跨域请求get类型

攻击方式:通过 <img> 、 <iframe> 、 <script> 等带 src 属性的标签

比如:点击链接

安全网站登录,带着cookie可以转账,当转账过程中打开攻击页面,攻击网站正好拿到cookie,向安全网站发起恶意请求,引起攻击。

第三方网站中有链接,可以通过 <img> 、 <iframe> 、 <script> 等带 src 属性的标签,这类标签只能发送get请求,而不能发送post请求

我把能想到的标签都写上了,思维穷尽了。。。

比如:

<a href="http://secure:3000/transfer?to=hacker&money=100">点击下载</a>

或者

document.body.addEventListener('click', transfer)  
transfer是创建`<img> 、 <iframe> 、 <script> 、<a>`等标签

设置 httpOnly Cookie 并不能阻止 CSRF 攻击,只是说明获取不到cookie(即使被XSS攻击也获取不到),但是CSRF 攻击的目的并非获取 Cookie,而是利用浏览器会自动携带 Cookie 的机制,从而伪造用户身份,向目标网站发起请求

防御措施

  1. GET 接口只用于查询,不要用于任何写入操作

如果用get更新、删除数据库等操作,很容易被用来CSRF攻击

  1. 设置 Referer 白名单

一般,跨站发送的请求在 header 中会携带 Referer 头部,服务端可以设置一个白名单,拒绝非白名单内的跨站请求即可。

注意:h5 中 a 标签和 area 标签,定义了一个新的 ref = "noreferrer",指定该属性之后,浏览器在请求该标签指定的地址时将不再发送 Referer;从HTTPS 跳转到 HTTP,处于安全考虑,浏览器也不会发送 Referer;这样可以绕过Referer头部,所以这种方式并不能防御所有get形csrf攻击

<a href="http://secure:3000/transfer?to=hacker&money=100" rel="noreferrer">点击下载</a>
  1. 添加 csrfToken

CSRF 攻击之所以能够成功是因为验证信息存在 Cookie 中,并且浏览器自动携带 Cookie,如果在请求参数中把参数加密或者使用一些随机数token,并在服务器端验证该 token,则能够防御 CSRF 攻击。例如:

http://secure:3000/transfer?to=hacker&money=100&csrfToken=xxx

用户登录后,服务端生成 token 并放在 session 中,后面该用户的每个请求都从 session 拿出这个 token,与请求中的 csrfToken 进行比对,若不一致则拒绝请求。

参数加密缺点:加密url每次都改变,url无法被用户收藏;url不好读,对用户不友好

所以更好的方案是保持原参数不变,新增一个参数token,值是随机的,不可预测的。

注:token 要足够随机,不可预测。设置 token 的有效时间。 token 的保密性。

随机数token缺点:仅用于对抗 CSRF 攻击,当网站还存在 XSS 漏洞时,这个方案就无效了。因为在 XSS 攻击下,攻击者完全可以请求页面后,读出页面内容里的 Token 值,然后再构造一个合法的请求,这个过程又被称为 XSRF。

CSRF跨域请求post类型

攻击方式

能发送post请求的方式:输入框、删除按钮、隐藏form表单,提交的时候请求安全网站

攻击案例

<form id="form" method="POST" enctype="application/x-www-form-urlencoded" action="http://secure:3000/transfer" style="display: none">
  <input type="text" name="to" value="hacker" />
  <input type="number" name="money" value="100" />
</form>

攻击者会在第三方网站隐藏一个表单,当用户访问的时候,自动提交该表单。这个隐藏对用户来说是看不到的

当用户被诱导进入 http://hacker:4000 之后,该表单会自动提交,向目标网站发起 POST 请求,如果浏览器自动携带 Cookie,则跨站请求会被成功执行。

在此想到Cookie 的 SameSite 属性,如果设为Lax、strict,post请求不会携带cookie,从而有效阻止csrf攻击

防御措施

  1. csrfToken

在表单中加入一个 hidden 的 csrfToken 值

<input type="hidden" name="csrfToken" value="xxxx">

防御原理就是发送请求都得带上token验证请求合法性,由于 csrfToken 是存储在后端的,攻击者无法猜测。

  1. CORS 白名单 + 自定义 header

现在大部分网页都是 SPA,通过 ajax 发送网络请求,根据浏览器的同源策略,可以在后端设置 CORS 白名单,只让来自指定的源的请求通过,这样就可以阻止大部分跨站攻击,我们还可以添加自定义 header,例如 X-CSRF-TOKEN。

自定义header会引起跨域请求?

  1. JWT 认证

将 token(登录之后服务器生成返给前端的) 放在 Authorization 头部传递给后端做验证。

其实我公司以上多种方式都有引用。

点击劫持(Clickjacking)

原理

点击劫持(Clickjacking),又被称为“UI-覆盖攻击”,是指,攻击者使用多个透明或不透明的图层,当用户想要点击最顶层的页面时,欺骗用户点击其它页面上的按钮或者链接。

劫持手段

覆盖劫持

把iframe框覆盖页面的登录框,诱使用户输入,这样可以获取到账号和密码,比如登录银行的页面

通过利用图片的 style,覆盖掉其他图片,例如覆盖掉网站的submit按钮、logo、文字。此时点击此图片就会链接到其他网站,达到攻击的目的。

拖拽劫持

拖放劫持核心思路:"拖放劫持"的思路是拖拽不受同源策略影响,诱使用户从隐藏的不可见iframe中"拖拽"出攻击者希望得到的数据,然后放到攻击者能控制的另外一个页面中,从而窃取数据。

到处看到“拖拽劫持”,它到底是什么东西,我特意试了,确实可拖拽文字到另一个页面的输入框中、拖拽图片上传到攻击者的网页中,从而窃取数据。

拖放劫持简单实现:

  1. 假设安全网站中重要敏感信息(本地cookie、token、sessionId、password等)放入frame、input、textarea等标签中。

  2. 在终点位置设置一个透明的textarea,用户拖动将网页资源移动到textarea中,监听onchange函数使用inner.html将源码打印。

重要敏感信息(本地cookie、token、sessionId、password等)放入frame、input、textarea等标签中,拖拽的话,就能被其他网站获取。

防御方式

  • 可以限定你的网页不能嵌套在任意网页内
if(window != window.top){
    window.top.location.href = correctURL;
}

如果允许引用同域的框架的话,可以判断域名。

if (top.location.host != window.location.host) {
  top.location.href = window.location.href;
}  

可以使用try...catch...进行错误捕获。如果发生错误,则说明不同域,表示你的页面被盗用了。可能有些浏览器这样写是不会报错,所以需要降级处理。

try{
  top.location.hostname;  //检测是否出错
  //如果没有出错,则降级处理
  if (top.location.hostname != window.location.hostname) { 
    top.location.href =window.location.href;
  }
}
catch(e){
  top.location.href = window.location.href;
}
  • 禁止拖拽:
    οndragstart="return false;" //禁止鼠标拖动,如拖动图片、连接等
    

火狐、chrome浏览器拖动过程流畅没有问题;IE浏览器在拖动时能明显看出是在拖动网页资源(比如能看出是不是图片),所以在拖动的时候观察拖动资源

HTML5引入的安全措施和问题

HTML5引入的安全措施

  • iframe标签的 sandbox属性
描述
''应用以下所有的限制
allow-same-origin允许 iframe 内容被视为与包含文档有相同的来源
allow-top-navigation允许 iframe 内容从包含文档导航(加载)内容
allow-forms允许表单提交。
allow-scripts允许脚本执行
  • a 和 area 标签定义的新属性:noreferrer

指定该属性之后,浏览器在请求该标签指定的地址时将不再发送 Referer;这是为了保护敏感信息和隐私。

  • Canvas的妙用

canvas 标签可以让 Javascript 可以在页面中直接操作图片对象,也可以直接操作像素,构造出图片区域。

攻击:可以通过操作 canvas 中的每个像素点,完成验证码的绘制。

验证码什么格式?仿真出来验证码来攻击

  • postMessage 接收窗口验证domain

HTML5引入的安全问题

<video> 、 <audio>都可以远程加载一段视频

平时我们每天都在写业务,可是质量保证呢

总结注意点

单点登录认证方式:cookie和JWT

cookie认证登录包括:

    1. post类型的CSRF:csrfToken 、CORS白名单
    1. 所有get类型的CSRF

    务必遵遵循以下建议:

      1. 不要在 GET 请求中实现数据写入操作
      1. 在服务端把 Cookie 的 SameSite 属性设为 Lax
      1. 所有表单提交增加 csrfToken 隐藏字段
  • 尽量用post请求,如果用get尽量不要在页面链接中暴露用户隐私信息

  • 对于单页面应用 SPA 来说,更推荐使用 JWT 方式做认证,可防御 CSRF 攻击,便于单点登录。

  • get和post类型的csrf都可以加入白名单,get是通过refer,post是设置cors白名单,get不使用那些躲避同源策略的标签也可以通过设置cors白名单

总之,在开发的时候多想一步怎么处理网站安全问题

www.cnblogs.com/lovesong/p/…

juejin.cn/post/686718…

juejin.cn/post/686957…

juejin.cn/post/684490…

juejin.cn/post/684490… 具体详细看认证区别

www.mchz.com.cn/cn/service/…

www.freebuf.com/sectool/104…

www.cnblogs.com/-qing-/p/10…