I am working on a project which is based on asp.net mvc, jquery and web api.
Currently I am setting sessions which are being used to authenticate users.
When an ajax call to web api is made I check if the user is currently logged in then respond him with the data else with an error.
protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (SessionManager.IsUserLoggedIn == false)
{
return false;
}
return true;
}
The POCO I'm materializing from login stored procedure contains almost 10 GUID(UserGUID, DeptGUID etc) and int properties.
Now the problem is that I need to completely remove session from application and use cookies to do that authentication process. I've read some solution which propose by using web form authentication I should put all my properties in the cookie (can be encrypted) and send this authentication cookie within request header with every request to the sever. Although I am using GUIDs but some critical flags are in integer form. Will it be wise to put that information out there in client browser?
Also on front end scripts I am making some decisions based upon the values of the properties which are stored in cookies.
How to ensure that the cookie I am receiving has not been tampered with?
What will be the most efficient and secure way to implement cookie based authentication in my scenario (web api, asp.net mvc )
Related
We have a page with several forms. Each has its own #Html.AntiForgeryToken(). On my local machine everything is great.
We deployed to Azure (PAAS), but the __RequestVerificationToken is not being created on every request. Sometime it is there and sometime I get the The required anti-forgery cookie is not present and rarely I get the tokens do not match error.
I'm completely clueless at this point. I can't figure out if there's something wrong in our code or on Azure environment? No ajax in these forms.
We have added the <machineKey> section to our web.config. No caching. Sometimes it occurs on new devices from the first time.
After spending a significant amount of time with investigation, using a combination of Sentry and Azure web server logs, I've found 2 major causes of the mentioned errors:
1) On mobile phones, when the browser is in the background, it may be abruptly stopped by the OS to free up resources. When this happens, usually, the page is stored on the phone's drive, and reloaded from there once the browser is re-opened.
The problem, however, is that by this time, the Anti-Forgery Token, which is a session cookie, has already expired, since this is essentially a new session. So the page loads without an Anti-Forgery Cookie, using HTML from the previous session. This causes the The required anti-forgery cookie is not present exception.
2) While seemingly related, the tokens do not match exception is usually only tangentially related. The cause seems to be user behaviour of opening multiple tabs at the same time.
The Anti-Forgery Cookie is only assigned when a user arrives to a page with a form on it. This means that they can go to your homepage, and have no anti-forgery cookie. Then they can open multiple tabs using middle-click. The multiple tabs are multiple parallel requests, each of them without an anti-forgery cookie.
As these requests don't have an anti-forgery cookie, for each of them, ASP.NET generates a separate pseudo-random token for their cookie, and uses that in the form; however, only the result of the last header received will be retained. This means that all the other pages will have invalid tokens on the page, since their anti-forgery cookie was overridden.
For a solution, I've created a global filter that should ensure that
The Anti-Forgery cookie is assigned on any page, even if the page has no form, and
The Anti-Forgery cookie is not session-bound. It's lifetime should be adjusted to match the user login token, but it should persist between sessions in case a mobile device reloads the page without the session.
The code below is a FilterAttribute that has to be added inside FilterConfig.cs as a global filter. Please do note that, while I do not believe this would create a security hole, I am by no means a security expert, so any input is welcome.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AntiForgeryFilter : FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var cookie = filterContext.HttpContext.Request.Cookies.Get(AntiForgeryConfig.CookieName);
var addCookie = true;
if (string.IsNullOrEmpty(cookie?.Value))
{
cookie = filterContext.HttpContext.Response.Cookies.Get(AntiForgeryConfig.CookieName);
addCookie = false;
}
if (string.IsNullOrEmpty(cookie?.Value))
{
AntiForgery.GetTokens(null, out string cookieToken, out string _);
cookie = new HttpCookie(AntiForgeryConfig.CookieName, cookieToken)
{
HttpOnly = true,
Secure = AntiForgeryConfig.RequireSsl
};
}
cookie.Expires = DateTime.UtcNow.AddYears(1);
if(addCookie) filterContext.HttpContext.Response.Cookies.Add(cookie);
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
I believe your problem comes from having multiple forms with different anti-forgery tokens on one page.
When page is requested you get two different tokens in forms hidden fields but only one token in the cookies.
Form POST contains mismatching tokens that causes an error.
Try how AFT will work for you if page contains only one form. If it works OK then my assumption is correct.
This answer contains possible solution for page with multiple forms however it has some security drawbacks as explained here.
I'm not sure why everything works OK on your localhost.
I've created simple application and tried form POST with correct cookies token but old Form token from previous session. To my surprise such POST successfully passes.
May be asp.net has some special handling for local requests in this case. I haven't found any info on this.
If my answer still doesn't help you could you please provide following data for further analysis:
Original page request with returned HTTP headers and form anti-forgery tokens.
Form POST request with sent HTTP headers.
net mvc 5 application using entity frame work etc and am new to .net c# etc (used to php & sessions)
so i have read allot about using .nets authentication service and that is some how registers a user upon login using FormsAuthentication.SetAuthCookie.
however i need to authenticate a user group for example admin or moderator. and from what i understand this can be achieved and be set using [authenticate(roles="admin")].
but surely if this is using a set cookie a user if they knew how could just change their registered role from user to admin to access restricted content?
so in as simple terms as possible how does .net mvc ensure security in authenticating users? can i use sessions instead of cookies? do i need to create my own authentication system.?
i have searched and read all i can find and most resources just explain how cookies work or how to implement authentication using cookies but very little about sessions.
I'll try to be as concise as possible:
Yes, ASP.NET MVC 5 uses cookies out of the box (if you chose Individual User Accounts in the project wizard)
The authorization of a group or role by means of an [Authorize(Roles="bla")] attribute to decorate controllers and/or controller methods will do just that. It's as if you would be writing
if(!User.IsInRole("bla"))
{
return new HttpUnauthorizedResult();
}
else
{
//here's your ultra-secret View
return View();
}
What if a user changes role while in-session or if he or she has a persistent cookie?
Indeed, you'll need to handle the interval between role change and cookie update.
Read up on it here
Long story short: the design decision is yours whether you think it better to log off a user when re-assigning roles or to make db roundtrips at every authorization check.
Can you use a session variable like in PHP? Sure (the Session object exists), but you shouldn't.
If and when the situation arises where you absolutely NEED to pass some arbitrary data however, there's ViewBag, ViewData and TempData.
I won't go as far as to say, that these constructs are superfluous, they certainly have their use from time to time, but do try and design your application to maximize the use of strongly-typed models, viewmodels and make use of the REST-based url architecture to get or put your data.
I need to be able to support "auxiliary" content on a MVC (4) web site -- images, PDFs, videos, etc. Most of this content is provided by the customer using the site so it should not be accessible to unauthenticated users.
(We're using Forms Authentication to generate an authentication ticket. Note, we are not necessarily using the built-in membership/identity providers, as some of our customers want to use authenticate via other means e.g. a federated identity service.)
I've secured the static content by setting up the site to route/authorize all requests through ASP.NET (via runAllManagedModulesForAllRequests) as described here.
I found that I needed to allow anonymous access to the Content and Scripts folders (via location) ... but this configuration works for most content types.
It does not however work for video content, specifically in Internet Explorer. Media Player comes up but reports that it cannot play the file. "The player might not support the file type or coded that was used to create the file."
(Interestingly enough, there's no error in Chrome or Firefox. The video plays in a new tab.)
I'm fairly certain it's Media Player that is the issue. If I add a location element and allow anonymous access to the video, it plays just fine. And we've had similar problems in the past where the root cause ultimately turned out to be security- / authentication- related.
But obviously I can't change Media Player. And I have to support IE. So ... does anyone know of a way to work around this issue programmatically in ASP.NET MVC? Any help would be appreciated.
I seem to recall seeing an SO post about sending the authentication information not just with HTTP requests. I can't locate that post now. But according to Fiddler the request is in fact an HTTP GET. And we're not sending an authentication header, but we are sending an authentication cookie.
Update:
I thought I would be able to adapt the answer to this question.
It involves appending a flag and the authorization cookie value to the content URL, and hooking in to the Application_BeginRequest event.
In the view:
var asset = ...;
var authToken = Request.Cookies[FormsAuthentication.FormsCookieName].Value;
var assetUri = string.Format("~/Media/{0}?requireAuthSync=true&token={1}",
asset.ID, Url.Encode(authToken));
Then in Global.asax.cs:
protected void Application_BeginRequest()
{
if (!string.IsNullOrEmpty(Context.Request["requireAuthSync"]))
{
AuthCookieSync();
}
}
private void AuthCookieSync()
{
var authParamName = "token";
var authCookieName = FormsAuthentication.FormsCookieName;
try
{
if (!string.IsNullOrEmpty(Context.Request[authParamName]))
{
var authCookieValue = Context.Request.QueryString[authParamName];
var authCookie = Context.Request.Cookies.Get(authCookieName)
?? new HttpCookie(authCookieName);
authCookie.Value = authCookieValue;
Context.Request.Cookies.Set(authCookie);
}
}
catch
{
}
}
Using this approach, the video does in fact play in IE.
However, it's now possible to bookmark the video (or other content) and use the bookmark to access the content without being authenticated ... so the site security is effectively broken.
So it seems, in order to preserve site security, I shouldn't be creating the cookie if it doesn't already exist. But with Media Player, creating the cookie is the only way to get the video to play. I appear to be stuck in a Catch-22.
Any insight would be much appreciated.
so the site security is effectively broken
First of all, the site is not broken. Although a user can tag it, the forms cookie will expire (depending on the token validity time you set when you issue the cookie) and the link will stop to work.
But then, note that you don't really have to pass the forms authentication cookie value as the token in the query string.
You could for example generate one-time GUID-like tokens and the very first time the token is used, it is marked at the server side and cannot be used again. Or even you could somehow encrypt or sign the token validitity time in the token so that the link is valid for a short period of time but the token is independent on the forms cookie but you don't need an auxiliary storage of used tokens.
So I have been happily building my ASP.Net MVC 4 application for a month now. I have the security model in place, users can log in and secure information is being stored in the users session for subsequent page requests. This all works great. I then implement some ajax calls to get additional data into the page. I do this by making a call to a Web API interface. In the Web API call I try access Session and low and behold Session is null. Now I get that Web API is supposed to be "stateless". All the parameters for a web api call should be passed in. But what if some of those parameters are sensitive and can not be passed up from the client when making the ajax call?
I've read about the hacks to get access to session in WebAPI. I would prefer not to do this and violate the tenants of web api. ?
So do you store this information in the database and pull it out in the web api call? Seem like a PITA to do that.
So how would could keep certain parameters secure when making the web api ajax call?
Or do you break down and just get access to session?
Each service call could accept a user id.
The WebAPI controller method uses the user id to do further data look ups, such as looking up sensitive order details.
The way I do it is inheriting from a Base Class, and this base class has the following:
public string UserName
{
get
{
return User.Identity.Name;
}
}
The WebAPI can see this UserName and each UserName is unique, so you can do a user lookup based on this variable.
If you're wanting to send any further data to the WebAPI then you're going to have to send, say, an ID to the Controller and validate it against the UserName to see if they have access to it.
Psuedo...
I want a Policy!
Client side
Sends Policy ID (1234 to WebAPI, meaningless to a hacker if they can't get info from it)
This ID is validated against
User.Identity.Name to see if information can be retrieved
We got our own central session management. Generally user can authenticate over it with an username and password, and as a result he gets an session_id. All other operations are done with that session_id. Let's say that the session management is accessed by a XML RPC.
I have two cases to implement:
Central web application made in Spring, which has login form
External web applications also made in Spring, which are relying on
passed session_id only.
Few more notices regarding system:
- session_id is stored in a cookie (after successful login, I have to add cookie to a response)
- every page request has to check session_id validity in session management system
I'm quite new to Spring, so I'm struggling to understand where and how to implement my custom logic.
My questions are:
What parts of a system I have to implement to have my own login
logic (got to have access to a response object too - to set cookie)?
I tryed something with extending UsernamePasswordAuthenticationFilter and implementing my own
AuthenticationManager, but I'm not sure that I'm going the right
way.
Is there point where/how can I implement my "every request session
check" in Spring Security manner?
session_id is stored in a cookie (after successful login, I have to add cookie to a response)
Do this in a AuthenticationSuccessHandler that is configured into your <form-login> element:
<form-login authentication-success-handler-ref="authenticationSuccessHandler"/>
External web applications also made in Spring, which are relying on passed session_id only.
Create a new filter where you check for the session_id cookie. If the cookie is not present or if it is invalid redirect to the central web application for the user to log in. If the cookie is present and valid and the user isn't already authenticated then create a new Authentication and add it to the SecurityContextHolder.
Take a look at RememberMeAuthenticationFilter.doFilter() for an example of what you want to do in your filter.
Add this filter to the filter chain using the <custom-filter> element.