XSS ..

时间:2025-05-08 07:21:55

Web安全中的XSS攻击详细教学,Xss-Labs靶场通关全教程(建议收藏) - 白小雨 - 博客园跨站脚本攻击(XSS)主要是攻击者通过注入恶意脚本到网页中,当用户访问该页面时,恶意脚本会在用户的浏览器中执行,从而可能导致数据被篡改、用户信息泄露等问题。为了防止 XSS 攻击导致的数据篡改,可以从输入验证与过滤、输出编码、设置 CSP、HttpOnly 等方面着手,以下是详细介绍:

输入验证与过滤

  • 原理:在数据进入应用程序之前,对用户输入进行严格的验证和过滤,只允许合法的字符和格式通过。
  • 示例:在使用 JavaScript 编写表单验证时,可使用正则表达式来验证用户输入是否符合预期。
function validateInput(input) {
    // 只允许字母和数字
    const regex = /^[a-zA-Z0-9]+$/;
    return regex.test(input);
}

const userInput = document.getElementById('userInput').value;
if (validateInput(userInput)) {
    // 处理合法输入
} else {
    // 提示用户输入不合法
}

输出编码

  • 原理:在将数据输出到 HTML、JavaScript、CSS 等环境中时,对特殊字符进行编码,将其转换为 HTML 实体或其他安全的表示形式,防止恶意脚本的注入。
  • 示例:在 Node.js 中使用 he 库对输出进行 HTML 编码。
const he = require('he');
const userInput = '<script>alert("XSS")</script>';
const encodedInput = he.encode(userInput);
// 输出编码后的内容,如 &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;
console.log(encodedInput); 

设置内容安全策略(CSP)

  • 原理:通过设置 HTTP 头信息,指定页面可以加载哪些资源(如脚本、样式表、图片等),从而限制恶意脚本的加载和执行。
  • 示例:在 Node.js 的 Express 框架中设置 CSP 头信息。
const express = require('express');
const app = express();

app.use((req, res, next) => {
    res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self'");
    next();
});

app.get('/', (req, res) => {
    res.send('Hello, World!');
});

const port = 3000;
app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

使用 HttpOnly 属性

  • 原理:对于存储敏感信息的 Cookie,设置 HttpOnly 属性,这样 JavaScript 脚本就无法访问这些 Cookie,从而防止攻击者通过 XSS 攻击窃取或篡改 Cookie 中的数据。
  • 示例:在 Node.js 的 Express 框架中设置带有 HttpOnly 属性的 Cookie。
const express = require('express');
const app = express();

app.get('/', (req, res) => {
    res.cookie('session_id', '123456', { httpOnly: true });
    res.send('Cookie has been set');
});

const port = 3000;
app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

避免内联事件和内联脚本

  • 原理:内联事件(如 onclickonload)和内联脚本容易受到 XSS 攻击,应尽量避免使用。可以通过分离 HTML、CSS 和 JavaScript 代码,使用事件委托等方式来处理事件。
  • 示例:不推荐的内联事件写法:
<button onclick="alert('Hello')">点击我</button>

推荐的事件委托写法:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
</head>

<body>
    <button id="myButton">点击我</button>
    <script>
        const button = document.getElementById('myButton');
        button.addEventListener('click', () => {
            alert('Hello');
        });
    </script>
</body>

</html>

定期更新和修复漏洞

  • 原理:及时更新使用的框架、库和服务器软件,修复已知的安全漏洞,减少被攻击的风险。同时,对应用程序进行定期的安全审计和漏洞扫描,及时发现和处理潜在的安全问题。

在处理潜在的不信任输入时,直接使用 innerHTML 是非常危险的,因为它可以导致跨站脚本攻击(XSS)。当您将不受信任的数据插入到网页中时,如果使用了 innerHTML,那么这段数据可能会包含恶意脚本,这些脚本会在用户的浏览器上执行。

例如,如果你直接从用户输入获取数据并将其赋值给 innerHTML,如下所示:

const userInput = "<img src='x' onerror='alert(\"XSS\")'>";
someElement.innerHTML = userInput;

在这种情况下,用户的输入包含了恶意脚本,它会创建一个带有错误图片链接的 <img> 标签,并且当加载图像失败时触发 onerror 事件,从而执行 JavaScript 警告框。这种行为就是 XSS 攻击的一个简单例子。

为了避免这种情况,你应该尽量避免直接使用 innerHTML 来插入不信任的内容。如果必须使用,应该确保对内容进行适当的转义或清理,以去除任何可能的脚本元素或其他危险代码。更安全的选择是使用文本显示方法如 textContent 或者 innerText 来显示不信任的内容,因为它们不会解析 HTML,因此不会执行其中的脚本。

另外,对于一些特定场景,比如需要允许某些格式化但不允许脚本执行,可以考虑使用库如 DOMPurify 对输入进行清理,这样可以安全地移除潜在的有害部分但仍保留一定的富文本格式。例如:

import DOMPurify from 'dompurify';

const cleanUserInput = DOMPurify.sanitize(userInput);
someElement.innerHTML = cleanUserInput;

总之,在处理用户输入时务必小心谨慎,尤其是在将其插入到页面中时,应采取一切必要措施防止 XSS 攻击。

1. 输入验证

使用正则表达式对用户输入进行验证,只允许特定字符通过,避免恶意脚本注入。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>输入验证防止 XSS</title>
</head>

<body>
    <input type="text" id="userInput" placeholder="请输入内容">
    <button id="submitButton">提交</button>
    <div id="output"></div>

    <script>
        function validateInput(input) {
            // 只允许字母、数字、空格和常见标点符号
            const regex = /^[a-zA-Z0-9\s.,!?]+$/;
            return regex.test(input);
        }

        const inputElement = document.getElementById('userInput');
        const submitButton = document.getElementById('submitButton');
        const outputElement = document.getElementById('output');

        submitButton.addEventListener('click', function () {
            const userInput = inputElement.value;
            if (validateInput(userInput)) {
                // 处理合法输入
                outputElement.textContent = `你输入的内容是: ${userInput}`;
            } else {
                outputElement.textContent = '输入包含非法字符,请重新输入。';
            }
        });
    </script>
</body>

</html>

在这个示例中,validateInput 函数使用正则表达式对用户输入进行验证,确保输入只包含字母、数字、空格和常见标点符号。如果输入合法,将其显示在页面上;否则,给出错误提示。

2. 输出编码

将用户输入的内容进行 HTML 实体编码,避免浏览器将其解析为 HTML 标签或脚本。可以使用 DOMPurify 库来实现。首先需要引入 DOMPurify 库,你可以通过 CDN 引入:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>输出编码防止 XSS</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.3.11/purify.min.js"></script>
</head>

<body>
    <input type="text" id="userInput" placeholder="请输入内容">
    <button id="submitButton">提交</button>
    <div id="output"></div>

    <script>
        const inputElement = document.getElementById('userInput');
        const submitButton = document.getElementById('submitButton');
        const outputElement = document.getElementById('output');

        submitButton.addEventListener('click', function () {
            const userInput = inputElement.value;
            // 使用 DOMPurify 进行净化
            const clean = DOMPurify.sanitize(userInput);
            outputElement.innerHTML = clean;
        });
    </script>
</body>

</html>

在这个示例中,当用户点击提交按钮时,使用 DOMPurify.sanitize 方法对用户输入进行净化,将其中的恶意脚本等危险内容过滤掉,然后将净化后的内容插入到页面中。

3. 避免使用内联事件和内联脚本

内联事件(如 onclickonload)和内联脚本容易受到 XSS 攻击,应尽量避免使用。可以通过事件委托等方式来处理事件。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>避免内联事件防止 XSS</title>
</head>

<body>
    <button id="myButton">点击我</button>
    <div id="output"></div>

    <script>
        const button = document.getElementById('myButton');
        const outputElement = document.getElementById('output');

        button.addEventListener('click', function () {
            outputElement.textContent = '按钮被点击了';
        });
    </script>
</body>

</html>

在这个示例中,使用 addEventListener 方法为按钮添加点击事件,而不是使用内联的 onclick 事件,这样可以避免潜在的 XSS 风险。

4. 使用 textContent 而不是 innerHTML

当需要向页面插入文本内容时,使用 textContent 而不是 innerHTML,因为 textContent 会将内容作为纯文本处理,不会解析其中的 HTML 标签。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>使用 textContent 防止 XSS</title>
</head>

<body>
    <input type="text" id="userInput" placeholder="请输入内容">
    <button id="submitButton">提交</button>
    <div id="output"></div>

    <script>
        const inputElement = document.getElementById('userInput');
        const submitButton = document.getElementById('submitButton');
        const outputElement = document.getElementById('output');

        submitButton.addEventListener('click', function () {
            const userInput = inputElement.value;
            // 使用 textContent 插入内容
            outputElement.textContent = userInput;
        });
    </script>
</body>

</html>

在这个示例中,使用 textContent 将用户输入的内容插入到页面中,确保内容以纯文本形式显示,避免 HTML 标签和脚本的执行。