CSRF is a type of web security attack where the attacker site loaded side by side with our web application and attacker site making HTTP requests to our application as the signed-in user. This happens when our authentication mechanism is based on cookies and is accessible to the attacker's site. When the attacker site postback or make HTTP GET call, the cookie is transmitted to our web application and it identifies as a valid user.
How to fix it?
Traditional (Pre SPA - Single Page Application)
This method relies upon one more security measure on the top of the authentication cookie. That is normally called as RequestVerificationToken. Below is the working
- When the page is served to the client, the server will inject an encrypted token in a hidden field
- The client fills data in the form. Then perform submit operation.
- The incoming POST request is validated by the server for the hidden field. If that field is not available or not able to decrypt using the key in hand, reject the request.
SPA
Setting Same-Site to Strict in ASP.Net
If we are using the latest version of .Net starting from 4.7.2, it is easy as below code snippet.
HttpCookie sameSiteCookie = new HttpCookie("AuthCookie"); sameSiteCookie.SameSite = SameSiteMode.Strict;
Another method is to use the web.config without any code change. We even can intercept cookies that are written by other components of the system,
Prior to .Net 4.7.2
As per Microsoft, they don't support earlier .Net versions such as .Net 4.5, .Net 4.5.1, .Net 4.5.2, .Net 4.6, .Net 4.6.1, .Net 4.6.2, .Net 4.7.x, etc.. also there is no reliable way to get it done. Keep the reliability aside, they didn't even provide any method to set it.
I agree giving an unreliable way doesn't make sense. But I am just attempting one way as given below.
private void AddCSRFCookieIfNotPresent(object source) { HttpApplication application = (HttpApplication)source; HttpContext context = application.Context; if (!isCSRFTokenHttpCookieAvalible(context.Request)) { string cookieSetBySomeone = context.Response.Headers["Set-Cookie"]; if (string.IsNullOrWhiteSpace(cookieSetBySomeone)) { context.Response.AddHeader("Set-Cookie", "SameSiteSample=sample; expires=Thu, 28-May-2021 22:08:36 GMT; path=/; secure; HttpOnly;SameSite=Strict"); } else { // Caution - If someone had set the cookie, it may be overwritten. // Else extract existing cookie header, adjust it to include SameSiteSample. Then put it back. Trace.WriteLine(cookieSetBySomeone); } } }
Hope this explains. Please read the caution part. It uses direct Set-Cookie header adjustment.
Now the question is where this function should be written? How to connect to the ASP.Net request processing pipeline?
The answer is kind of depends. But the simple way is to just wrap in HttpModule. The full code goes below.
public class CSRFTokenHttpModule : IHttpModule { private void Application_BeginRequest(Object source, EventArgs e) { AddCSRFCookieIfNotPresent(source); } private void AddCSRFCookieIfNotPresent(object source) { HttpApplication application = (HttpApplication)source; HttpContext context = application.Context; if (!isCSRFTokenHttpCookieAvalible(context.Request)) { string cookieSetBySomeone = context.Response.Headers["Set-Cookie"]; if (string.IsNullOrWhiteSpace(cookieSetBySomeone)) { context.Response.AddHeader("Set-Cookie", "SameSiteSample=sample; expires=Thu, 28-May-2021 22:08:36 GMT; path=/; secure; HttpOnly;SameSite=Strict"); } else { // Caution - If someone had set the cookie, it may be overwritten. // Else extract existing cookie header, adjust it to include SameSiteSample. Then put it back. Trace.WriteLine(cookieSetBySomeone); } } } private bool isCSRFTokenHttpCookieAvalible(HttpRequest Request) { for (var i = 0; i < Request.Cookies.Count; i++) { var cookie = Request.Cookies[i]; if (cookie.Name == "SameSiteSample") { return true; } } return false; } public void Dispose() { // nothing to do. } public void Init(HttpApplication context) { context.BeginRequest += (new EventHandler(this.Application_BeginRequest)); } }
Below goes the web.config entries to connect the class to the ASP.Net pipeline.
<modules runAllManagedModulesForAllRequests="true"> <clear/> <add name="CSRFTokenHttpModule" type="CSRFHttpModule.CSRFTokenHttpModule,CSRFHttpModule" preCondition="" /> </modules>
This is just an attempt out of curiosity, not production-ready. Please try at your risk.
The recommendation is to upgrade the .Net framework and use the supported way.
Rewrite
There are some sites and SO posts talking about rewriting the cookie that adds the same-site flag. This is not tried. Just added it in case it helps someone.
No comments:
Post a Comment