I've recently read one of Jeff's articles about XSS and it got me thinking about how to better protect the login cookies in my home cooked authentication system.
Basically what I do now is this(note, everything is configurable and currently set to true):
protected static string ComputeLoginHash(string passwordhash){
StringBuilder sb=new StringBuilder();
sb.Append(passwordhash);
if(CookieUseIP){
sb.Append(HttpContext.Current.Request.UserHostAddress);
}
if(CookieUseBase){
sb.Append(HttpContext.Current.Request.MapPath("/"));
}
if(CookieUseBrowserInfo){
sb.Append(HttpContext.Current.Request.UserAgent);
}
sb.Append(SiteName);
return ComputeHash(sb.ToString());
}
(note that passwordhash is made out of password, unique salt, and username).
Ok, so one of the questionable things I do is use the UserAgent string. Is there harm in doing this? Or browsers which will change their UserAgent string under normal operation(as in, without being updated)? My goal is basically for if an attacker gets a login cookie, for them to not be able to do anything with it. Would this help meet my goal or is it just overly cumbersome for the user? At the moment, the only info I store in the cookie plain text is the username.
First and foremost you should never write your own session handler. You are reinventing the wheel and it will be less secure.
If ComputeLoginHash() is producing a cookie value then you a big problem on your hands. An attacker can obtain the username/password hash from the database and then build a cookie value by passing it to a hash function. This would allow an attacker to login without the need to cracking a password. Effectively you are completely removing the protection provided by hashing passwords.
A cookie value must always be a cryptographic nonce, this value must expire (less than a day is good.). For added security enable http-only cookies which helps thwart xss. Also set the sts-header to enforce https and in turn take care of OWASP A9. Also,don't forget about session riding. Also there is absolutely no point in checking the user-agent because this is an attacker controlled variable.
Related
I have an unusual situation in which authentication isn't necessary but where learning the user-id via windows authentication under certain conditions would be useful.
To give some context, I want to be able to require windows authentication when the user-agent matches certain conditions, but not require authentication in other conditions. With only some familiarity with asp.net and iis I suspect I am missing an easy way of accomplishing this. So far I've looked into writing a module that checks the user-agent and then adds the WindowsAuthenticationModule if the conditions are met - but I can't figure out how to do this.
Any suggestions the best way to auth or not auth on the value of the user-agent?
If you setup IIS to use windows authentication, you should be able to do something like the following code snippet.
However, as you may guess from the comments in the code, I would advise against it. The User Agent is easily spoofed - so any authentication checks you do based on it can also easily be bypassed. The same holds true for pretty much anything that comes across in an http header (e.g., basing authentication on http referrer is also a bad idea).
string windowsUserName = null;
var currentContext = System.Web.HttpContext.Current;
//NOT SECURE - easily spoofed!
if (currentContext.Request.UserAgent == "Some special user agent")
{
if (!currentContext.User.Identity.IsAuthenticated
|| currentContext.User.Identity.AuthenticationType != "Windows")
{
throw new SecurityException(#"You are not authorized, but you can easily
hack this application by modifying the user agent that you send to the server.")
}
windowsUserName = User.Identity.Name;
}
So in short, even if the above works, don't do it. You really need to completely rethink how you are authenticating your application.
If, as you seem to indicate in the first sentence of your question, this is purely informational, then it may be ok (e.g. if it is just for debugging purposes). However, it would not be suitable e.g. for auditing or restricting access to any resources, and you must be extremely careful that this code doesn't get reused in any real security context.
I can actually see the verification token key generated by MVC3 framework in plain text when making a request to the server without ssl.
This key is stored in a cookie called: _RequestVerificationToken_Lw__
In mixed security environment it is actually possible to see this token in plain text sent to the server on the initial request to the non ssl site. This token is also static for the duration of the user's session. Then what's the use of having this token when it can easily be stolen by an attacker, because the cookie gets thrown around in plain text.
Shouldn't this cookie be marked as secure and never to be sent across in plain text? Or at the very least be regenerated on every request such that the secure information doesn't leak out of the ssl channel?
I'm talking about this block in MVC 3 AntiForgeryWorker class
private string GetAntiForgeryTokenAndSetCookie(HttpContextBase httpContext, string salt, string domain, string path)
{
string forgeryTokenName = AntiForgeryData.GetAntiForgeryTokenName(httpContext.Request.ApplicationPath);
AntiForgeryData token = (AntiForgeryData) null;
HttpCookie httpCookie = httpContext.Request.Cookies[forgeryTokenName];
if (httpCookie != null)
{
try
{
token = this.Serializer.Deserialize(httpCookie.Value);
}
catch (HttpAntiForgeryException ex)
{
}
}
if (token == null)
{
token = AntiForgeryData.NewToken();
string str = this.Serializer.Serialize(token);
HttpCookie cookie = new HttpCookie(forgeryTokenName, str)
{
HttpOnly = true,
Domain = domain
};
if (!string.IsNullOrEmpty(path))
cookie.Path = path;
httpContext.Response.Cookies.Set(cookie); //Ma, Why isn't this marked as "SECURE"
}
return this.Serializer.Serialize(new AntiForgeryData(token)
{
Salt = salt,
Username = AntiForgeryData.GetUsername(httpContext.User)
});
}
That's quite the inflammatory question title you have there.
The built-in MVC anti-forgery functionality is as secure as the application is configured to be. All cookies written to Response.Cookies will be automatically marked with the "secure" modifier if <httpCookies requireSSL="true" /> is set in Web.config (see MSDN docs). MVC's anti-forgery cookie also gets this behavior if this switch is set.
Combine this with other functionality like setting the HSTS header in your responses, and you're essentially providing a guarantee that the browser will never send sensitive data over plaintext channels.
Additionally, the anti-forgery system does allow storing custom data in the tokens, and you can receive a callback to verify the custom data when the token is validated. See AntiForgeryConfig.AdditionalDataProvider for more information.
With protection against CSRF attacks, an optimal solution is to always use SSL. Without SSL, yes, the nonce--as it is called--is vulnerable to a MITM attack. When using cookies to store the nonce, the cookie must be marked HTTP-only. This prevents JavaScript from reading the cookie. You should also render the nonce as an <input type="hidden" value="nonce"> tag within all <form>s in addition to a cookie.
Anyone with access to the browser itself would be able to read the nonce, and the only way to prevent a replay attack is to have nonces expire the first time after they are validated for the first time by the server. This approach can cause a terrible user experience when the user uses the back button and resubmits a request with the same nonce, however. Because you're using ASP.NET MVC's built-in anti-CSRF protection mechanism, it may not be easy to change its behavior to only allow a nonce to be used once. (EDIT: Thanks to Levi below for informing me that ASP.NET MVC actually makes this quite simple)
If you want better control over generating and validating the nonces then I suggest rolling your own implementation, as I did with my JuniorRoute framework. In fact, feel free to take a look at JuniorRoute's source code to see how I implemented it. It's too much code for a Stack Overflow post.
My Take
a) The form submission is deemed not forged based on comparison of
__RequestVerificationToken cookie &
__RequestVerificationToken form field.
The 2 values are some kind of symmetrically match and hence not same.
b) Cookie can never be marked default must-use-secure-channel by the framework because some applications do not use https.
c) The __RequestVerificationToken implementation is protection against CSRF & cannot help valid user from snooping into process memory:p.
So I'm very new with HMAC authentication and I really don't know what I'm doing nor reading atm.
I've been trying to understand the following articles / links / discussions properly:
How to implement HMAC Authentication in a RESTful WCF API
http://blogs.microsoft.co.il/blogs/itai/archive/2009/02/22/how-to-implement-hmac-authentication-on-a-restful-wcf-service.aspx
http://buchananweb.co.uk/security01.aspx
With that said I have a few questions:
Understanding the first link, if for example I have a loginAuthentication service created in .net and will be accessed from an iPhone app do I pass an unencrypted username (message) for this and should return just a true / false or should it return an encrypted string in which I will be using later on for other transactions (Delete, Insert services, etc)?
[ServiceContract]
public partial class LoginService
{
[OperationContract]
bool Authenticate(string username) {
// stuffs
}
}
With that said, after I verified the user, and this is where I get lost. Is it better that I save something in the database 'with a timestamp' (someone told me about this and I read some discussions about this too)? Or do I just return it with the encrypted message (dependent on the first question) so that everytime a request is made the timestamp is already attached?
a. And what do I do with that timestamp?
b. Is it going to be used once the message is sent again for another transaction?
Keys and secret message. The way I understood it is that the key will be the password of the user. So if the user sends his username I can open the message using the password of that user? This makes sense if the user already has a session and is just requesting to get data or requesting for a delete, insert, etc. Should it still be the same way if it's just authenticating the username and password of the user?
Thank you for your time!
The first thing I would like to mention is that the WCF Web Api was a beta project which is no longer being developed. It was replaced by ASP.NET Web API which is an awesome framework for developing RESTful services.
If you want to get a good idea how a RESTful service and authentication works the Netflix API would be a great place to start. They have a lot of documentation regarding the security portion and this helped me understand HMAC a lot more.
HMAC creates a hash using a secret key. The client and server both maintain a copy of the secret key so that they can generate matching hashes. This allows you to 'sign' a request which serves as both authentication (you know the person sending it is who they say they are), and message integrity (knowing the message they sent is the original message and has not been tampered with).
A signature is created by combining
1. Timestamp (unix epoc is the easiest to send in urls)
2. Nonce (a random number that can never be used twice to protect against someone re-using it)
3. Message (for a GET request this would be the URL, a POST would be the whole body)
4. Signature (the three previous items combined and hashed using the secret key)
Each of the above can be sent in the query string of the request, then the server can use the first 3 and their copy of the secret key to recreate the signature. If the signatures match then all is good.
In a RESTful API that is over plain HTTP (not using HTTPS over an ssl), I would sign every request sent because again this authenticates and provides message integrity. Otherwise if you just send an authentication token you know the user is authenticated but how do you know the message was not tampered with if you do not have a Message Digest (the HMAC hash) to compare with?
An easy way to implement the server-side checking of the signature is to override OnAuthorization for System.Web.Http.AuthorizeAttribute (Make sure not to use Mvc autorize attribute). Have it rebuild the signature just as you did on the client side using their secret key, and if it does not match you can return a 401. Then you can decorate all controllers that require authentication with your new authorize attribute.
Hopefully this helps clear up some of your confusion and does not muddy the water even further. I can provide some more concrete examples later if you need.
References:
Netflix Api Docs: http://developer.netflix.com/docs/Security#0_18325 (go down to the part about creating signatures, they also have a link which shows a full .NET example for creating the HMAC signature)
.NET class for creating HMAC signatures http://oauth.googlecode.com/svn/code/csharp/OAuthBase.cs
Netflix API Wrapper I wrote: https://bitbucket.org/despertar1318/netflix-api/overview
ASP.NET Web API: http://www.asp.net/web-api
Looking at your questions in turn
...do I pass an unencrypted username (message) for this and should return just a true / false or should it return an encrypted string in which I will be using later on for other transactions (Delete, Insert services, etc)?
If you just returned a boolean, you'd have no way to then match the authentication request to subsequent requests. You'll need to return some sort of authentication indicator, on a classic website this would be the session cookie, in your instance you want to pass a value that will act as shared key.
Is it better that I save something in the database 'with a timestamp'? Or do I just return it with the encrypted message so that everytime a request is made the timestamp is already attached?
Back to the session analogy, you want to store the key from question one somewhere (the database?) with a timestamp that indicates the life of the session/validity of the key. If it's forever then I wouldn't bother with the timestamp, if it's anything else you'll need something to say when it expires.
The way I understood it is that the key will be the password of the user. So if the user sends his username I can open the message using the password of that user? This makes sense if the user already has a session and is just requesting to get data or requesting for a delete, insert, etc. Should it still be the same way if it's just authenticating the username and password of the user?
This is where the HMACing happens. You have your shared secret, you have a message, this is how I usually combine it all together.
Use all of the message as the body of data to be hashed (that way you can be sure that someone's not just copied the hash and part of the message). Hash the body of the message using the key we shared in step one. You could salt this if wanted, I'd use the username.
Finally make sure the message contains a timestamp (UTC preferably), this way you can help prevent replaying the message later. The service that's responding to the message can compare the timestamp to what it thinks the time is. If it falls outside given bounds, fail the message. Because the timestamp will be part of the HMAC, someone can't just update the date and replay the message, the hashes won't match as soon as the message is tampered with.
I have an app with multiple subdomains, subone.parent.com, subtwo.parent.com.
I have a logon page at parent.com/login. When a user logs in I redirect them to the proper domain based on which one they are a member of. This works fine.
FormsAuthenticationTicket ticket = new FormsAuth...
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
cookie.Domain = subone.parent.com
Response.Cookies.Add(cookie)
This properly authenticates the user for subone.parent.com and not subtwo.parent.com. However I would like to do the following.
If the user goes back to parent.com, I would like to know that they are logged in and redirect them back to subone.parent.com.
Is there a best practice for accomplishing this? Or do I have to set another cookie for parent.com?
I'm working in asp.net mvc if it matters.
THanks!
You can share cookies across domains like you are trying to do, but its not straight forward, example here.
Another options is to set the cookie to be ".parent.com" rather than specifying the sub-domain explicitly and use the cookie store the details of the sub-domain. Then you can access the cookie from any of your sub-domains (and parent assuming its www.parent.com).
If your using MVC, you can pretty easily create a custom filter and add to the www.parent.com controllers to check for the existence of the cookie, and if so redirect to the sub domain the cookie specifies. Further details of filters here.
I would set the cookie for the explicit domain as you have there because that maintains any security information within that specific domain's cookie. You can also add a non-encrypted cookie at the *.parent.com level that holds information about which domains have been authenticated. There is no real way to tie this together though without using maybe timestamps and having a logical connection between the applications (ie - sub2 has a session timeout of 20 minutes so if the domain + valid timestamp occur in the parent cookie it would be valid, however this is business logic).
I'm not sure the reasoning behind the disconnect between domains, but you might actually prefer to have a single cookie that has encrypted text behind encrypted text. EG:
1) Sub1 logs in, sets the parent.com cookie as valid. Sends a piece of user data to an authentication web service.
2) The authentication service recognizes sub1 as the sender, encrypts the user data, and adds it to a custom cookie object.
3) The custom cookie object constructs a composite string on a unique split character (or sequence) and makes it available to the service method.
4) The service, using the forms encryption, encrypts the entire ticket and sends it back to the original login.
That way each server would be able to unencrypt the global ticket, but each piece of data would be encrypted using a common algorithm but a server based salt. So if sub2 attempts to read the cookie data from sub1, it gets the encrypted version rather than raw data.
you could share the same session on all subdomains. That is the code we use to accomplish that :-)
void MasterPage_Unload(object sender, EventArgs e)
{
///ASP.NET uses one cookie per subdomain/domain,
///we need one cookie for _all_ subdomains.
if (Context.Response.Cookies["ASP.NET_SessionId"] == null)
return;
var sessionCookie = new HttpCookie("ASP.NET_SessionId", Context.Session.SessionID);
sessionCookie.Domain = ".yourdomain.com" ;
Context.Response.SetCookie(sessionCookie);
}
inside the Page_Load method is:
Unload += MasterPage_Unload;
it works great :-)
robert
We have an application that does single sign-on using a centralized authentication server (CAS). We'd like to do single sign-out, such that if the user logs out of one application (say a front-end portal), the user is automatically signed out of all applications using the same single sign-on ticket.
The expectation would be that each application would register a sign-out hook (URL) with the CAS at the time of logon to that application. When the CAS receives the sign out request from one of the applications, it invokes the sign-out hook for all the application sharing the SSO ticket.
My question is this: is there a way to abandon an InProc session from a different session? I presume, since the HTTP request will be coming from the CAS server, that it will get its own session, but it is the session of the user that I want to terminate. I have pretty good idea of how to do this using a separate session state server, but I'd like to know if it is possible using InProc session state.
Haha, well... It looks like you can. I was wondering myself if there was any way to do this, turns out, there is.
When you use InProc, the InProcSessionStateStore (internal class) persist the session state in an internal (non public) cache. You can access this cache through reflection and remove the session state manually.
using System;
using System.Reflection;
using System.Web;
object obj = typeof(HttpRuntime).GetProperty("CacheInternal",
BindingFlags.NonPublic | BindingFlags.Static)
.GetValue(null, null);
if (obj != null)
{
MethodInfo remove = obj.GetType()
.GetMethod("Remove", BindingFlags.NonPublic | BindingFlags.Instance,
Type.DefaultBinder, new Type[] { typeof(string) }, null);
object proc = remove.Invoke(obj, new object[] { "j" + state.SessionID });
}
The end result is, that the next request will take on the same SessionID, but the HttpSessionState will be empty. You'll still get the Session_Start and Session_End events.
After doing a bit of digging around and considering the answers provided so far I've come up with an alternative that lets me continue to use InProc session. Basically, it consists of extending the HttpModule that already handles single sign-on to detected CAS sign outs and redirect the browser to the application sign out page.
Outline:
Sign-On:
For each new single sign-on request, create a new SSO cookie and encode a unique value in it to identify the session (not the session id, so it isn't leaked).
Construct the the sign-out callback url, encoded with the identifier, and register it with the CAS server.
Sign-Out:
When a sign-out request is received from the CAS server, decode the identifier and store it in an application-wide cache. This needs to be pinned in the cache at least long enough for the session to expire naturally.
For each request, look for the SSO cookie and check its value against the cached, signed-out session identifiers. If there is a hit, remove the SSO cookie and redirect the browser to the application's sign-out url.
For each sign-out, check to see if there is an SSO cookie, if so, forward the sign-out request to the CAS. In any event, abandon the user's session, and sign them out of the application.
Page_Load:
Check for the presence of the SSO cookie. If there isn't one, redirect to the sign out page.
No can do.
http://forums.asp.net/p/416094/416094.aspx#416094
With InProc SessionState, you won't be able to access the data... With StateServer, you still will have a sticky scenario trying to access the correct API to remove the session.
You will most likely want to use a database backed state solution like the pre-packaged SqlServer state provider or a third party solution like DOTSS: http://codeplex.com/dotss
With the database backed solution, you will be able to lookup the state record in a table by session id and mark it as completed. These techniques will vary based on the provider you choose.