flawopen.com/Cross-Site Scripting/C#

Cross-Site Scripting in C#

High CWE-79 Draft — pending review
ELI5

Imagine a guestbook where visitors write a public note. Stored XSS is like someone writing a note that isn't just text — it's a hidden trick that makes the guestbook page itself start doing things, like stealing the next visitor's login session, the moment anyone opens the page to read it.

Key terms on this page
output encoding
Converting characters that have special meaning in HTML (like < and >) into harmless equivalents before inserting untrusted text into a page, so the browser displays it as text instead of running it as markup or script.
DOM
The in-memory tree structure a browser builds from a page's HTML — where an element's innerHTML is set determines whether inserted content is rendered as inert text or executable markup.

What's happening

Cross-site scripting happens when untrusted input reaches the rendered page without proper output encoding. Razor pages auto-encode by default, so this vulnerability in C# almost always traces back to its explicit raw-output helper being used on untrusted data.

Real-world impact

In 2005, an 19-year-old user exploited a stored XSS flaw on MySpace to create the "Samy" worm — a script that added itself to every profile that viewed it, infecting over a million profiles within about 20 hours and forcing MySpace offline to contain it. It remains one of the most-cited demonstrations of how quickly stored XSS can self-propagate.

Source: widely documented in security industry retrospectives — see References below.

Vulnerable vs. fixed

VULNERABLE
@* Html.Raw skips encoding entirely *@
<div>
  Welcome, @Html.Raw(Model.Name)
</div>
FIXED
@* default Razor output auto-encodes *@
<div>
  Welcome, @Model.Name
</div>

Why the fix works

Plain Razor output (@Model.Name) HTML-encodes the value automatically before writing it — special characters become entities, so a <script> tag renders as visible text. Html.Raw() explicitly disables that encoding, writing the string exactly as given.

C#-specific gotchas

Html.Raw() is named exactly like React's dangerouslySetInnerHTML — a deliberate warning

It exists for genuine cases (trusted CMS content, a sanitized rich-text field), but reaching for it on untrusted input reintroduces the exact vulnerability Razor's default behavior exists to prevent.

Older ASP.NET Web Forms syntax doesn't encode by default

Classic <%= value %> writes raw output; <%: value %> is the encoding variant. Legacy Web Forms code still using the raw syntax is a real audit target in older codebases.

JSON serialized into a <script> block needs its own encoding

Passing server-side data into client-side JS via an inline <script> tag needs JS-context-aware encoding (or a dedicated helper), not HTML encoding — a value safe for HTML body context can still break out of a JS string literal.

Common misconceptions

"Razor auto-encodes everything, so I'm safe by default"

True for standard @ output — false the moment Html.Raw() is used on untrusted data.

"This only matters for comment sections"

Any reflected value counts — a search query echoed on a results page, a URL parameter shown in an error message, a filename displayed after upload.

How to check if you're affected

grep -rn "Html.Raw(" --include="*.cshtml" . grep -rn "<%=" --include="*.aspx" .
Roslyn security analyzers can flag Html.Raw usage on request-derived data; a manual review of each hit against its data source is still the most reliable check.

Prevention checklist

FAQ

Is this the same bug as SQL injection?

Same underlying shape — untrusted data mixed into a command's structure without encoding — but the target and damage differ: SQL injection targets the database, XSS targets other users' browsers.

Does Blazor have the same issue?

Blazor's standard component rendering encodes by default; its MarkupString type is the equivalent explicit opt-out, carrying the same risk as Html.Raw() if applied to untrusted content.

References

View in: Python JavaScript Go Java PHP C# Ruby C/C++ Rust Kotlin Swift Solidity (N/A)