.NET Cross-Site Scripting
Cross-Site Scripting (XSS) is one of the most common web application security vulnerabilities that can affect .NET applications. In this comprehensive guide, we'll explore what XSS is, how it works in .NET applications, and how to protect your applications against these types of attacks.
What is Cross-Site Scripting (XSS)?
Cross-Site Scripting (XSS) is a security vulnerability that allows attackers to inject malicious client-side scripts into web pages viewed by other users. These attacks occur when an application takes data from a user and sends it to a web browser without properly validating or encoding it.
When successful, XSS attacks enable hackers to:
- Steal session cookies and impersonate users
- Capture keystrokes and form data
- Redirect users to malicious websites
- Deface websites or manipulate page content
- Execute arbitrary JavaScript in victims' browsers
Types of XSS Attacks
There are three main types of XSS attacks that can affect .NET applications:
-
Reflected XSS: Occurs when malicious script is reflected off a web application to the victim's browser through URLs, form submissions, or other immediate responses.
-
Stored XSS: Happens when malicious script is stored on the target server (like in a database) and later retrieved and displayed to users.
-
DOM-based XSS: Takes place entirely in the browser when JavaScript manipulates the DOM in an unsafe way.
XSS Vulnerabilities in .NET Applications
.NET applications can be vulnerable to XSS attacks when they:
- Display user input without proper encoding
- Render HTML or JavaScript dynamically based on user input
- Build HTML or URLs with unvalidated user data
- Use older frameworks that don't have automatic XSS protection
Preventing XSS in .NET Applications
1. Output Encoding
The primary defense against XSS is proper output encoding. ASP.NET provides built-in tools for this:
// BAD - Direct insertion of user input
@Html.Raw(Model.UserComment) // MVC Razor
// GOOD - Using HTML encoding
@Model.UserComment // MVC Razor (auto-encoded)
@Html.Encode(Model.UserComment) // Classic ASP.NET
HttpUtility.HtmlEncode(userInput) // General purpose
Different contexts require different encoding strategies:
// HTML context encoding
var encodedHtml = HttpUtility.HtmlEncode(userInput);
// URL encoding
var encodedUrl = HttpUtility.UrlEncode(userInput);
// JavaScript encoding (using AntiXSS library)
var encodedJs = Microsoft.Security.Application.Encoder.JavaScriptEncode(userInput);
2. Content Security Policy (CSP)
Add CSP headers to your .NET application to restrict which scripts can execute:
// In ASP.NET Core Startup.cs or Program.cs
app.Use(async (context, next) =>
{
context.Response.Headers.Add(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' https://trusted-cdn.com");
await next();
});
3. ASP.NET Core Built-in Protection
ASP.NET Core has built-in XSS protection mechanisms:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
// Enforces automatic HTML encoding
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
}
4. Input Validation
Always validate user input before processing it:
// Backend validation
public class CommentInputModel
{
[Required]
[StringLength(1000)]
[RegularExpression(@"^[^<>]*$", ErrorMessage = "HTML characters are not allowed")]
public string CommentText { get; set; }
}
Real-World Example: Building a Secure Comment System
Let's build a simple but secure comment system in ASP.NET Core:
Step 1: Define the Comment Model
public class Comment
{
public int Id { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public DateTime PostedDate { get; set; }
}
public class CommentViewModel
{
[Required]
[StringLength(100)]
public string Author { get; set; }
[Required]
[StringLength(2000)]
public string Content { get; set; }
}
Step 2: Create the Controller
public class CommentsController : Controller
{
private readonly ICommentRepository _commentRepository;
public CommentsController(ICommentRepository commentRepository)
{
_commentRepository = commentRepository;
}
public IActionResult Index()
{
var comments = _commentRepository.GetAllComments();
return View(comments);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Add(CommentViewModel model)
{
if (!ModelState.IsValid)
{
return RedirectToAction("Index");
}
// Create new comment - content is HTML encoded by default in the view
var comment = new Comment
{
Author = model.Author,
Content = model.Content,
PostedDate = DateTime.Now
};
_commentRepository.AddComment(comment);
return RedirectToAction("Index");
}
}
Step 3: Create the View
@model IEnumerable<Comment>
<h2>Comments</h2>
<div class="comment-form">
<form asp-action="Add" asp-controller="Comments" method="post">
@Html.AntiForgeryToken()
<div class="form-group">
<label for="Author">Your Name:</label>
<input type="text" id="Author" name="Author" class="form-control" required maxlength="100" />
</div>
<div class="form-group">
<label for="Content">Comment:</label>
<textarea id="Content" name="Content" class="form-control" required maxlength="2000"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Comment</button>
</form>
</div>
<div class="comments-list">
@foreach (var comment in Model)
{
<div class="comment">
<h4>@comment.Author</h4>
<p>@comment.Content</p>
<small>Posted on: @comment.PostedDate.ToString("MMM dd, yyyy")</small>
</div>
}
</div>
Notice that in the Razor view, the @comment.Content
automatically HTML encodes the output, protecting against XSS.
Common Vulnerable Patterns to Avoid
1. Using Html.Raw()
with User Input
// VULNERABLE: Bypasses encoding
@Html.Raw(Model.UserGeneratedHtml)
2. Using [AllowHtml]
Without Sanitization
// VULNERABLE: Allows users to submit HTML
[AllowHtml]
public string Description { get; set; }
3. Unsafe JavaScript Data Handling
// VULNERABLE: Direct insertion of server data into DOM
function showUserInfo() {
// Assuming userInfo comes from the server with user-provided data
document.getElementById('userProfile').innerHTML = userInfo;
}
Using HTML Sanitization Libraries
When you need to allow limited HTML (like for a rich text editor), use a sanitization library:
// Using HtmlSanitizer package
using HtmlSanitizer;
public class CommentService
{
public string SanitizeUserHtml(string html)
{
var sanitizer = new Sanitizer();
// Configure allowed tags and attributes
sanitizer.AllowedTags.Add("b");
sanitizer.AllowedTags.Add("i");
sanitizer.AllowedTags.Add("p");
return sanitizer.Sanitize(html);
}
}
Usage:
[HttpPost]
public IActionResult SaveComment(CommentModel model)
{
// Sanitize HTML content before saving
model.Content = _commentService.SanitizeUserHtml(model.Content);
// Now save the sanitized content
_repository.SaveComment(model);
return RedirectToAction("Index");
}
Testing for XSS Vulnerabilities
Simple test payloads to check your application for XSS vulnerabilities:
<script>alert('XSS')</script>
<img src="x" onerror="alert('XSS')">
javascript:alert('XSS')
<div onmouseover="alert('XSS')">Hover over me</div>
Summary
Cross-Site Scripting remains one of the most common security vulnerabilities in web applications, including those built with .NET. By implementing proper output encoding, input validation, and leveraging the built-in protections of modern .NET frameworks, you can significantly reduce the risk of XSS attacks.
Remember these key points:
- Always encode output when displaying user-provided content
- Validate and sanitize user input on both client and server sides
- Use Content Security Policy headers to add an extra layer of protection
- Consider context-specific encoding for different parts of your HTML
- Use security libraries for advanced scenarios like HTML sanitization
Additional Resources
- OWASP XSS Prevention Cheat Sheet
- Microsoft's ASP.NET Core Security Documentation
- HtmlSanitizer NuGet Package
- AntiXSS Library
Exercise
Exercise 1: Create a simple ASP.NET Core MVC application with a form that accepts user input and displays it back to the user. Implement proper XSS protection using the techniques described in this article.
Exercise 2: Audit an existing .NET application for XSS vulnerabilities. Look for instances where user input is displayed without proper encoding, and fix them using appropriate encoding methods.
Exercise 3: Implement a Content Security Policy in your .NET application and test that it properly restricts unsafe script execution.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)