Preventing CSRF with the same-site cookie attribute - asp.net

I was surfing the web and found article Preventing CSRF with the same-site cookie attribute.
As on link maintain We need to add Set-Cookie header.
Set-Cookie: key=value; HttpOnly; SameSite=strict
Now My Question is, I want to set this in my ASP.NET site in all Cookies and Authentication Cookie.
I tried to set this using header from IIS but someone says this is wrong way implementation.
I have also tried below.
HttpCookie newAuthenticationCookie = new HttpCookie(FormsAuthentication.FormsCookieName
, FormsAuthentication.Encrypt(newAuthenticationTicket))
{
HttpOnly = true
};
newAuthenticationCookie.Values.Add("SameSite", "strict");
But it seems like not helping me.
Please suggest me a better way to do this.
Thanks.

After Deep review on HttpCookie Source it's confirm that we cannot do this with the code, as there is no way to add extra attribute on Cookie and class is marked as sealed.
But still anyhow I manage solution by modifying web.config as below.
<rewrite>
<outboundRules>
<rule name="Add SameSite" preCondition="No SameSite">
<match serverVariable="RESPONSE_Set_Cookie" pattern=".*" negate="false" />
<action type="Rewrite" value="{R:0}; SameSite=strict" />
<conditions>
</conditions>
</rule>
<preConditions>
<preCondition name="No SameSite">
<add input="{RESPONSE_Set_Cookie}" pattern="." />
<add input="{RESPONSE_Set_Cookie}" pattern="; SameSite=strict" negate="true" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
This add SameSite=strict on each Set-Cookie.

You can also set this in code when creating a cookie:
var httpCookie = new HttpCookie("mycookie", "myvalue");
httpCookie.Path += ";SameSite=Strict";
Response.SetCookie(httpCookie);
This will give you the following header:
Set-Cookie:mycookie=myvalue; path=/;SameSite=Strict
bit of a hack until it's pushed in to the framework.

Just adding my answer to systematize all the info found here and in other places.
1. To secure custom cookies under 4.7.2 and later
var c = new HttpCookie("test");
c.SameSite = SameSiteMode.Lax;
2. To secure Forms authentication cookie
In web.config
<authentication mode="Forms">
<forms ..... cookieSameSite="Lax" />
</authentication>
3. To secure ASP.NET Session cookie
In Global.asax
void Session_Start(Object sender, EventArgs e)
{
Response.Cookies["ASP.NET_SessionId"].SameSite = SameSiteMode.Lax;
//while we're at it lets also make it secure
if (Request.IsSecureConnection)
Response.Cookies["ASP.NET_SessionId"].Secure = true;
}
Fun fact: even if you set <httpCookies requireSSL="true" /> the ASP.NET session cookie will still be non-secure for some reason.
3(a). UPDATE 01.2020: .NET 4.8 Session cookie is now "SameSite" by default
Installing the latest Windows Update will make your session cookies Lax by default. You can control it here:
<sessionState cookieSameSite="Lax" /> <!-- in system.web -->
4. <httpCookies samesite=xxx> does not exist?
Adding <httpCookies sameSite="Strict" /> like suggested in the comment above in web.config didn't work, I was getting the error.
Unrecognized attribute 'samesite'
Even though I'm targeting 4.7.2. Tested on multiple project and multiple machines, also VS2019 does not show this in intellisense and MS docs do not mention it anywhere.

.NET 4.7.2 has now built-in support for SameSite property. The HttpCookie has now a property called SameSite. See more info here from Microsoft.
No need anymore to hack this through the config file.

In order to have SameSite defined to ASP.NET_SessionId cookie I had to set the web.config under system.web section:
<sessionState cookieSameSite="Lax" />

Because in this day and age we use owin to fix the same silly webapi cookie bug...
public class CookieSameSiteMiddleware : OwinMiddleware
{
public CookieSameSiteMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
var url = context.Request.Path.Value.ToLowerInvariant();
if (url.Contains("/api/mylogin"))
{
context.Response.OnSendingHeaders(x =>
{
var scv = context.Response.Headers.FirstOrDefault(h => h.Key == "Set-Cookie");
if (!scv.Equals(default(KeyValuePair<string, string[]>)))
{
//context.Response.Headers.Remove("Set-Cookie");
context.Response.Headers.Set("Set-Cookie", scv.Value[0] + "; SameSite=strict");
}
}, null);
}
await this.Next.Invoke(context);
}
}
Make sure the middle-ware is registered before .UseWebApi()

Pre 4.7.2 you can just append the string to the cookie path.
FormsAuthentication.SetAuthCookie(username, false, FormsAuthentication.FormsCookiePath + "; SameSite=Lax");

https://www.nuget.org/packages/Microsoft.Owin.Security.Cookies/4.1.0 now supports SameSite.
That is very good news because the other solutions here doesn't work that brilliantly:
Implementing OwinMiddleware: Works great, except for performance. This might be something specific for our environment but that solution was about 10% of our CPU.
<outboundRules>: Probably possible to get working. But all solutions I've seen so far and we tested, including the one in this thread, had some issues when multiple cookies where set in the same response.

Related

Acces-Control-Allow-Origin works with Web.config (IIS7) but not with (WebApiConfig.cs) ASP.NET Cross Origin support

For a project i want to load and view a pdf file with angular-pdfjs. The team uses ASP.net Cross Origin, to Allow-Acces-Control, Headers, Credentials etc.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Replace the default implementation of the ITraceWriter with our custom logger.
config.Services.Replace(typeof (ITraceWriter), new GlobalTraceLogger());
// Replace the default exception logger to be able to log exceptions with NLog
config.Services.Replace(typeof (IExceptionLogger), new GlobalExceptionLogger());
// Replace the default exceptionhandler to be able to handle exceptions globally
config.Services.Replace(typeof (IExceptionHandler), new GlobalExceptionHandler());
// We must enable cors, because otherwise we are not able to commuincate with a java script client
// TODO: We need to restirct the requested resource. Do not allow every origin!
// Do not run this in prodocutive environment
var cors = new EnableCorsAttribute("*", "*", "*", "*");
cors.SupportsCredentials = true;
config.EnableCors(cors);
config.MapHttpAttributeRoutes();
// Make the default return type JSON
var appXmlType =
config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
This works good so far, but if i want to load my pdf file with angular-pdfjs, i get a Cross Origin Error, because Allow-Acces-Control-Origin "*" didn't works for my pdf-url.
(https://img3.picload.org/image/roirrgcw/corsworksnot.png)
But if i using instead of ASP.net Cross Origin Support the Allow-Access-Control of IIS7 in Web.config:
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested- With, Content-Type, Accept" />
<add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />
<add name="Access-Control-Allow-Credentials" value="true" />
</customHeaders>
</httpProtocol>
it works and the pdf will be loaded correctly.
(https://picload.org/image/roirrgci/corsworks.jpg)
But the problem is, at the moment the page is loaded via "file://" and so i get
an error because there is no Access-Control-Allow-Origin for 'null'. That means, my pdf is loading correctly this way, but the login, pictures... won't be loaded anymore. So my question is, if someone knows how i can change the WebApiConfig-Implementation that my pdf-file get an Access-Controll-Allow as well. Or maybe can someone tell where the error could be.
For information:
Thats the way i'm loading the pdf with angular-pdfjs:
<!---------------------------THE PDF VIEWER DIRECTIVE------------------------->
<div pdf-viewer="options" pdf-url="pdfUrl" id="my-viewer" class="col col-lg-10"></div>
<!---------------------------THE PDF VIEWER DIRECTIVE------------------------->
and thats the url, i'm using:
function PdfviewController(ebmGuideLineService, mediaService, $scope, $window) {
var vm = this;
$scope.pdfUrl = 'http://localhost:3787/NCCN_Evidence_Blocks_Melanoma.pdf';
$scope.options = { mouseZoom: false, mousePan: false };
Please tell me, if you need more informations and thank you for your help.

Add custom response header to web.config

I have a website that is susceptible to a clickjacking vulnerability. Doing some research, it looks like one of the simple approaches is to simply add the X-Frame-Options: SAMEORIGIN to the response header. This is a very old web application (ca. last update was 2004), and is running IIS 6 with ASP.NET 2.0.
In newer versions, I could simply add the following section to the web.config
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
</system.webServer>
And that would be the end of it. However, I can't seem to be able to verify that this is possible using IIS 6.
Is this possible with IIS 6 and ASP.NET 2.0 to be done in only the web.config file? If so, how? If not, what code changes would I have to make in order to achieve the same result? Would simply adding
Context.Response.AddHeader("X-Frame-Options", "SAMEORIGIN");
to the Global.asax#Application_EndRequest be sufficient?
I don't believe that you'll be able to accomplish this solely by updating the web.config since you are targeting II6 (as support for the <customHeaders> section was added in IIS7+).
What you would likely need to do would be to create a custom HttpModule similar to the approach mentioned in this blog post that would handle actually adding the Header which might look something like this :
public class SameOriginHeaderModule : IHttpModule
{
private HttpApplication _application;
public void Init(HttpApplication context)
{
_application = context;
context.PreSendRequestHeaders += OnPreSendRequestHeaders;
}
void context_EndRequest(object sender, EventArgs e)
{
// If your request exists, add the header
if (_application.Context != null)
{
var response = _application.Response;
response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
}
}
public void Dispose()
{
}
}
You would then need to register this module within your web.config file as seen below :
<configuration>
<system.web>
<httpModules>
<add name="SameOriginHeaderModule" type="SameOriginHeaderModule" />
</httpModules>
</system.web>
</configuration>

Share ADFS/WIF federated claims cookie on multiple virtual applications (but the same domain)

I'm trying to (re)use ADFS 2/WIF claims based authentication cookies for multiple different applications running on the same domain.
So I have these applications/virtual directories that I would like to reuse the same authentication cookie:
https://domain.local/portal
https://domain.local/myapp
In the portal, I'd like to include (client side authenticated) content from myapp, so I don't want every app to be authenticated separately with a redirect to STS/ADFS.
I thought this would be pretty straightforward as they could both access the same cookie as they reside on the same domain, but the cookie is only valid for the application it was created in (FedAuth and FedAuth1 cookie paths are restricted to "/portal/")
When I set the 'path' in the cookieHandler settings to "/", I will get an exception:
[SecurityTokenException: ID4291: The security token 'System.IdentityModel.Tokens.SessionSecurityToken' is not scoped to the current endpoint.]
System.IdentityModel.Tokens.SessionSecurityTokenHandler.ValidateToken(SessionSecurityToken token, String endpointId) +1008632
System.IdentityModel.Services.SessionAuthenticationModule.ValidateSessionToken(SessionSecurityToken sessionSecurityToken) +351
System.IdentityModel.Services.SessionAuthenticationModule.SetPrincipalFromSessionToken(SessionSecurityToken sessionSecurityToken) +91
System.IdentityModel.Services.SessionAuthenticationModule.AuthenticateSessionSecurityToken(SessionSecurityToken sessionToken, Boolean writeCookie) +66
System.IdentityModel.Services.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs) +929
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
I've tried to use the Microsoft.Owin.Security.WsFederation beta packages mentioned in this article as an alternative, no success getting this running:
http://blogs.msdn.com/b/webdev/archive/2014/02/21/using-claims-in-your-web-app-is-easier-with-the-new-owin-security-components.aspx
Before I'm going to try to override methods in the SessionSecurityTokenHandler, is it even possible what I'm trying to achieve?
Thanks in advance!
Change cookieHandler as below in system.identityModel.services --> federationConfiguration
<federatedAuthentication>
<cookieHandler requireSsl="true" path="/" />
</federatedAuthentication>
It was actually pretty simple to do it, by replacing MachineKeySessionSecurityTokenHandler with a custom implementation that get's rid of the token validation:
public class SharedSecurityTokenHandler : MachineKeySessionSecurityTokenHandler
public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SessionSecurityToken token, string endpointId)
{
if (token == null) throw new ArgumentNullException("token");
if (endpointId == null) throw new ArgumentNullException("endpointId");
return ValidateToken(token);
}
}
the just registering it here in the web.config:
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<add type="Security.Web.SharedSecurityTokenHandler, Security.Web, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
I've been trying to achieve the same thing and found that SessionAuthenticationModule.ValidateSessionToken(SessionSecurityToken sessionSecurityToken) calls:
securityTokenHandler.ValidateToken(sessionSecurityToken, this.CookieHandler.Path)
..where the second argument is endpointId. Therefore, configuring both my apps with:
<system.identityModel.services>
<federationConfiguration>
<cookieHandler domain="example.com" path="/" />
...
</federationConfiguration>
</system.identityModel.services>
..allowed the validation in MachineKeySessionSecurityTokenHandler to pass.

Custom Role Provider not called. What am I doing wrong?

So I'm trying to create a Hello World custom Role Provider-solution in ASP.NET MVC 4.
Basically I've set authentication mode="Windows" in web.config along with defining a role provider like this:
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider" type="MyProject.Code.MyRoleProvider" />
</providers>
</roleManager>
Then I've decorated the About controller method like this:
[Authorize(Roles = "SomeRole")]
public ActionResult About()
{ /* ... */ }
The custom RoleProvider-class looks like this:
namespace MyProject.Code {
public class MyRoleProvider : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
if (roleName == "SomeRole" && username = "Administrator") return true;
return false;
}
public override string[] GetRolesForUser(string username)
{
return new string[] { "SomeRole" };
}
/* a bunch of other overridden methods simply throwing not implementedException */
}
}
Now the thing is I've put a breakpoint on every single executable line of code in MyRoleProvder but none are hit. I have tested that breakpoints elsewhere are hit so the debugger is not the problem. Why isn't my code in the role provided executed? I was expecting IsUserInRole and/or GetRolesForUser to be executed when I navigate to the About-page. Am I wrong? Have I configured something wrong?
Full web.config for reference
edit: The user is redirected to the login page when the about page is clicked. I now realize this is due to the user actually is not authenticated yet. Authorization naturally happens after authentication. Is IISExpress not providing Windows identity?
As this is the first result in a google search i want to give a solution, maybe for others:
To use the windows identity in IIEExpress you have to activate it in your applicationhost.config , which is located in
[SolutionDir].vs\config\applicationhost.config
there you can find the xml tag
<configuration>
<system.webServer>
<security>
<authentication>
<windowsAuthentication enabled="false">
change enabled="false" to enabled="true"
and you can use windows authenticatuion in IISExpress
I think your type declaration is incomplete, you should include both the full name and the assembly name.
type="MyProject.Code.MyRoleProvider, MyProject"
You might also need to set Version, Culture and PublicKeyToken if your assemblies are place in the GAC
Hope this helps

Using ASP.NET routing to serve static files

Can ASP.Net routing (not MVC) be used to serve static files?
Say I want to route
http://domain.tld/static/picture.jpg
to
http://domain.tld/a/b/c/picture.jpg
and I want to do it dynamically in the sense that the rewritten URL is computed on the fly. I cannot set up a static route once and for all.
Anyway, I can create a route like this:
routes.Add(
"StaticRoute", new Route("static/{file}", new FileRouteHandler())
);
In the FileRouteHandler.ProcessRequest method I can rewrite the path from /static/picture.jpg to /a/b/c/picture.jpg. I then want to create a handler for static files. ASP.NET uses the StaticFileHandler for this purpose. Unfortunately, this class is internal. I have tried to create the handler using reflection and it actually works:
Assembly assembly = Assembly.GetAssembly(typeof(IHttpHandler));
Type staticFileHandlerType = assembly.GetType("System.Web.StaticFileHandler");
ConstructorInfo constructorInfo = staticFileHandlerType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
return (IHttpHandler) constructorInfo.Invoke(null);
But using internal types doesn't seem to be the proper solution. Another option is to implement my own StaticFileHandler, but doing so properly (supporting HTTP stuff like ranges and etags) is non-trivial.
How should I approach routing of static files in ASP.NET?
Why not use IIS to do this? You could create a redirect rule to point any requests from the first route to the second one before it even gets to your application. Because of this, it would be a quicker method for redirecting requests.
Assuming you have IIS7+, you would do something like...
<rule name="Redirect Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Redirect" url="/a/b/c/{R:1}" redirectType="Permanent" />
</rule>
Or, if you don't need to redirect, as suggested by #ni5ni6:
<rule name="Rewrite Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Rewrite" url="/a/b/c/{R:1}" />
</rule>
Edit 2015-06-17 for #RyanDawkins:
And if you're wondering where the rewrite rule goes, here is a map of its location in the web.config file.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<!-- rules go below -->
<rule name="Redirect Static Images" stopProcessing="true">
<match url="^static/?(.*)$" />
<action type="Redirect" url="/a/b/c/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
After digging through this problem for a few hours, I found that simply adding ignore rules will get your static files served.
In RegisterRoutes(RouteCollection routes), add the following ignore rules:
routes.IgnoreRoute("{file}.js");
routes.IgnoreRoute("{file}.html");
I've had a similar problem. I ended up using HttpContext.RewritePath:
public class MyApplication : HttpApplication
{
private readonly Regex r = new Regex("^/static/(.*)$", RegexOptions.IgnoreCase);
public override void Init()
{
BeginRequest += OnBeginRequest;
}
protected void OnBeginRequest(object sender, EventArgs e)
{
var match = r.Match(Request.Url.AbsolutePath);
if (match.Success)
{
var fileName = match.Groups[1].Value;
Context.RewritePath(string.Format("/a/b/c/{0}", fileName));
}
}
}
I came up with an alternative to using the internal StaticFileHandler. In the IRouteHandler I call HttpServerUtility.Transfer:
public class FileRouteHandler : IRouteHandler {
public IHttpHandler GetHttpHandler(RequestContext requestContext) {
String fileName = (String) requestContext.RouteData.Values["file"];
// Contrived example of mapping.
String routedPath = String.Format("/a/b/c/{0}", fileName);
HttpContext.Current.Server.Transfer(routedPath);
return null; // Never reached.
}
}
This is a hack. The IRouteHandler is supposed to return an IHttpHandler and not abort and transfer the current request. However, it does actually achieve what I want.
Using the internal StaticFileHandler is also somewhat a hack since I need reflection to get access to it, but at least there is some documentation on StaticFileHandler on MSDN making it a slightly more "official" class. Unfortunately I don't think it is possible to reflect on internal classes in a partial trust environment.
I will stick to using StaticFileHandler as I don't think it will get removed from ASP.NET in the foreseeable future.
You need to add TransferRequestHandler for handling your static files.Please see following answer
https://stackoverflow.com/a/21724783/22858

Resources