彻底理解前端安全面试题(1)—— XSS 攻击,3种XSS攻击详解,建议收藏(含源码)

前言

前端关于网络安全看似高深莫测,其实来来回回就那么点东西,我总结一下就是 3 + 1 = 4,3个用字母描述的【分别是 XSS、CSRF、CORS】 +
一个中间人攻击。当然 CORS 同源策略是为了防止攻击的安全策略,其他的都是网络攻击。除了这 4 个前端相关的面试题,其他的都是一些不常用的小喽啰。

我将会在我的《面试题一网打尽》专栏中先逐一详细介绍,然后再来一篇文章总结,预计一共5篇文章,欢迎大家关注~

本篇文章是前端网络安全相关的第一篇文章,内容就是 XSS 攻击。

一、准备工作

跨站脚本攻击(cross-site scripting),为了和 css 区分所有才叫 XSS【也叫作代码注入攻击】,重点在【脚本】两个字,所以同样都是利用
script 标签,XSS 和后面说的 CSRF 还是有区别的。通过在网站注入恶意脚本,使脚本在用户的浏览器上运行,从而盗取用户的信息或者破环页面的结构。

1.1 拉取仓库

很多知识都需要结合实际的代码来学习,所以本篇文章的基础是需要一个服务端的项目,可以跟着我的这篇文章搭建自己的服务端项目。或者直接克隆我的[仓库代码](https://gitee.com/yangjihong2113/learn-
express “仓库代码”)在这个提交上拉一个新分支,本篇文章所有的代码都是在这个提交基础上进行的。

1.2 新增 xss 文件夹

在项目的根目录增加一个 xss 文件夹,并且在 xss 下面新建 index.html 和 index.js

__dirname 是 Node.js 中的一个特殊变量,表示当前执行脚本所在的目录的绝对路径。它是全局对象 global
的属性之一,可在任何地方使用。具体可以看这个

至于运行的时候为什么加上目录参数,请看这篇文章

1.3 提交代码

二、攻击方式

顾名思义,xss 的攻击方式重点是脚本,就是利用 js 脚本干的一些坏事,主要的攻击方式如下:

  1. 利用脚本获取页面的数据,盗用 cookie、localStorage 等
  2. 破坏页面结构,操作 dom
  3. DOS 攻击,拒绝服务请求,恶意发送请求,占用服务器资源

反正 js 能干的事情都可以利用。

三、攻击类型

xss 的攻击类型分别是 存储型、反射型、DOM 型,下面开始做详细的讲解

3.1 存储型 XSS 攻击

恶意脚本由前端生成、发送并存在目标服务器上,属于服务器端漏洞,重点是:

  1. 这个脚本是前端某个用户写的,好好想想,肯定是这样的,服务端不会无缘无故的多出来数据;
  2. 服务器在接收到前端传的内容后,没有经过检查就存到数据库中
  3. 攻击发生在前端再一次访问存储的数据的时候
3.1.1 具体流程
  1. 一个用户修改一个公共【文件名】为一段脚本,如

  2. 服务器对【文件名】没有过滤(对不合法的文件名没有进行转义等处理),就保存在服务端数据库了

  3. 当其他用户访问这个公共【文件名】的时候就会触发攻击

  4. 脚本中可以获取网站的 cookie,把 cookie 数据传到黑客服务器【或者是利用脚本破坏页面结构等】

  5. 拿到用户 cookie 信息后,就可以利用 cookie 信息在其他机器上登录改用户的账号,并利用用户账号进行一些恶意操作

3.1.2 代码模拟

我模拟的是【破坏页面结构】的 XSS 攻击,我们再在 XSS 文件夹下新建两个文件

  1. data.txt 用来存数据来模拟数据库
  2. index2.html 用来展示第二个页面,用于访问数据库中的数据

现在我们总共有四个文件了。

系统功能描述:

  1. 一个公开可访问的书籍列表,一个管理员用户 1 可以在页面 1 (index.html) 修改书籍的名称;
  2. 一个普通用户 2, 可以在页面 2(index2.html)访问书籍列表(也就是书籍名)
(1)index.html 的代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=`, initial-scale=1.0" />
    <title>Document</title>
    <style>
      h1,h2 {
        margin: 0;
      }
      .box {
        display: inline-flex;
        flex-direction: column;
      }
      .book-name {
        margin: 10px 0;
        width: 400px;
        height:100px
      }
      .button {
        width: 100px;
        height: 38px;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <h1>【首页,用户1】</h1>
      <h1>设置一个公开的书籍的名称</h1>
      <div>这个书籍是所用用户都可以访问的</div>
      <div>修改书名:</div>
      <textarea class="book-name" id="name" ></textarea>
      <button class="button" onclick="saveName()">保存</button>
    </div>
    <script>
      function saveName() {
        const name = document.getElementById('name').value;
        if (name) {
          fetch('saveName', {
            method: 'post',
            headers: {
              'content-type': 'application/json'
            },
            body: JSON.stringify({
              name: name
            }),
          }).then(() => {
            console.log('保存成功 姓名:', name)
          })
        }
      }
    </script>
  </body>
</html>
(2)index2.html 的代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=`, initial-scale=1.0" />
    <title>Document</title>
    <style>
      h1,
      h2 {
        margin: 0;
      }
      .box {
        display: inline-flex;
        flex-direction: column;
      }
      .book-name {
        margin: 10px 0;
        width: 400px;
        height: 100px;
      }
      .button {
        width: 100px;
        height: 38px;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <h1>【另一个页面,用户2】</h1>
      <h1>书籍列表页面</h1>
      <div>访问公开的书籍</div>
      <div>书名</div>
      <div id="name"></div>
    </div>
    <script>
      // 初始化的时候就获取数据
      fetch('/getName')
        .then((res) => {
          return res.json();
        })
        .then((data) => {
          const name = data.name;
          const ele = document.getElementById('name');
          // 设置书名
          ele.innerHTML = name;
        });
    </script>
  </body>
</html>
(3)index.js 的代码
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser')
const fs = require('fs')

const app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// 首页 -> 用户1 保存数据
app.get('/', function (req, res) {
  res.sendFile(path.join(__dirname, '/index.html'));
});

// 首页 -> 保存书名
app.post('/saveName', function (req, res) {
  const { name } = req.body
  fs.writeFileSync(path.resolve(__dirname, 'data.txt'), name)
  res.send('保存成功');
});


// 新建另一个页面 
app.get('/index2', function (req, res) {
  res.sendFile(path.join(__dirname, '/index2.html'));
});
// 另一个页面 -> 用户2 获取数据
app.get('/getName', function (req, res) {
  // 从 data.txt 中取出存储的书名
  const name = fs.readFileSync(path.resolve(__dirname, 'data.txt')).toString()
  console.log(name)
  res.send(
    JSON.stringify({
      name: name,
    })
  );
});

app.listen(3000);

注意 xss/index.js 中关于使用 express 写服务端代码,有两个知识点

(1)post 请求需要使用 body-parser 解析 body 的 json 数据

(2)写入/读取 data.txt 是用node 的核心模块 fs,方法 fs.writeFileSync / s.readFileSync

(3)fs.readFileSync 的结果是 buffer 需要转成字符串 toString()

data.txt 里面不必输入任何内容,我们会通过代码进行写入。

(4)运行代码
npm run dev xss

访问 localhost:3000,输入一段恶意的代码作为书名,点击保存

点击保存之后,输入的内容会保存在 data.txt 中

再打开 localhost:3000/index2.html,这样一个存储型的、对于 dom 结构的破环的 XSS 攻击就完成了。

这里面有一个知识点就是我们在 index.html 中保存的恶意脚本是

<img src='invalid-image' onerror='alert("我是秦始皇,加v给我100万")'>

我们用了一个 img 标签,然后使用 onerror 事件里面写一些脚本,而没有这么写,为啥呢?

<script>alert(1)</script>

这是因为我们在 index2.html 里面使用 innerHTML 将数据渲染在页面上,使用 innerHTML 直接渲染 script
字符串,脚本是不会被执行的。 而事件处理器,如 img 的 onerror 事件却可以触发。

(5)提交代码

3.2 反射型 XSS 攻击

恶意脚本在前端访问的 URL 中,要用户主动点击 URL,服务器解析 URL, 并返回恶意脚本,属于服务端漏洞,重点是:

  1. 恶意脚本不是前端用户手动写的,和存储型有区别
  2. 恶意脚本在 URL 上,需要用户手动点击才能触发攻击
  3. 服务器收到访问 URL 的请求时,解析 URL 得到恶意脚本,然后返回给客户端
  4. 服务器不会存储恶意脚本,只会返回(反射)它
3.2.1 具体流程
  1. 黑客诱导用户访问有恶意代码的 URL ,如 https://danger.com?xss= }
  2. 浏览器的恶意代码的 URL 页面有渲染 XSS 值的逻辑
  3. 此时浏览器弹出警告框
  4. 一个反射型 XSS 攻击就完成了

黑客经常会通过 qq群或者邮件等渠道诱导用户去点击这些恶意链接。

看到没?没事别惦记乱七八糟的连接,谨防电信诈骗!

3.2.2 代码模拟
(1)新建 xss/ index3.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>反射型 xss 攻击</div>
    <h1><a href="http://localhost:3000/reflect?xss=<script>alert('哈哈,你上当了')</script>"> 点击收款100万 </a></h1>
    
</body>
</html>
(2)修改 index.js

注意看这个服务端代码关于 reflect 方法的 get 请求,他就是简单的把参数上的获取并返回,浏览器就能运行这段代码。

(3)运行代码
npm run dev xss

访问 localhost:3000/index3

跳转之后发生了攻击![](https://img-
blog.csdnimg.cn/direct/6edc3d4aced644a8a1acfdf56a461bf2.png)

(4)提交代码

3.3 DOM 型 XSS 攻击

攻击者通过操纵 DOM 来触发攻击,是前端漏洞,不牵扯到服务器。重点是:

  1. 整个过程服务器不参与
  2. 脚本的具体来源还是利用网页中用户交互的部分, URL 参数、表单输入、cookie 等
  3. 多发生在使用 innerHTML 的场景
3.3.1 具体流程
  1. 从 URL 中取出恶意代码/或者从用户输入的表单/ cookie 中
  2. 把恶意代码使用 innerHTML 插入页面,改变 dom 结构

尤其是在使用 innerHTML 的时候会出现这种问题,可以使用插件如 xss-filters 避免这一类问题,这个插件的原理是将某些字符进行转义,将 <
转成 &lt 等

3.3.2 代码模拟
(1)新增 index4.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=<device-width>, initial-scale=1.0">
    <title>Document</title>
    <style>
        textarea {
            width: 400px;
            height: 50px;
        }
    </style>
</head>
<body>
    <h1>dom 型 xss 攻击</h1>
    <div>输入</div>
    <textarea id="input"></textarea>
    <button onclick="save()">保存</button>
    <hr>
    <div>输出</div>
    <div id="output"></div>
    <script>
        function save() {
            const text = document.getElementById('input').value
            if (text) {
                const output = document.getElementById('output')
                output.innerHTML = text
            }
        }
    </script>
</body>
</html>
(2)修改 xss/index.js

(3)运行代码
npm run dev xss


点击保存按钮之后,就出现攻击了。

注意,这全程没有服务端的参与,但是我们还是修改 index.js 。但是其实 index.js 里面的代码就是为了起一个服务运行 index4.html
而已, 应该很好理解吧。

(4)提交代码

好了至此三种类型的 XSS
攻击我们都用代码的形式实现。一点都不难吧,光说不练假把式,如果只看理论知识,肯定云里雾里,你跟着一起写一遍代码就理解了,不用背诵就记下来了。

3.4 XSS 攻击防御方法

  1. 不使用服务端渲染,前两种是服务端的安全漏洞,服务器不能信任前端输入的任何东西,服务端要对输入脚本进行过滤或者转码。

总结

xss 攻击的三种类型都用代码模拟了,我的仓库地址如下,欢迎查看

yangjihong2113/learn-express

内容较多,难免疏漏,如有问题,欢迎指正。

这是一系列的文章,关于网络安全的内容还有 CSRF、CORS 和中间人攻击的内容没有总结,持续更新中,欢迎关注。

学习网络安全技术的方法无非三种:

第一种是报网络安全专业,现在叫网络空间安全专业,主要专业课程:程序设计、计算机组成原理原理、数据结构、操作系统原理、数据库系统、 计算机网络、人工智能、自然语言处理、社会计算、网络安全法律法规、网络安全、内容安全、数字取证、机器学习,多媒体技术,信息检索、舆情分析等。

第二种是自学,就是在网上找资源、找教程,或者是想办法认识一-些大佬,抱紧大腿,不过这种方法很耗时间,而且学习没有规划,可能很长一段时间感觉自己没有进步,容易劝退。

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

第三种就是去找培训。

image.png

接下来,我会教你零基础入门快速入门上手网络安全。

网络安全入门到底是先学编程还是先学计算机基础?这是一个争议比较大的问题,有的人会建议先学编程,而有的人会建议先学计算机基础,其实这都是要学的。而且这些对学习网络安全来说非常重要。但是对于完全零基础的人来说又或者急于转行的人来说,学习编程或者计算机基础对他们来说都有一定的难度,并且花费时间太长。

第一阶段:基础准备 4周~6周

这个阶段是所有准备进入安全行业必学的部分,俗话说:基础不劳,地动山摇
image.png

第二阶段:web渗透

学习基础 时间:1周 ~ 2周:

① 了解基本概念:(SQL注入、XSS、上传、CSRF、一句话木马、等)为之后的WEB渗透测试打下基础。
② 查看一些论坛的一些Web渗透,学一学案例的思路,每一个站点都不一样,所以思路是主要的。
③ 学会提问的艺术,如果遇到不懂得要善于提问。
image.png

配置渗透环境 时间:3周 ~ 4周:

① 了解渗透测试常用的工具,例如(AWVS、SQLMAP、NMAP、BURP、中国菜刀等)。
② 下载这些工具无后门版本并且安装到计算机上。
③ 了解这些工具的使用场景,懂得基本的使用,推荐在Google上查找。

渗透实战操作 时间:约6周:

① 在网上搜索渗透实战案例,深入了解SQL注入、文件上传、解析漏洞等在实战中的使用。
② 自己搭建漏洞环境测试,推荐DWVA,SQLi-labs,Upload-labs,bWAPP。
③ 懂得渗透测试的阶段,每一个阶段需要做那些动作:例如PTES渗透测试执行标准。
④ 深入研究手工SQL注入,寻找绕过waf的方法,制作自己的脚本。
⑤ 研究文件上传的原理,如何进行截断、双重后缀欺骗(IIS、PHP)、解析漏洞利用(IIS、Nignix、Apache)等,参照:上传攻击框架。
⑥ 了解XSS形成原理和种类,在DWVA中进行实践,使用一个含有XSS漏洞的cms,安装安全狗等进行测试。
⑦ 了解一句话木马,并尝试编写过狗一句话。
⑧ 研究在Windows和Linux下的提升权限,Google关键词:提权
image.png
以上就是入门阶段

第三阶段:进阶

已经入门并且找到工作之后又该怎么进阶?详情看下图
image.png

给新手小白的入门建议:
新手入门学习最好还是从视频入手进行学习,视频的浅显易懂相比起晦涩的文字而言更容易吸收,这里我给大家准备了一套网络安全从入门到精通的视频学习资料包免费领取哦!

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

  • 20
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值