{"id":1453,"date":"2025-06-26T12:40:39","date_gmt":"2025-06-26T12:40:39","guid":{"rendered":"https:\/\/www.cmarix.com\/qanda\/?p=1453"},"modified":"2026-02-05T12:05:47","modified_gmt":"2026-02-05T12:05:47","slug":"how-to-protect-your-net-8-app-against-csrf-and-xss-attacks","status":"publish","type":"post","link":"https:\/\/www.cmarix.com\/qanda\/how-to-protect-your-net-8-app-against-csrf-and-xss-attacks\/","title":{"rendered":"How to Protect your .NET 8 App Against CSRF and XSS Attacks?"},"content":{"rendered":"\n<p>Web security is more important than ever, especially as attackers get smarter. In this post, you\u2019ll learn how to protect your .NET 8 application against CSRF and XSS attacks using built-in .NET features, middleware, and coding best practices.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">CSRF (Cross-Site Request Forgery) Protection<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">What is CSRF?<\/h3>\n\n\n\n<p>CSRF tricks an authenticated user into unknowingly submitting a request to a web application, potentially changing user data or performing unauthorized actions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>How .NET 8 Protects Against CSRF<\/strong><\/h3>\n\n\n\n<p>ASP.NET Core (including .NET 8) provides built-in CSRF protection using anti-forgery tokens, primarily for unsafe HTTP methods (POST, PUT, DELETE).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What are the Step-by-Step Guide to Enable CSRF Protection?<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Use Razor Pages \/ MVC with AntiForgeryToken<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;form asp-controller=\"Account\" asp-action=\"UpdateEmail\" method=\"post\">\n    @Html.AntiForgeryToken()\n    &lt;input type=\"email\" name=\"newEmail\" required \/>\n    &lt;button type=\"submit\">Update Email&lt;\/button>\n&lt;\/form><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Validate the token on POST action<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;HttpPost]\n&#91;ValidateAntiForgeryToken]\npublic IActionResult UpdateEmail(string newEmail)\n{\n    \/\/ Update logic\n    return Ok();\n}<\/code><\/pre>\n\n\n\n<p>The [ValidateAntiForgeryToken] attribute ensures that the token is checked and rejects any request without a valid token.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">For APIs: Use SameSite Cookies + Custom Header Tokens<\/h2>\n\n\n\n<p>CSRF tokens don&#8217;t apply well to APIs (since many use application\/json requests without forms), so for Web APIs, consider:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Use SameSite cookies:<\/strong> Prevent cookies from being sent cross-origin.<\/li>\n\n\n\n<li>Require a custom header to confirm origin.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>services.Configure&lt;CookiePolicyOptions>(options =>\n{\n    options.MinimumSameSitePolicy = SameSiteMode.Strict;\n});<\/code><\/pre>\n\n\n\n<p>On the client, send a custom header (e.g., X-CSRF-TOKEN) that is validated server-side.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;HttpPost]\npublic IActionResult SecureApi(&#91;FromHeader(Name = \"X-CSRF-TOKEN\")] string csrfToken)\n{\n    if (csrfToken != expectedToken)\n    {\n        return Unauthorized();\n    }\n\n    return Ok(\"Secure call\");\n}<\/code><\/pre>\n\n\n\n<p>Use middleware or filters to validate CSRF headers globally.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>XSS (Cross-Site Scripting) Protection<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>What is XSS?<\/strong><\/h3>\n\n\n\n<p>XSS attack is injecting malicious scripts into trusted content. These scripts can steal cookies, log keystrokes, or hijack sessions.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Preventing XSS in .NET 8<\/strong><\/h3>\n\n\n\n<p>.NET automatically HTML-encodes Razor output \u2014 but you still need to follow output encoding, input validation, and content security policies (CSP).<\/p>\n\n\n\n<p><strong>Razor Pages \/ Views: HTML Encoding by Default<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;p>@Model.UserComment&lt;\/p> &lt;!-- Safe: encoded by default --><\/code><\/pre>\n\n\n\n<p>Avoid using @Html.Raw() unless absolutely necessary and only on trusted content.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Server-side Validation and Sanitization<\/strong><\/h3>\n\n\n\n<p>Use regex or libraries to sanitize input:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if (Regex.IsMatch(comment, @\"&lt;script>\", RegexOptions.IgnoreCase))\n{\n    ModelState.AddModelError(\"Comment\", \"Script tags are not allowed.\");\n}<\/code><\/pre>\n\n\n\n<p>Or sanitize using a library like <a href=\"https:\/\/github.com\/mganss\/HtmlSanitizer\" target=\"_blank\" rel=\"noopener\">HtmlSanitizer<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dotnet add package Ganss.XSS<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>var sanitizer = new HtmlSanitizer();\nstring safeHtml = sanitizer.Sanitize(userInput);<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Add HTTP Security Headers<\/strong><\/h3>\n\n\n\n<p>Use middleware to add headers like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Content-Security-Policy<\/li>\n\n\n\n<li>X-Content-Type-Options<\/li>\n<\/ul>\n\n\n\n<p>X-XSS-Protection (deprecated but still good for older browsers)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>app.Use(async (context, next) =>\n{\n    context.Response.Headers.Add(\"Content-Security-Policy\", \"default-src 'self'\");\n    context.Response.Headers.Add(\"X-Content-Type-Options\", \"nosniff\");\n    await next();\n});<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>CSP (Content Security Policy) Example<\/strong><\/h3>\n\n\n\n<p>This prevents inline script execution unless specifically allowed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Content-Security-Policy: default-src 'self'; script-src 'self' https:\/\/trusted.cdn.com;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Don\u2019t Allow HTML in User Input (If Not Needed)<\/strong><\/h3>\n\n\n\n<p>If your site doesn\u2019t need to render raw HTML, strip it out completely:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>var clean = Regex.Replace(userInput, \"&lt;.*?>\", string.Empty);<\/code><\/pre>\n\n\n\n<p><strong>Test Your Protection<\/strong><\/p>\n\n\n\n<p>Use tools like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>OWASP ZAP or Burp Suite \u2013<\/strong> Penetration testing<\/li>\n\n\n\n<li><strong>Postman \u2013<\/strong> Simulate CSRF\/XSS<\/li>\n\n\n\n<li><strong>Chrome DevTools \u2013<\/strong> Test SameSite behavior<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Secure Your .NET 8 App From Common Attacks<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td><strong>Threat<\/strong><\/td><td><strong>Defense<\/strong><\/td><\/tr><tr><td><strong>CSRF<\/strong><\/td><td>AntiForgeryToken, SameSite cookies, custom headers<\/td><\/tr><tr><td><strong>XSS<\/strong><\/td><td>HTML encoding, sanitizing input, CSP headers, avoid using @Html.Raw()<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Protecting your .NET 8 app from CSRF and XSS is not just about writing secure code, but also about configuring your application correctly. With built-in tools like AntiForgeryToken, HTML encoding in Razor, and custom middleware for headers, .NET gives you a solid base \u2014 but you must implement these correctly.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Web security is more important than ever, especially as attackers get smarter. In this post, you\u2019ll learn how to protect your .NET 8 application against CSRF and XSS attacks using built-in .NET features, middleware, and coding best practices. CSRF (Cross-Site Request Forgery) Protection What is CSRF? CSRF tricks an authenticated user into unknowingly submitting a [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1458,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[4,3],"tags":[],"class_list":["post-1453","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dot-net","category-web"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/1453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/comments?post=1453"}],"version-history":[{"count":7,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/1453\/revisions"}],"predecessor-version":[{"id":1462,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/1453\/revisions\/1462"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media\/1458"}],"wp:attachment":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media?parent=1453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/categories?post=1453"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/tags?post=1453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}