ASP.net Web Pages stack comes with Simple Membership, of which the best explanation is Matthew Osborn's Using SimpleMembership. SimpleMembership is a lightweight user/login/membership system which allows a cookie to be used for "remember me" login purposes. I would like to improve the the security of the cookie by forcing the cookie to be httpOnly and be a secure (https only) cookie. How can I do this?
Update: #Darin Dimitrov pointed out that httpOnly is session only which is not what I want.
If you don't use a persistent cookie that would mean that the cookie is no longer stored on the client computer which kind of defeats the whole purpose of the remember me functionality. HttpOnly cookies are stored in the memory of the browser but only for the given session. In order to improve security make sure that the cookie is set with the secure flag which indicates that this cookie will be transmitted only over an encrypted connection.
If that applies to you, I suggest to have a look at the source code and see how it's done:
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/9c98c6e9a150#src%2fWebMatrix.WebData%2fWebSecurity.cs
Now that I've checked myself... :) this is how they look as of now:
public static bool Login(string userName, string password, bool persistCookie = false)
{
VerifyProvider();
bool success = Membership.ValidateUser(userName, password);
if (success)
{
FormsAuthentication.SetAuthCookie(userName, persistCookie);
}
return success;
}
public static void Logout()
{
VerifyProvider();
FormsAuthentication.SignOut();
}
Where Membership provides about the same API as the System.Web.Security.Membersip -- which is actually swapped with WebMatrix.WebData.SimpleMembershipProvider at startup.
Basically if you want a custom auth cookie mechanism you've got to implement your own login & logout logic. The web has plenty of samples in that direction.
I hope this helps a bit.
Good luck! :)
Related
I'm building a simple CMS using ASP.NET MVC 5 and Entity Framework 6. I have 2 sites: Public and Admin. Public site to diplay all the content and Admin site to manage all the content.
I only need a single Admin account to handle all the content in the Admin site.
I'm thinking to use a session to keep the logged in user data and check for the session details when accessing an authorized page.
Keep the user data in a session.
var obj = db.UserProfiles.Where(a => a.UserName.Equals(objUser.UserName) && a.Password.Equals(objUser.Password)).FirstOrDefault();
if (obj != null)
{
Session["UserID"] = obj.UserId.ToString();
Session["UserName"] = obj.UserName.ToString();
return RedirectToAction("UserDashBoard");
}
Check before accessing an authorized page.
public ActionResult UserDashBoard()
{
if (Session["UserID"] != null)
{
return View();
} else
{
return RedirectToAction("Login");
}
}
So with this approach I wouldn't need to implement advance ASP Identity functions for the authorization.
Is this approach correct and would there be any downsides using this approach?
NEVER EVER EVER EVER EVER use session for authentication. It's insecure for starters, and it won't survive a loss of session (which IIS can kill at any time, for any reason). Session cookies are not encrypted, so they can be grabbed and used easily (assuming a non-encrypted link, even if you use HTTPS for authentication pages).
Another issue is that you are doing your authentication way too late in the pipeline. OnAuthenticate runs at the very beginning of the pipeline, while you action methods are towards the end. This means that the site is doing a lot of work it doesn't have to do if the user is not authorized.
I'm not sure why you are so against using Identity, the MVC basic templates already roll a full identity implementation for you. You don't have to do much.
The downside is that you have to write it all yourself anyway. You already need role-based authorisation and have to write cludges. Identity already have this implemented and tested for you. Also keeping information in session is not very secure.
And you don't need to implement much yourself anyway. Yes, there are lot of functionality that you'll probably won't need, but just don't use it.
Don't build your own authorisation system. Since you ask this question, you are probably not qualified enough to make it secure.
I have an interesting problem with trying to keep track of expired WIF authentication sessions/cookies.
As a bit of background: the site is MVC 3, uses Windows Identity Foundation (WIF) that has a trust with an ADFS server as an STS. The entire site is protected by SSL. The STS has the token expiry set to 60 minutes.
When a user signs out manually, we just simply call the SignOut method on the FedAuth module:
FederatedAuthentication.WSFederationAuthenticationModule.SignOut(false);
This of course removes the FedAuth cookies, but here's where the problem starts. If I capture those cookies with Fiddler, I can re-present them to the site within their expiry time and still be treated as logged in.
I realise that this is being performed from a privileged position of the browser having accepted fiddler as a proxy... but the customer is worried that those auth cookies not actually being expired presents a significant security risk. They're are not convinced that SSL protects the site sufficiently, and that if an attacker could execute an MITM attack, they could use those cookies after the user thinks they have logged out.
I have explained that if they are vulnerable after log out, they are vulnerable during log in, but they don't care...
So I have looked for ways to be sure that once a user logs off, the fedauth cookies associated with that logon session are treated as expired. The WIF handlers don't seem to have a built in mechanism for tracking expired tokens, and I have not found anything else related to this.
I guess that this is in fact a wider problem -> how to detect expired cookies in general? A valid cookie is a valid cookie!
The obvious solution is to track those cookies after logout somehow, but I'd like to avoid the custom code route if possible; as a noob, a lot of the security literature says to avoid custom coding any kind of session mechanics, as you will probably get it wrong!
Is anyone aware of any standard solutions in ASP.NET to this problem?
Thanks in advance.
You don't without keeping a server-side list of the tokens recently revoked. This is why normally we rely upon an inherent expiration as well as HTTPS to prevent the token from being leaked/stolen.
I was tasked with a similar request by our security team. I opted to store the asp.net session id in the OWIN cookie and on each request that contained a session id in the cookie I verify it matches the active session's Id.
Store session id in the cookie (adapted from this answer) at the end of the first request that is authenticated and doesn't already have the session id in the cookie:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
bool authenticated = User.Identity.IsAuthenticated;
var sessionGuid = (User as ClaimsPrincipal).FindFirst("sessionID")?.Value;
//put the SessionID into the cookie.
if (authenticated && string.IsNullOrEmpty(sessionGuid))
{
var id= Session.SessionID;
//update the guid claim to track with the session
var authenticationManager = HttpContext.GetOwinContext().Authentication;
// create a new identity from the old one
var identity = new ClaimsIdentity(User.Identity);
// update claim value
identity.RemoveClaim(identity.FindFirst("sessionID"));
identity.AddClaim(new Claim("sessionID", id));
// tell the authentication manager to use this new identity
authenticationManager.AuthenticationResponseGrant =
new AuthenticationResponseGrant(
new ClaimsPrincipal(identity),
new AuthenticationProperties { IsPersistent = true }
);
}
}
Then on each future request if I find a session in the cookie compare it to active session. If they don't match then logout:
protected override void OnActionExecuting( ActionExecutingContext filterContext)
{
var claim = (User as ClaimsPrincipal).FindFirst("sessionID")?.Value;
//does the owin cookie have a sessionID?
if (!string.IsNullOrEmpty(claim))
{
string session = Session.SessionID;
//does it match the one stored in the session?
if(session != claim)
{
//no? log the user out again..
Session.Abandon();
//redirect to logged out page
this.Request.GetOwinContext().Authentication.SignOut();
//tell them its over..
Response.Write("Expired Session");
Response.End();
}
}
base.OnActionExecuting(filterContext);
}
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.
Is there any good reason why ASP.NET's session state cookie and the Forms Authentication cookie are two separate cookies? What if I want to "tie" them to each other? Is it possible in an elegant way?
Right now, I am stuck with the following solution, which works, but is still ugly:
[Authorize]
public ActionResult SomeAction(SomeModel model)
{
// The following four lines must be included in *every* controller action
// that requires the user to be authenticated, defeating the purpose of
// having the Authorize attribute.
if (SomeStaticClass.WasSessionStateLost/*?*/) {
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
// ...
}
#RPM1984: This is what happens:
[HttpPost]
public ActionResult Login(LoginModel loginModel)
{
if (/* user ok */)
{
// ...
Session["UserID"] = loginModel.UserID;
Session["Password"] = loginModel.Password;
// ...
}
else
{
return View();
}
}
And it doesn't take much guessing to know what WasSessionStateLost does.
Session != Authentication
The session state cookie tracks the user's activity during a browser session.
The forms authentication cookie tracks the user's authenticated activity during a given time period, specified by the expiration date of the ticket and whether or not you have created a persistent cookie (e.g "Remember Me" checkbox).
You shouldn't be touching the session cookie itself, and all it contains is an identifier to tie the client session (browser) to the server.
If you need to access the session, use HttpContext.Current.Session.
What exactly are you trying to "tie" together?
What does SomeStaticClass.WasSessionStateLost do?
I'll start with a solution, then an explanation followed by a recommendation.
Create a custom authorization attribute:
Since your application defines Authorized as follows:
Logged in
Must have values in Session["UserID"] and Session["Password"]
you need to define your own AuthorizationAttribute
public class AuthorizedWithSessionAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if(httpContext.Request.IsAuthenticated &&
Session["UserID"] != null && Session["Password"] != null)
return true;
// sign them out so they can log back in with the Password
if(httpContext.Request.IsAuthenticated)
FormsAuthentication.SignOut();
return false;
}
}
Replace all your [Authorize] attributes with [AuthorizedWithSession] and you shouldn't need to put session check code in your controllers.
I don't know enough about your application, but saving passwords in session (even worse in plain text) is not a secure thing to do.
In addition, as RPM1984 said, the session cookie and authentication cookie are separate.
Explanation:
Think of the session as a bucket of info (on the server side) with your name on it. ASP.NET can take and put stuff in that bucket. ASP.NET gives you a name, your session id, and puts it on the bucket so it can know which one is yours.
The authentication cookie tells ASP.NET that you're authenticated and stores your authentication name in it. The authentication name is usually set by the developer of the application and is usually a unique key (think primary key in a DB) to separate you from the other users.
Recommendation to be more secure:
Encrypt the passwords before your store them. This is not total security, but it beats storing passwords in plain text and of course, if someone were to get a hold of the encryption key, they can crack the passwords.
Rather than using session, which is short lived you could cache in the System.Web.Cache. With this you can add events that are called before an entry is removed and decide accordingly if the cache should be cleared. You can set a higher time-out value on that, with the added bonus that you're not storing the clear text password in a file or database anywhere. Another bonus is you won't be vulnerable to session hijacking.
Of course if the application pool recycles the cache is gone, and as it's in memory load balanced machines will be out of sync, but Velocity or another distributed, out of process cache system would solve that.
It's not perfect though, entries may be dumped due to pressure on the cache, and of course you know this is all a bad idea anyway, so I'll skip that lecture.
So I know the best practice would be to run my Flex app over ssl along with implementing other forms of security however that isn't an option at this point (for monetary reasons and the app simply doesn't need that much security otherwise my sponsors would pay for it). However, I would like to implement some form of security and I'm wondering whether it's even worth it when I don't have ssl to protect the transactions.
So my setup is that I have a ASP.Net server side with a Flex UI. Right now the UI is the only thing that protects access to the sever: the server doesn't do any sort of verification during each request, it just assumes the person is allowed to do it. Obviously, anybody could write a program to generate posts (even if I could use SSL it would be like swiss cheese). Like I said before, security isn't a big deal, this is an internal app and it's nothing critical, however I do believe in doing things right. Would keeping the user info in session be a viable option and then verifying that the given user has permission, etc. Perhaps some sort of token system?
What would your preferred method of protecting this setup be?
...and no, I won't give you the url :)
ASP.NET Session itself is token based security and yes you can easily implement that by doing
[WebMethod(true)]
and yes, any web method requires login to be done first, it should call User.IsAuthenticated, that verifies the session token.
You can easily implement form authentication (let web.config empty, you can use FormsAuthentication in code).
for example,
[WebMethod(true)]
public string DoLogin(
string username,
string password)
{
//.. do your verification
FormsAuthentication.SetAuthCookie(username,false);
return "Login Sucessful";
}
[WebMethod(true)]
public string ChangePassword(
string oldPass,
string newPass)
{
// verify user is logged on or not..
if(!User.IsAuthenticated)
return "Please Login";
// The code below is secure, only
// authenticated user will go through below
// change pass...
return "Password Changed Successfully.";
}
We developed many Flex+ASP.NET sites, we did exactly same thing, but instead of return "string" we usually return a class like following...
public class WSResult<T>{
public bool Successful;
public string Message;
public T Result;
public T[] Results;
}
The convention is simple, if method was successful then you return Success = true, and depending upon whether you want to return an array of items or just single item, you can return either Results or Result. In case if there has been any error or unathorized access you can set Successful=false and set Message as detailed string. As per following example.
[WebMethod(true)]
public WSResult<BusinessUser> DoLogin(
string username,
string password)
{
try{
BusinessUser user = BusinessUser.GetByUsername(username);
if(user==null)
throw new Exception("User not found");
if(user.Password != password)
throw new Exception("Password did not match");
return new WSResult<BusinessUser>{ Result=user };
}catch(Exception ex)
{
// this will even catch any DAL exceptions or any system error as well
// Log Exception... somewhere for tracking...
return new WSResult<BusinessUser>{ Successful=false, Message = ex.Message };
}
}
Unfortunately, I know diddly squat about flex, but I think I can help anyway. I think you have two reasonably good options.
First though, we need to clarify something... Are you saying the server doesn't do any authorization? Does it at least have the ability to authenticate a user? Do you have any control over the server code? If not, I don't think the following suggestions will help. I'm not sure how you're supposed to secure a server with just client side code. Maybe there is a way, but I can't think of it.
1) Use HTTP digest authentication. This requires that the server is configured to understand it and that there is support in the flex api for adding the appropriate auth header to the HTTP request. The server authenticates the user by his password and can check what operations can be performed by said user against some authorization mechanism.
2) Follow the guidelines in this article to implement the authentication scheme that many atom publishing endpoints use. The flex api will have to provide some support for this, maybe there is an existing third party lib though. If you can get access to the HTTP headers you should be able to implement the rest.
Good luck.
How are you commuicating with the server SOAP. REST etc?
If it is SOAP have a look at the answer to this question
General Password Security && Implementation in Actionscript 3
and here's a link how to add the header to the SOAP message
How to add a "flat" message header to a flex web service call?
Hope this helps
Jon