In ASP.NET Core, Razor encodes variable output by default, so most XSS in C# apps comes from switching that off, placing untrusted data inside JavaScript, or returning unescaped markup from an API. Keep the default, use the built-in encoders when you build strings by hand, and review every place that bypasses them.
What does Razor do for you?
Microsoft's documentation says the Razor engine automatically encodes all output that comes from variables, unless you work to prevent it. It applies HTML attribute encoding rules whenever you use the @ directive. Attribute encoding is a superset of HTML encoding, so you do not have to choose between them. Razor Tag Helpers also encode the values you put in tag parameters.
That default is good, and it has two stated limits. The @ directive is safe only in an HTML context. Microsoft warns you must not use it to insert untrusted input directly into JavaScript. And the protection applies to views and Razor Pages, not to other code paths you write yourself.
Where do C# apps still get XSS?
Think of three patterns.
First, raw output. Calling Html.Raw on a value tells Razor not to encode it. A constructed example: a product page stores a seller's description and renders it with Html.Raw so line breaks work. A description containing a script tag now runs for every shopper. The fix is to render text normally and handle line breaks with a small encoded transformation, or to sanitize allowed markup first.
Second, data inside script blocks. A page that writes var name = "@Model.Name"; inside a script element is using an HTML encoder for a JavaScript context. Microsoft points to the JavaScriptEncoder for that job. Better still, emit the value as a data attribute or a JSON blob and read it from script.
Third, APIs. Microsoft notes that web APIs returning HTML, XML or JSON can trigger XSS in their client apps if they do not handle user input properly, depending on how much trust the client places in the API. If an API accepts user-generated content and returns it in an HTML response, the data is open to attack. Set the correct content type and validate and encode on the way out.
How do the built-in encoders work?
ASP.NET Core exposes HtmlEncoder, JavaScriptEncoder and UrlEncoder, and you can take them through dependency injection in a constructor. Use them when you assemble output outside Razor, for example when you write a response body by hand, build an email, or generate a script fragment.
For URLs, Microsoft says to URL encode untrusted data placed in a query string. A value like a quoted string with spaces and an ampersand becomes percent-encoded text. Encoding does not validate a whole destination, so a user-supplied address used in an anchor still needs a scheme and host check.
What about the rest of the defense?
OWASP, in its cross-site scripting cheat sheet, says no single technique solves XSS. Frameworks reduce bugs but problems occur when escape hatches are used or components are out of date. Add a content security policy as a second layer, keep sanitizers and UI packages updated, and treat each bypass as a code review item.
What to do
- Search the solution for
Html.Raw,@Html.Raw,HtmlStringandMarkupString(Blazor). List each one with its data source. - Remove the bypass where the content is plain text. Where markup is required, sanitize with a maintained library and allow a short list of tags.
- Search script blocks in views for
@expressions and move values to data attributes or JSON. - Inject
HtmlEncoder,JavaScriptEncoderorUrlEncoderwherever you build output by hand. - Check every API action that returns user-supplied content for the right
Content-Typeand encoding. - Add a content security policy header and begin in report-only mode.
- Scan dependencies on every build so out-of-date front-end packages show up before release. Safeguard's SBOM and dependency scanning, plus the free scan at gold.safeguard.sh/scan, can do this.
Quick check
Is @Model.Name inside a script block safe?
No. Razor's default encoding targets HTML. Microsoft says not to insert untrusted input directly into JavaScript with @. Use a JavaScript encoder or pass the value as data.
Do I need to encode in a Razor view if I use Tag Helpers?
Not for ordinary values. Microsoft says Tag Helpers encode the input you use in tag parameters. You still need care with raw output and URLs.
Sources
- Microsoft Learn: Prevent Cross-Site Scripting (XSS) in ASP.NET Core
- OWASP Cross Site Scripting Prevention Cheat Sheet
Last reviewed: 18 October 2026.