Cross-Site Scripting (XSS) occurs when an application displays untrusted input as executable HTML or JavaScript.
For example, an attacker submits:
<script>stealCookies()</script>
If the browser executes it, the attacker may steal session information, modify the page, or redirect the user.
1. Use Razor’s automatic HTML encoding
Razor automatically HTML-encodes values displayed using @.
<p>@Model.Comment</p>
If Model.Comment contains:
<script>alert('Hacked')</script>
Razor generates:
<script>alert('Hacked')</script>
The browser displays it as text instead of executing it. Razor and Tag Helpers automatically encode variable output by default. Microsoft Learn
2. Avoid Html.Raw() with untrusted content
This is dangerous:
@Html.Raw(Model.Comment)
Html.Raw() bypasses Razor’s automatic encoding. If Comment contains a script, the browser may execute it.
Use this instead:
@Model.Comment
Also avoid creating HtmlString objects from user-controlled content because they are not automatically encoded.
3. Sanitize content when HTML must be allowed
Some applications intentionally accept formatted HTML, such as:
- Blog articles
- Rich-text editor content
- Product descriptions
In that situation, normal Razor encoding would display the HTML tags as text. Before using Html.Raw(), sanitize the content with an allow-list-based HTML sanitizer.
var safeHtml = sanitizer.Sanitize(model.Content);
Then render only the sanitized value:
@Html.Raw(Model.SanitizedContent)
A commonly used third-party package is:
dotnet add package HtmlSanitizer
Example:
using Ganss.Xss;
var sanitizer = new HtmlSanitizer();
var safeContent = sanitizer.Sanitize(article.Content);
HtmlSanitizer is an external NuGet library; it is not built into ASP.NET Core.
For your portfolio’s TinyMCE article content, this sanitization step is particularly important because the content is intentionally rendered as HTML.
4. Validate input—but don’t rely on validation alone
Use ViewModel validation to restrict expected input:
public class CommentViewModel
{
[Required]
[StringLength(500)]
public string Comment { get; set; } = string.Empty;
}
Validation helps reject unexpected input, but it does not replace output encoding or sanitization. Microsoft recommends encoding untrusted content at output even when it has already been validated or sanitized.
5. Handle JavaScript values safely
Avoid inserting untrusted input directly into JavaScript:
<script>
const name = '@Model.Name'; // unsafe context
</script>
A safer approach is to store the encoded value in a data attribute:
<div id="user-data" data-name="@Model.Name"></div>
<script>
const element = document.getElementById("user-data");
const name = element.dataset.name;
document.getElementById("result").textContent = name;
</script>
Prefer safe DOM properties:
element.textContent = userInput;
Avoid dangerous DOM operations with untrusted input:
element.innerHTML = userInput;
document.write(userInput);
6. Use Tag Helpers for attributes and URLs
<a asp-controller="Product"
asp-action="Details"
asp-route-id="@Model.Id">
Details
</a>
Tag Helpers apply appropriate encoding when generating attributes. Avoid manually concatenating untrusted values into HTML, JavaScript, or URLs.
7. Add a Content Security Policy
A Content Security Policy provides an additional layer of protection by controlling which scripts the browser may execute.
Example response header:
Content-Security-Policy: default-src 'self'; script-src 'self'
CSP is defence in depth. It does not replace output encoding and sanitization.
Key points
- Display normal values using
@Model.Property. - Never use
Html.Raw()with untrusted or unsanitized content. - Sanitize rich HTML before rendering it.
- Encode at output; do not store pre-encoded values in the database.
- Avoid placing untrusted values directly inside JavaScript.
- Use
textContentinstead ofinnerHTML. - Validate input, but never treat validation alone as XSS protection.
- Consider CSP as an additional security layer.
Interview answer
Razor prevents XSS by automatically HTML-encoding values rendered with the
@syntax. Developers must avoid bypassing this protection withHtml.Raw()orHtmlString. If rich HTML must be displayed, it should first be sanitized with an allow-list-based sanitizer. JavaScript, URLs and HTML attributes must also use context-appropriate encoding.