本教程包括:
- 手动测试XSS攻击
- 修复XSS漏洞
- 自动化测试过程
安全问题在网络上是一场无休止的战斗。你可以在短短几分钟内建立一个服务器,而下一分钟,已经有人在试图入侵它了。这些攻击可以使用恶意的机器人自动进行,也可以手动发起。网站可以成为恶意用户的目标,试图破坏你的网络存在或数据。跨站脚本(XSS)只是你的网站可能受到的攻击的一种类型。
在XSS攻击中,用户利用你的应用程序中的数据输入点的漏洞。该攻击将脚本代码注入表单字段或地址栏,并迫使其运行恶意脚本。这些攻击可能导致敏感的cookie信息被泄露,或者在你的网页上运行脚本,将外来元素注入你的页面中。
在本教程中,你将学习并演示如何使用浏览器安全测试来防止你的网页受到这种XSS攻击。
前提条件
要跟上本教程的学习,需要具备一些条件:
克隆和运行示例应用程序
首先,你需要克隆将被测试的XSS攻击的演示应用程序。运行下面的命令来获取你系统上的代码。
git clone --single-branch --branch base-project https://github.com/coderonfleek/xss-attacks.git prevent-xss-attacks
一旦应用程序被克隆,进入项目的根目录(cd prevent-xss-attacks),通过运行以下命令安装依赖项。
npm install
在完全安装了依赖项后,运行该应用程序。
node server
这将启动应用服务器http://localhost:5000 。在你的浏览器上导航到这个URL。
这个页面由一个表格和右边的信息显示栏组成。当你填写表格并按下回车键时,你的电子邮件会出现在Details 。
手动测试XSS攻击
当表单被提交后,信息被提交到服务器上的一个端点(/sendinfo)。这个端点将电子邮件以json 响应体的形式发送回来,然后被页面接收并显示在细节部分。显示电子邮件表明,在表格中输入的数据已经进入后台,并被返回到页面。一个恶意的用户可以很容易地利用这个过程,在电子邮件字段中输入损坏的数据。例如,不在电子邮件字段中输入有效的电子邮件,而是输入文件字段的HTML标记。
<input type="file" />
填写密码字段并点击Submit 。细节部分看起来大不相同。
已经输入的数据导致了一个新的HTML元素,即文件输入字段,显示在页面上。这肯定是不需要的,如果它的位置好,可能会很危险。攻击者可以利用这种策略在你的表单中嵌入恶意数据(或脚本)。在你的表单中放置一个隐藏的输入字段,可能会导致该表单将有害的数据连同其有效载荷一起提交给你的服务器。这可能会导致严重的损害和破坏数据的完整性。你会希望在别人利用之前抓住这样的漏洞。一个有效的方法是通过浏览器测试。
安装 Jest 和 Puppeteer
浏览器测试可以让你像普通用户一样与网页进行交互,从而对其进行测试。这允许你测试不同的数据输入场景,以发现并修复黑客可能试图探索的任何漏洞。
要建立自动化的浏览器测试,你需要两个包:
通过运行此命令安装这些软件包。
npm install --save-dev jest puppeteer
安装了这些包后,现在可以开始为浏览器编写测试了。
为浏览器添加XSS测试
在这一节中,你将编写一个测试套件来测试你的浏览器,以检测电子邮件输入漏洞。如果发现该漏洞,测试将失败。一个失败的XXS测试表明,你的电子邮件字段中存在一个漏洞,需要加以解决以避免攻击。
你将编写的测试将执行你在上一节中手动执行的相同攻击。创建测试文件login.test.js ,并输入此代码。
const puppeteer = require("puppeteer");
test("Check for XSS attack on email field", async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.goto("http://localhost:5000");
await page.type("#userEmail", '<input type="file" />');
await page.type("#userPassword", "password");
await page.click("#submitButton");
let emailContainer = await page.$("#infoDisplay");
let value = await emailContainer.evaluate((el) => el.textContent);
expect(value.length).toBeGreaterThan(0);
} finally {
await browser.close();
}
}, 120000);
在上面的测试文件中,Check for XSS attack on email field 测试案例使用 Puppeteer 启动一个浏览器实例,然后在 URLhttp://localhost:5000 加载应用程序。一旦应用程序运行,电子邮件字段就会使用输入文件标记来填写。密码字段也被填写。点击Submit 按钮,一旦提交表单,显示部分将被检查出一个长度大于零的字符串(非文本HTML元素将返回一个长度为零的字符串)。一旦测试运行完毕,关闭浏览器。
现在你已经有了XSS测试,可以检查攻击了。为了完成测试设置,在package.json 文件中添加一个test 脚本。
...
"scripts" : {
"test" : "jest"
}
确保你的应用程序目前正在使用node server.js ,并运行测试文件。
npm run test
这个测试将失败,因为正如我们已经知道的,这个漏洞确实存在。下面是CLI输出显示的内容。
FAIL ./login.test.js (5.475 s)
✕ Check for XSS attack on email field (2096 ms)
● Check for XSS attack on email field
expect(received).toBeGreaterThan(expected)
Expected: > 0
Received: 0
23 | let value = await emailContainer.evaluate(el => el.textContent);
24 |
> 25 | expect(value.length).toBeGreaterThan(0);
| ^
26 |
27 | }, 120000);
28 |
at Object.<anonymous> (login.test.js:25:24)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 14.582 s
Ran all test suites.
修复XSS漏洞
修复XSS漏洞的一个有效方法是确保在对数据进行任何形式的处理之前,对输入应用程序的数据进行验证。数据验证可以在应用程序的客户和服务器两端进行。对于这个应用程序,你将在服务器端验证收到的电子邮件,以确保只有安全的文本被返回到客户端。
找到server.js 。/sendinfo 端点显示,电子邮件未经验证就返回给了客户端。
app.post("/sendinfo", (req, res) => {
const email = req.body.email;
res.send({ email });
});
现在,用这段代码替换这个端点。
app.post("/sendinfo", (req, res) => {
let email = req.body.email;
if (!validEmail(email)) {
email = "Enter a Valid Email e.g test@company.com";
}
res.send({ email });
});
function validEmail(mail) {
return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(
mail
);
}
在新的代码中,一个validEmail 函数接收一个字符串,并根据该字符串是否为有效的电子邮件返回一个布尔值。然后,这个函数被用于/sendinfo ,以验证客户端发送的电子邮件。如果电子邮件是有效的,它将被返回给客户。如果无效,则发送一条消息,提示用户输入一个有效的电子邮件。
改变了server.js 代码后,再次重启应用程序,杀死它(Ctrl + C),用node server.js 重新启动它。你可以先通过刷新浏览器和重试攻击来进行手动测试。这将显示一个验证信息而不是输入字段。
现在用npm run test 命令运行测试套件。如控制台输出所示,测试将通过。
PASS ./login.test.js (6.953 s)
✓ Check for XSS attack on email field (3887 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 16.258 s
Ran all test suites.
安全测试过程的自动化
这个练习的主要目的是使浏览器测试过程自动化,以便在XSS漏洞进入你的生产代码之前就能被发现。
要开始这个过程,请浏览你的项目的根目录,并创建一个新的文件夹,名为.circleci 。接下来,在其中创建一个新的config.yml 文件。打开新创建的文件,使用以下内容。
version: 2.1
orbs:
node: circleci/node@5.0.2
jobs:
build:
working_directory: ~/repo
docker:
- image: cimg/node:18.2.0-browsers
steps:
- checkout
- node/install-packages:
cache-path: ~/project/node_modules
override-ci-command: npm install
- run:
name: Run the application
command: node server.js
background: true
- run:
name: Run tests
command: npm run test
该脚本拉入Node.js Orb来安装Node及其包管理器。然后,它拉入所需的图像,安装和缓存依赖物。为了确保浏览器测试的运行,该应用程序在后台进程中启动。一旦应用程序启动并运行,test 脚本将被运行以测试它。
提交项目的所有修改,并推送到你的远程GitHub仓库。
接下来,到CircleCI仪表板上的Projects 页面添加项目。
点击Set Up Project,开始设置该项目。CircleCI将检测您项目中的配置文件。请随意查看并确保您的配置文件所在的分支被选中。
现在,点击Set Up Project继续。这将自动触发构建管道并成功构建。
点击构建,查看测试细节。

总结
在你的构建过程中建立安全检查,可以为你的代码增加很多价值。仅仅是代码可以运行并且没有错误是不够的;你还必须确保它不能被破坏。像这样一个安全驱动的开发过程可以扩展到你的代码中涉及用户交互的其他部分,以防止恶意用户利用你代码中的漏洞。鼓励你的团队成员也使用这些步骤来防止他们代码中的XSS攻击。
编码愉快!