# 跨站脚本攻击(XSS)

## XSS跨站脚本攻击

跨站脚本攻击通常是通过非法注入HTML代码或JavaScript代码来操控用户浏览器的。

跨站脚本攻击主要分三种

* DOM型：DOM可以通过操作JavaScript脚本动态的改变文档结构、样式和行为，它不需要服务器的参与，是属于前端的安全漏洞。
* 反射性（又称非持久性）：攻击者将恶意代码注入到URL中，然后提交到服务器，服务器解析后将这段恶意代码返回给浏览器，然后浏览器解析。
* 存储性（持久性）：攻击者将恶意代码发送给服务器，服务器保存到数据库中，当浏览器请求此数据时就会被攻击。

我们讨论一下上面三种类型的XSS攻击场景。

### DOM型。

DOM型是纯粹的前端漏洞，一定不能将用户输入的内容直接通过`v-html`、`outnerHTML`或`innerHTML`渲染，否则很容易受到DOM型XSS攻击。

请看下面这种代码：

```html
<body>
    <h1 id="h1"></h1><input id="input" type="text" />
</body>
<script>
    const input = document.querySelector("#input");
    const h1 = document.querySelector("#h1");

    input.addEventListener("keydown", (e) => {
        if (e.code === "Enter") {
            console.log(e.target);
            h1.innerHTML = e.target.value;
        }
    });
</script>
```

入侵者可以很容易通过输入框入侵，通过`<script>`注入恶意代码。

![https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/20211224000642.png](https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/20211224000642.png)

处理这类攻击一般可以从这几个方面入手：

1. 尽量避免直接使用`v-html`、`innerHTML`和`outerHTML`，特别是要避免将用户的输入直接渲染，最好是使用vue的模板语法或v-text，如果要使用v-html，必须要确保内容的来源是可信。
2. 对特殊字符进行转义，例如`<`、`>`等。

我们采用转义的方法看一下效果

```html
// lt和gt为了避免被转义加上了\，实际代码不用加\
h1.innerHTML = e.target.value.replace(/\</g, "\&\l\t").replace(/\>/g, "\&\g\t");
```

能够成功防御DOM型XSS攻击。

![https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/20211224002034.png](https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/20211224002034.png)

### 反射型

攻击者将恶意代码注入到web服务器中，服务器将这段恶意代码返回给浏览器，由于浏览器相信服务器是“可信任的”，因此会执行脚本。

我们常常利用URL实现页面传参或者是get方法的HTTP请求，而攻击者很容易利用浏览器的地址栏来实现入侵。

```javascript
const h1 = document.querySelector("#h1");
const param = decodeURIComponent(window.location.search.substring(1).split("=")[1]);
const scr = document.createElement("script");
scr.src = `http://example.com?param=${param}&callback=fn`;
document.body.appendChild(scr);

function fn(data) {
    h1.innerHTML = data;
}
```

以上代码通过URL来进行页面传参，先获取URL中的参数，并将其传给后端，利用JSONP来实现跨域，即通过`script`标签来加载后端数据，后端将参数处理后传回。

```
// 后端传回fn("<p style='color: red'>" + param + "</p>")
```

打开浏览器在后面加上参数

```
?param=你好
```

![https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/20211224124925.png](https://raw.githubusercontent.com/const-love-365-10000/cloudImg/master/img/20211224124925.png)

但是如果攻击者传入HTML代码，前端和后端都不做检查处理的话就会直接被入侵。

```html
// 地址栏加上参数?tag=</p> <i>入侵</i>
<p>// 甚至可以加入script标签注入恶意代码</p>
<script src="恶意代码网址"></script>
<p>// 这时HTML变为了
<h1 id="h1">
    <p style="color: red"></p>
    <script src="恶意代码网址"></script>
    <p></p>
</h1>
```

通过`<p>`和`<\p>`来闭合原本的标签，然后注入恶意代码。

防御反射型XSS攻击的方式和防止DOM型类似，可以通过验证参数格式，避免使用`innerHTML`和`v-html`和对关键字符转义等方式防御此类攻击。

### 存储型

存储型是危害最大的一种XSS攻击方式，攻击者将恶意代码发送给服务器，并长久地保存在服务器中，当浏览器请求此数据时就会被攻击。一旦攻击者成功将恶意代码注入到服务器中，那么任何加载这段恶意代码的用户都会受到攻击。

试想一下，在一个说说社区，一个用户将恶意代码作为说说发布，如果前端和后端都没做任何防御措施，后端会将其当做普通说说存储到数据库中，而所有用户一旦打开说说社区加载到了这条说说，那么就有可能被入侵。

存储型和反射型类似，但是不同的是存储型XSS攻击会将恶意代码存入到数据库中，实现长久攻击，因此存储型XSS攻击也被称为长期型XSS攻击。

防御的手段有

1. 进行数据验证，避免输入非法字符，严格规定输入数据的格式，从根本上阻止恶意代码被注入到数据库中，后端也应该做此限制，因为攻击者可能会绕开前端直接发送http请求。
2. 转义特殊字符，原理同上。

### 总结

XSS攻击本质上是通过注入非法的HTML代码来实现入侵，因此只要针对它的攻击特点做出针对性的防御措施，就能抵御攻击。

总结针对XSS攻击最有效的防御手段有

* 尽量避免直接使用`v-html`、`innerHTML`和`outerHTML`，特别是要避免将用户的输入直接渲染，最好是使用vue的模板语法或v-text，如果要使用v-html，必须要确保内容的来源是可信的，并使用dompurify这类工具进行处理。
* 对用户输入的表单格式进行验证，严格限制用户输入的格式，比如注册时手机号必需要是11位的纯数字。不仅前端需要验证，后端也需要验证。
* 对特殊字符转义

常用的转义字符

* `&lt`
* `&gt`
* `&quot`
* `&#39`
* `&amp;`
* `&#x2F`

除此之外还有

1. 使用在响应头`Set-Cookie`加上`httpOnly`,这样可以避免js访问此cookie，这并不能阻止XSS，但是可以减少损失。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://1425816423.gitbook.io/my-knowledge-base/qian-duan-an-quan-yu-xing-neng-you-hua/qian-duan-an-quan/kua-zhan-jiao-ben-gong-ji-xss.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
