C# XSS攻击验证器
·
public class GlobalXssValidationHandler : DelegatingHandler
{
/// <summary>
/// 重写 SendAsync 方法,拦截所有 HTTP 请求
/// </summary>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
bool XssValidationEnabled = Convert.ToBoolean(Config.XssValidationEnabled);
if (XssValidationEnabled)
{
// 步骤1:捕获并读取请求体,转换为字符串(保留原始流,不影响后续处理)
string requestBody = string.Empty;
// 新增判断:如果是文件上传请求(multipart/form-data),则不执行XSS校验
bool isFileUpload = request.Content != null && request.Content.Headers.ContentType != null
&& request.Content.Headers.ContentType.MediaType.Equals("multipart/form-data", StringComparison.OrdinalIgnoreCase);
if (!isFileUpload && request.Content != null && request.Content.Headers.ContentLength > 0)
{
var contentStream = await request.Content.ReadAsStreamAsync();
contentStream.Position = 0; // 重置流指针到起始位置
using (var streamReader = new StreamReader(contentStream, Encoding.UTF8, true, 1024, true))
{
requestBody = await streamReader.ReadToEndAsync();
}
contentStream.Position = 0; // 重置流指针,供后续 Controller 读取
// 步骤2:直接调用你已有的 XSS 校验方法(核心集成点)
// 假设你的校验方法名为:MyExistingXssCheck,返回 bool(true表示安全,false表示可能包含XSS攻击)
bool hasXss = SafeXssValidator.IsSafeInput(requestBody);
// 步骤3:校验失败,直接返回 400 错误,阻止请求继续传递
if (!hasXss)
{
return request.CreateErrorResponse(
HttpStatusCode.BadRequest,
AutoCEP.XSSErrorMsg);
}
}
}
// 校验通过,继续传递请求到后续管道(Controller、Action 等)
var response = await base.SendAsync(request, cancellationToken);
return response;
}
}
/// <summary>
/// 允许特殊字符输入的XSS防护验证器
/// 核心策略:不拦截特殊字符,而是通过输出编码确保安全,同时拦截明显的脚本攻击
/// </summary>
public static class SafeXssValidator
{
// 定义危险标签列表
private static readonly HashSet<string> DangerousTags = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"script", "iframe", "svg", "a", "img","input","form","object"
};
// 定义危险函数列表
private static readonly HashSet<string> DangerousFunctions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"eval", "function", "document.write"
};
// 定义危险关键词列表(用于事件处理程序内容)
private static readonly HashSet<string> DangerousKeywords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"tostring", "atob", "eval", "alert", "prompt", "confirm", "top",
"document.cookie", "window.local", "document.local", "sessionstorage"
};
// 特殊字符模式(用于检测混淆的攻击载荷)
private static readonly string SpecialCharPattern = @"[-~\[\]\+\(\)\!]+";
/// <summary>
/// 检查输入是否包含潜在的XSS攻击载荷
/// </summary>
/// <param name="input">用户输入字符串</param>
/// <returns>true表示安全,false表示可能包含XSS攻击</returns>
public static bool IsSafeInput(string input)
{
if (string.IsNullOrEmpty(input))
return true;
// 检查危险标签
if (ContainsDangerousTags(input))
return false;
// 检查危险事件属性
if (ContainsDangerousEvents(input))
return false;
// 检查危险函数调用
if (ContainsDangerousFunctions(input))
return false;
// 检查href/src属性中的危险链接
if (ContainsDangerousUrls(input))
return false;
return true;
}
/// <summary>
/// 检查是否包含危险标签
/// </summary>
private static bool ContainsDangerousTags(string input)
{
// 使用正则表达式匹配HTML标签
var tagPattern = @"<(\w+)[^>]*>";
var matches = Regex.Matches(input, tagPattern, RegexOptions.IgnoreCase);
foreach (Match match in matches)
{
if (match.Groups.Count > 1)
{
string tagName = match.Groups[1].Value.ToLower();
if (DangerousTags.Contains(tagName))
{
return true;
}
}
}
return false;
}
/// <summary>
/// 检查是否包含危险事件属性
/// </summary>
private static bool ContainsDangerousEvents(string input)
{
// 检查尖括号内是否包含任何on事件
var onEventInTagPattern = @"<[^>]*\bon\w+\s*=[^>]*>";
if (Regex.IsMatch(input, onEventInTagPattern, RegexOptions.IgnoreCase))
{
return true;
}
// 检查是否包含特殊字符模式
if (Regex.IsMatch(input, SpecialCharPattern))
{
// 进一步检查特殊字符模式是否与已知攻击模式匹配
if (ContainsSuspiciousPattern(input))
{
return true;
}
}
return false;
}
/// <summary>
/// 检查事件处理程序内容是否包含危险关键词
/// </summary>
private static bool ContainsDangerousKeywordsInEventHandler(string input, int startIndex)
{
// 查找事件处理程序的内容(直到下一个属性或标签结束)
int endIndex = input.IndexOfAny(new[] { ' ', '>', '\'', '"' }, startIndex);
if (endIndex == -1) endIndex = input.Length;
if (startIndex >= endIndex) return false;
string handlerContent = input.Substring(startIndex, endIndex - startIndex).Trim();
// 移除可能的引号
handlerContent = handlerContent.Trim('\'', '"', ' ');
// 检查是否包含危险关键词
foreach (var keyword in DangerousKeywords)
{
if (handlerContent.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0)
{
return true;
}
}
// 检查是否包含特殊字符模式(混淆的攻击载荷)
if (Regex.IsMatch(handlerContent, SpecialCharPattern))
{
// 进一步检查特殊字符模式是否与已知攻击模式匹配
if (ContainsSuspiciousPattern(handlerContent))
{
return true;
}
}
return false;
}
/// <summary>
/// 检查是否包含可疑的攻击模式
/// </summary>
private static bool ContainsSuspiciousPattern(string content)
{
// 这里可以添加更复杂的模式检测逻辑
// 例如检测常见的JS混淆技术
// 简单的检测:如果包含大量特殊字符且长度较短,可能是混淆的攻击载荷
int specialCharCount = Regex.Matches(content, SpecialCharPattern).Count;
if (specialCharCount > 3 && content.Length < 50)
{
return true;
}
return false;
}
/// <summary>
/// 检查是否包含危险函数调用
/// </summary>
private static bool ContainsDangerousFunctions(string input)
{
foreach (var function in DangerousFunctions)
{
// 简单的函数名检测
if (input.IndexOf(function, StringComparison.OrdinalIgnoreCase) >= 0)
{
// 进一步检查是否是函数调用模式
var functionPattern = $@"{function}\s*\(";
if (Regex.IsMatch(input, functionPattern, RegexOptions.IgnoreCase))
{
return true;
}
}
}
return false;
}
/// <summary>
/// 检查href/src属性中的危险链接
/// </summary>
private static bool ContainsDangerousUrls(string input)
{
// 匹配href和src属性
var urlPattern = @"(href|src)\s*=\s*([""']?)([^""'\s>]+)\2";
var matches = Regex.Matches(input, urlPattern, RegexOptions.IgnoreCase);
foreach (Match match in matches)
{
if (match.Groups.Count > 3)
{
string url = match.Groups[3].Value;
// 检查是否是合法的URL格式
if (!IsValidUrl(url))
{
return true;
}
// 检查是否是javascript:或data:等危险协议
if (url.Trim().StartsWith("javascript:", StringComparison.OrdinalIgnoreCase) ||
url.Trim().StartsWith("data:", StringComparison.OrdinalIgnoreCase) ||
url.Trim().StartsWith("vbscript:", StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
/// <summary>
/// 检查URL是否合法
/// </summary>
private static bool IsValidUrl(string url)
{
// 先修剪字符串并转换为小写以便检查
string trimmedUrl = url.Trim().ToLowerInvariant();
// 检查危险协议(使用更健壮的方式)
if (trimmedUrl.StartsWith("javascript:") ||
trimmedUrl.StartsWith("data:") ||
trimmedUrl.StartsWith("vbscript:") ||
trimmedUrl.StartsWith("jscript:") ||
trimmedUrl.StartsWith("livescript:") ||
trimmedUrl.StartsWith("about:"))
{
return false; // 是不安全的协议
}
// 合法的URL应该以http://或https://开头
if (trimmedUrl.StartsWith("http://") ||
trimmedUrl.StartsWith("https://"))
{
return true;
}
// 相对URL也是可以接受的
if (trimmedUrl.StartsWith("/") ||
trimmedUrl.StartsWith("./") ||
trimmedUrl.StartsWith("../"))
{
return true;
}
// 锚点链接
if (trimmedUrl.StartsWith("#"))
{
return true;
}
// 邮件链接
if (trimmedUrl.StartsWith("mailto:"))
{
return true;
}
// 其他情况视为不安全
return false;
}
/// <summary>
/// 安全编码输出字符串(额外的防护层)
/// </summary>
public static string SafeEncode(string input)
{
if (string.IsNullOrEmpty(input))
return string.Empty;
return System.Web.HttpUtility.HtmlEncode(input);
}
}
更多推荐

所有评论(0)