How to secure the ASP.NET_SessionId cookie? - c#

I have set the .ASPXAUTH cookie to be https only but I am not sure how to effectively do the same with the ASP.NET_SessionId.
The entire site uses HTTPS so there is no need for the cookie to work with both http and https.

To add the ; secure suffix to the Set-Cookie http header I simply used the <httpCookies> element in the web.config:
<system.web>
<httpCookies httpOnlyCookies="true" requireSSL="true" />
</system.web>
IMHO much more handy than writing code as in the article of Anubhav Goyal.
See: http://msdn.microsoft.com/en-us/library/ms228262(v=vs.100).aspx

Here is a code snippet taken from a blog article written by Anubhav Goyal:
// this code will mark the forms authentication cookie and the
// session cookie as Secure.
if (Response.Cookies.Count > 0)
{
foreach (string s in Response.Cookies.AllKeys)
{
if (s == FormsAuthentication.FormsCookieName || "asp.net_sessionid".Equals(s, StringComparison.InvariantCultureIgnoreCase))
{
Response.Cookies[s].Secure = true;
}
}
}
Adding this to the EndRequest event handler in the global.asax should make this happen for all page calls.
Note: An edit was proposed to add a break; statement inside a successful "secure" assignment. I've rejected this edit based on the idea that it would only allow 1 of the cookies to be forced to secure and the second would be ignored. It is not inconceivable to add a counter or some other metric to determine that both have been secured and to break at that point.

Going with Marcel's solution above to secure Forms Authentication cookie you should also update "authentication" config element to use SSL
<authentication mode="Forms">
<forms ... requireSSL="true" />
</authentication>
Other wise authentication cookie will not be https
See: http://msdn.microsoft.com/en-us/library/vstudio/1d3t3c61(v=vs.100).aspx

Found that setting the secure property in Session_Start is sufficient, as recommended in MSDN blog "Securing Session ID: ASP/ASP.NET" with some augmentation.
protected void Session_Start(Object sender, EventArgs e)
{
SessionStateSection sessionState =
(SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");
string sidCookieName = sessionState.CookieName;
if (Request.Cookies[sidCookieName] != null)
{
HttpCookie sidCookie = Response.Cookies[sidCookieName];
sidCookie.Value = Session.SessionID;
sidCookie.HttpOnly = true;
sidCookie.Secure = true;
sidCookie.Path = "/";
}
}

It is also worth considering:
Using cookie prefixes
__Secure-, which signals to the browser that the Secure attribute is required.
__Host-, which signals to the browser that both the Path=/ and Secure attributes are required, and at the same time, that the Domain attribute must not be present.
A good article on why this helps
https://check-your-website.server-daten.de/prefix-cookies.html
Renaming your cookies
Instead of using names that clearly identify programming language.
e.g
ASP.NET_SessionId = __Secure-SID
Using samesite settings
sameSite="Lax"
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
Make cookie https secure
requireSSL="true"
SECURE EXAMPLE
<sessionState cookieless="false" cookieName="__Secure-SID" cookieSameSite="Lax" />
<httpCookies httpOnlyCookies="true" sameSite="Lax" requireSSL="true" />

Adding onto #JoelEtherton's solution to fix a newly found security vulnerability. This vulnerability happens if users request HTTP and are redirected to HTTPS, but the sessionid cookie is set as secure on the first request to HTTP. That is now a security vulnerability, according to McAfee Secure.
This code will only secure cookies if request is using HTTPS. It will expire the sessionid cookie, if not HTTPS.
// this code will mark the forms authentication cookie and the
// session cookie as Secure.
if (Request.IsSecureConnection)
{
if (Response.Cookies.Count > 0)
{
foreach (string s in Response.Cookies.AllKeys)
{
if (s == FormsAuthentication.FormsCookieName || s.ToLower() == "asp.net_sessionid")
{
Response.Cookies[s].Secure = true;
}
}
}
}
else
{
//if not secure, then don't set session cookie
Response.Cookies["asp.net_sessionid"].Value = string.Empty;
Response.Cookies["asp.net_sessionid"].Expires = new DateTime(2018, 01, 01);
}

If the entire site uses HTTPS, your sessionId cookie is as secure as the HTTPS encryption at the very least. This is because cookies are sent as HTTP headers, and when using SSL, the HTTP headers are encrypted using the SSL when being transmitted.

Related

Form based Authentication using C# failing for the application

I am trying to implement form authentication in asp.net for one of my applications under default website in IIS to prevent anonymous users from accessing the website and I am facing some issues when I try to do this
I have the below settings done in web.config file for this application to implement form-auth. I have included the machine key tag, authentication tag for form-based auth and authorization tag to deny anonymous user
<authentication mode="Forms">
<forms name="cookiename" cookieless="UseCookies" path="/" protection="None" timeout="30" />
</authentication>
<authorization>
<deny users="?"/>
</authorization>
In the IIS UI,
.NET Authorization rules have Anonymous Users with Deny Rule and All Users (of Inherited type) with Allow rule
Also, under Authentication UI, I have Anonymous Authentication disabled and From based Authentication enabled (Do we need to disable the Anonymous authentication here?)
When user logs into our portal, a cookie gets assigned to this user so that IIS can keep track of user's identity and gets navigated to the application (Aspx page) for which I am trying to implement form authentication. I am using webclient to post some request to perform some validation before navigating to this application and I am using CookieAwareWebClient to handle the cookies.
public class CookieAwareWebClient : WebClient
{
public CookieAwareWebClient()
{
CookieContainer = new CookieContainer();
}
public CookieContainer CookieContainer { get; private set; }
protected override WebRequest GetWebRequest(Uri address)
{
var request = (HttpWebRequest)base.GetWebRequest(address);
request.CookieContainer = CookieContainer;
return request;
}
}
Using the CookieAwareWebClient, I am trying to post the request. Here, authenticationCookie is the cookie which gets created right after user login and I am setting this value to myCookie object so that IIS can keep track of user's identity
using (CookieAwareWebClient client = new CookieAwareWebClient())
{
Cookie myCookie = new Cookie();
myCookie.Name = authenticationCookie.Name;
myCookie.Value = authenticationCookie.Value;
myCookie.Domain = URL.Host;
client.CookieContainer.Add(myCookie);
client.UseDefaultCredentials = true;
byte[] responsebytes = client.UploadValues(URL, "POST", reqparm);
}
When I try to post the request to the URL, the identity of the user is lost somewhere even though the web client has cookie container which holds the cookie value, and I am getting 401 error
Remote server returned an error (401) Unauthorized
Because of this, IIS thinks the user as anonymous and hence not able to load the application. Is this the right way to handle form-auth to prevent anonymous users from accessing website and where am I going wrong? I am using IIS 8.5 version

Why is the cookie I set in the server not showing in the client?

I am not able to read a cookie on the client that I set on the server.
I am using C# / MVC and am handling the cookie logic outside of the controller.
I pass in the Response, and can see it is set after I call this method from the controller, but once the response gets sent to the client, this cookie is not present.
In the controller, I am calling the SetCookie method like this:
CookieHelper.SetCookie(Response);
I have also tried to pass in System.Web.HttpContext.Current.Response to no avail.
The method I am calling is:
public class CookieHelper {
public static void SetCookie(HttpResponseBase Response)
{
HttpCookie cookie = new HttpCookie("jayscookie");
cookie.Value = "a much longer string than this";
cookie.Expires = DateTime.Now.AddDays(7);
Response.Cookies.Add(cookie);
}
}
I have tried HttpResponseWrapper as well.
I have also tried System.Net.Cookie instead of HttpCookie
My IIS Setting for Session State is represented by this line in the Web.Config:
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" cookieless="false"/>
I have tried without cookieless="false"
The Cookie Settings section of this application in IIS are:
Mode: UseCookies
Name: ASP.NET_SessionId
Time-out: 20 (minutes)
I have visited the following and tried solutions:
Why is my cookie not set?
Cookies not getting set in c#
Why are the cookies not being set when doing a Redirect?
Cookie not setting in asp.net mvc
How to set cookie value?
HttpWebRequest/Reponse return cookies sent
As well as several microsoft documentation pages, such as:
https://msdn.microsoft.com/en-us/library/ms178194.aspx
Is there anything I am missing, or any basic concept I am obviously not taking into account? Setting cookies on the client works fine, just not setting them on the server.
The cookie.Value string was a different value in my application. The value in the application was far too big, and exceeded the limit for cookie size. I will have to figure out a different approach, but that was the issue.
The size limit for cookies is around 4KB, for any who may find this information useful.
Server side can t access client side directly and vice versa. Sorry for poor explain but i'm in train

User.Identity.IsAuthenticated is always false

I am using Membership provider but how do I get my username and password to login/signin? When I check to see with this code:
if (User.Identity.IsAuthenticated)
// this code always returns false
I have this before when the user uses asp:Login to login:
if (Membership.ValidateUser(email, password))
{
// this is true but what am I missing here to make above not be false?
The so called duplicate question/answer uses SetAuthCookie which according to this (System.Web.HttpContext.Current.User.Identity.IsAuthenticated fails sometimes) causes issues and I need to avoid it.
Use code like below to make it work. The method RedirectFromLoginPage will create the authentication cookie as well as redirect the user to the original page or the default URL (i.e. home page) defined in web config.
if (Membership.ValidateUser(email, password))
{
// Log the user into the site
FormsAuthentication.RedirectFromLoginPage(email, false);
}
Also, make sure that forms authentication is enabled in web config. At a minimum at least set mode="Forms" if not other settings under authentication.
<authentication mode="Forms">
<forms loginUrl="member_login.aspx"
defaultUrl="index.aspx" />
</authentication>

HttpContext.Current.User.Identity.IsAuthenticated returns false

I have a strange issue.
I have a page with the following code.
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
Server.Transfer(#"~/Views/Public/Unauthorised.aspx");
return;
}
For some reason, with one user (and we've narrowed it down to his single machine and windows logon profile), IsAuthenticated always returns false. Even though he is logged into the website, and can navigate to other pages that require authenticated user. Except this one page.
We checked that the machine accepts all cookies and we still get the same issue. I'm not sure where to go from here... any suggestions?
There are at least two known cases that can make this behavior.
First case when you have set requireSSL="true" on the Authentication session on web.config and you call that function from a non secure page. So double check if the page is secure or not, if you use the requireSSL="true"
Debug.Assert(Request.IsSecureConnection, "The IsAuthenticated will fail.");
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
Server.Transfer(#"~/Views/Public/Unauthorised.aspx");
return;
}
Second case when you do not have set the domain="site.com" again on authentication session inside the web.config, and you try to request a cookie the one time from the www.yoursitename.com and the other from yoursitename.com. In that case the authentication cookies are different and it will fail. So set that parameter among others on web.config.
<authentication mode="Forms">
<forms domain="yoursitename.com" />
</authentication>

EnableCrossAppRedirects - Where is the cross-domain feature documented?

Here an interesting feature of ASP.NET FormsAuthentication explained in this SO answer: How do you pass an authenticated session between app domains
Quick summary; you can create two ASP.NET websites with the same encryption keys. WebsiteA can create a formsauth token, and redirect to WebsiteB with the token in the querystring (or POST body). Switch on EnableCrossAppRedirects in WebsiteB and ASP.NET detects the token and creates the formsauth cookie. In code:
FormsAuthentication.RedirectFromLoginPage("alice", true);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket("Alice", true, 30);
string encrypted = FormsAuthentication.Encrypt(ticket);
Response.Redirect("http://siteb.dev/Secure/WebForm1.aspx?" + FormsAuthentication.FormsCookieName + "=" + encrypted);
Sounds like a great feature, but where is it documented? I'd feel a bit uneasy using a undocumented feature.
Where I've looked - no mention of this feature in any of the MSDN reference. I thought maybe RedirectFromLoginPage would build a redirect like my code above, it doesn't.
EnableCrossAppRedirects - "is checked within the RedirectFromLoginPage method when the redirect URL does not point to a page in the current application. If EnableCrossAppRedirects is true, then the redirect is performed"
Forms Authentication Across Applications - some advice on setting the machine keys so that a cookie created on a sub-domain, nothing about EnableCrossAppRedirects
forms Element for authentication
Having looked at reflector there is a (somewhat undocumented) feature of forms Authentication. When EnableCrossAppRedirects is enabled .NET will, in addition to looking for the auth cookie, attempt to extract the forms authentication "cookie" from either the form post or the query string. This code is embedded in the FormsAuthentication class in the ExtractTicketFromCookie method, where it can clearly been seen trying to find the authentication cookie in the request data.
if (FormsAuthentication.EnableCrossAppRedirects)
{
text = context.Request.QueryString[name];
if (text != null && text.Length > 1)
{
if (!cookielessTicket && FormsAuthentication.CookieMode == HttpCookieMode.AutoDetect)
{
cookielessTicket = CookielessHelperClass.UseCookieless(context, true, FormsAuthentication.CookieMode);
}
try
{
formsAuthenticationTicket = FormsAuthentication.Decrypt(text);
}
catch
{
flag2 = true;
}
if (formsAuthenticationTicket == null)
{
flag2 = true;
}
}
if (formsAuthenticationTicket == null || formsAuthenticationTicket.Expired)
{
text = context.Request.Form[name];
if (text != null && text.Length > 1)
{
if (!cookielessTicket && FormsAuthentication.CookieMode == HttpCookieMode.AutoDetect)
{
cookielessTicket = CookielessHelperClass.UseCookieless(context, true, FormsAuthentication.CookieMode);
}
try
{
formsAuthenticationTicket = FormsAuthentication.Decrypt(text);
}
catch
{
flag2 = true;
}
if (formsAuthenticationTicket == null)
{
flag2 = true;
}
}
}
}
Therefore if you enable EnableCrossAppRedirects on both applications, then the first application is authorised to redirect to the external site, and the second application will automatically read in the authentication cookie from the request. You just need to engineer it so that the return login URL either posts the cookie data or sends it in the querystring. You also need to be sure that either the machine keys are synchronised, or that the cookie is encrypted using the external apps machine key (by the first app). It seems by default .NET will send the encrypted authentication cookie in the querystring for you and asume your machine keys are in sync (see MSDN quote below).
Here's some more info on MSDN .
If the CookiesSupported property is true, and either the ReturnUrl
variable is within the current application or the
EnableCrossAppRedirects property is true, then the
RedirectFromLoginPage method issues an authentication ticket and
places it in the default cookie using the SetAuthCookie method.
If CookiesSupported is false and the redirect path is to a URL in the
current application, the ticket is issued as part of the redirect URL.
If CookiesSupported is false, EnableCrossAppRedirects is true, and the
redirect URL does not refer to a page within the current application,
the RedirectFromLoginPage method issues an authentication ticket and
places it in the QueryString property.
There is a big warning about the impact on security. EnableCrossAppRedirects is a security setting which prevents ASP.NET login controls from redirecting to an external return URL (another web application). With this setting enabled it can be exploited in some forms of attack - a user is sent to the official login page, but on login is redirected to a different application which they may believe is the same. This is why it's disabled by default.
One way to help mitigate this when enabling the feature is as follows:
To improve security when using cross-application redirects, you should
override the RedirectFromLoginPage method to allow redirects only to
approved Web sites.
You also need to ensure the redirect request is served over SSL to protect the "cookie" in transit, as anyone intercepting would be able to gain control of the account.

Categories