In my ASP.NET web application, I want to check every time the user is trying to get a page from my application if the user exist in the DB (of course after the first time we save the user details in the session).
I tried to use the Application_AuthenticateRequest event in the global.asax to
check for each request but the session does not exist in this event.
I need an advice for where i can put my authorization logic that i would still have the session data available (to reduce db access).
You sound as though you are "rolling your own" authentication system.
I would look into using ASP.NET's built in Forms authentication system that is commonly used with an ASP.NET Membership Provider. Built-in providers already exist for SQL Server, and you can create your own Membership Provider by inheriting from the System.Web.Security.MembershipProvider base class.
Essentially, the ASP.NET membership providers usually work by setting a client side cookie (also known as an Authentication Ticket) in the client's browser, once the client has successfully authenticated themselves. This cookie is returned to the web server with each subsequent page request, allowing ASP.NET, and thus your code, to determine who the user is, usually with a single line of code like so:
string username = HttpContext.Current.User.Identity.Name;
// The above gets the current user's name.
if(HttpContext.Current.User.Identity.IsAuthenticated)
// Do something when we know the user is authenticated.
You then should not need to store anything in the Session state. Of course, if you want to store user-specific data in a session variable (i.e. user-data that may not be part of the authentication of a user, perhaps the user's favourite colour etc.) then by all means you can store that in a session variable (after retrieving it from the DB when the user is first authenticated). The session variable could be stored based on the user's name (assuming unique names) and retrieved using code similar to the above which gets the current user's name to access the correct session object.
Using the built-in forms authentication will also allow you to "protect" areas of your website from un-authorized users with simple declarative code that goes in your web.config, for example:
<authorization>
<deny users="?"/>
</authorization>
Adding the above to your "main" web.config would ensure that none of your pages are accessible to un-authorized users (though you'd probably never do this in reality - it's just meant as an example). Using the ASP.NET Role Provider in conjunction with the Membership Provider will give you even greater granularity over who can or can't access various sections of your website.
You could use the SqlMembershipProvider (or a custom provider if you're not using MSSQL) and deny unauthenticated users from the entire application except from the login page. This check will be limited to the time of logon as the authentication ticket will be stored either in session or as a cookie on the user's machine.
More details at How To: Use Membership in ASP.NET 2.0 and
Examining ASP.NET 2.0's Membership, Roles, and Profile
Related
I'm currently using ADFS and claims-based identity in ASP.NET web applications to convey informations such as a user's name or identifier.
In addition, inside applications a ClaimsTransformer adds a couple more claims that are relevant to that app. Such claims include applicative roles (Admin, Validator, etc.), whereby the user can't see some pages unless they have the right role.
However, just because you have no role doesn't mean you can't access the application at all. Vanilla users that aren't part of a specific group are sometimes also able to see some pages. Besides, we have a few web applications where roles aren't used -- it's just a matter of whether you can get in or not. We call this "access", which is different from "role" or "permission".
Access rights to applications are stored in a separate database. You can have access to an application if an admin manually granted it to you or if you satisfy a set of rules calculated by another system.
How would you materialize access in terms of claims? A claim type like HasAccess with a true/false value sounds weird and vague.
Is claims-based identity the right place to look at? The idea is to insert that information precisely enough in the authorization chain that every auth cookie doesn't have to carry a list of all accessible applications for the user, but early enough in the process that an accessibility check doesn't have to be done at each http request.
We're developing a SAAS solution for a big company in which doctors can view patients and make mutations, order products, provide licenses.
This project is for 4 separate companies under one umbrella company. For each company we developed a portal. All portals use the same code but have a strict separated database because the database contains all the patient information.
We're using Sitecore as CMS.
The client decided to use virtual folders instead of subdomains for the production environment. Our staging evironment url is for example: acc-portal1.umbrella.com. For the production environment they would like a URL such as: acc.umbrella.com/portal1. One SSL certificate is being used for all portals and requests.
We're using Membership Provider (forms authentication) for the authentication of users. Users can not log in with the same account in for example portal1 and portal3 because of the usage of separated databases.
Because we're using formsauthentication the ".ASPXAUTH" cookie is being used. Of course the "ASP.NET_SessionId" cookie is used also.
Because the client wants to use virtual folders instead of subdomains, the cookies are shared over all portals. It is possible to set the "path" on the node in web.config but this path is dynamically read by Sitecore and resolved in a pipeline. I did not find a way to override this path after it is being loaded in the web.config. Also I did not find a way to alter the ASP.NET_SessionId cookie path.
My question is: is it a (security) risk to share these cookies over multiple portals (remember, they should be separated completely)? Are there any other problems this setup could cause?
Hope somebody can help!
Yes, there is a huge security risk. What you do is called a multitenant application. You have to take special steps to ensure that cookies and other sensitive data cannot be shared.
My advice would be to store the tenant name (portal1) in the custom data section of the forms authentication cookie. You set the custom data when you issue the forms cookie.
Then, have a custom module or just a handler of the Application_AuthorizeRequest event, where the identity is already established based on the cookie.
In your handler, you decrypt the forms authentication ticket from the cookie, retrieve the user data and compare to the actual url. If there is a match - nothing happens. If there is no match, it means that user is authenticated in one portal but tries to access another. You can gently clear the response and render a message "well, this portal is not meant for you" or just log the user out.
I'm using a login form to authenticate users.
FormsAuthentication is right out as it stores sensitive user/role membership in either client-side in a cookie or within the URL. Within the URL is a huge security risk, so I won't even get into that. With the
FormsAuthentication cookie, this creates problems with a) security where the client is in the position of dictating it's own roles; and b) way too much data stored in cookies. Since I'm gaining nothing through security and loosing out big time on the size of user data storage, I'd rather just work with Sessions.
I'd like to reuse something like FormsAuthentication for all its basic login form-handling features. But i would rather have it store user data server-side in perhaps Session rather than client-side all stuffed into a single cookie. I'd rather just authenticate against a Session token of some sort.
I have no database and local disk storage of user data is forbidden. I rely on a 3rd party authentication service provider, and a requirement is that I must reduce chatter with this service. Thus, sessions for temporary storage of user info. Sucks, but that's not necessarily the problem I'm asking about. Also, a requirement is that I must set/use HttpContext.user and likely Thread.CurrentPrincipal for use later on in such things as AuthorizeAttribute, for displaying user info in views, etc.
So FormsAuthentication stores all user data client-side in a cookie. Whereas Session stores all data server-side and just relies on a simple client-side token cookie. However, Session is not available anywhere during the asp.net startup and authentication steps. Is there an equivalent forms "membership" provider that stores all data in Session server-side instead of client-side?
If there is no Session equivalent...
Where do I set HttpContext.user and Thread.CurrentPrincipal to make both values available throughout the rest of both MVC apps without interfering or messing up other MVC components?
Hinging on #1, is Session available at that entry point? If not, how do I make it available so I can create the Principle/Identity object using the data stored in Session?
This can't possibly be a unique requirement. Are there libraries already available which handle this?
Session stores information in a client-side cookie too! (or in the URL if cookieless).
If you want to authenticate a client, he'll have to provide some kind of credentials - usually an encrypted token in a cookie once he has logged on. If not a cookie, then what do you propose?
You should use FormsAuthentication. The sensitive information stored in a client-side cookie is encrypted using a key that should only be known to the web server. "the encryption methods being public knowledge" doesn't mean that you can decrypt the data without access to the appropriate cryptographic key.
You mention "roles" and a "third-party authentication provider". If your third party is also providing roles (i.e. an "authorization provider" as well as an "authentication provider"), then it would be reasonable to cache roles obtained from the provider on the server. Session is not available when a request is being authorized, so the best solution is to use the Cache (System.Web.Caching.Cache).
Personally I would encapsulate this in a custom RoleProvider. The custom RoleProvider would implement GetRolesForUser by getting roles from the third party on the first call, then caching them in Cache.
Not sure if I like what I'm about to suggest, but you could do the following:
Leverage the Application State or System.Cache as a global storage for user credentials.
Use an InMemory database (like RavenDb) which can also have encryption (in memory, I believe).
Using the Application state as a place to storage relatively common / frequent stuff I think is not a great place because of
Scaling / locking issues? <-- just a gut feeling.
Permenant data? so you have users in the website's memory .. then the website crashes or recycles, etc... what happens now to that data?
RavenDb is awesomeballs - go.use.it.now.
I understand that you are not storing anything locally, so whenever a user hits your system, you need to refresh your inmemory cache, etc. Fine. A pain in the f'ing butt , but fine none-the-less. (EDIT: unless the data has been cached in memory .. that is)
Anywys, two suggestions.
ProTip:
Oh! move away from role based shiz and start using Claims based identity stuff. Yes, it still works with IPrincipal and HttpContext.User, etc. So all previous code is NOT busted. But now it's baked into .NET 4.5
Awesome Video on this for you, me, everyone!
Finally - bonus suggestion
A nice package that auth's with either Facebook/Google/Twitter. You said you're keeping the user cred's on another site (good move!). If you're using other providers, then stick with DNOA or SS.
GL!
We are building a set of web applications which all utilize their own unique role providers using sql server and utilizing Windows Identity Foundation to provide a Secure Token service to handle the membership using Active Directory. This solution also provides the benefit of single sign on. One oversight was only allowing users a single session. Tracking if the user already has an active session seems easy enough to implement, it’s what and how to deal with a user’s session if it’s determined they are already logged on elsewhere .
My question is what is the recommended approach for killing an existing user session if the user attempts to spawn an additional session? Would also like to advise the user that an existing session has been detected and by continuing, that existing session will be terminated. (This part also seems trivial…)
Example Scenario with 2 web apps and a STS Identity app:
User attempts to access application A:
STS Identity extension determines user is not already logged on, provides a claim and caches a user/session identifier.
User attempts to access application B on different computer. (they can access application B if using the same session)
STS Identity determines user has active session and denies logon.
There seem to be some other issuer to overcome, for example….
How to update the logged on user cache from the application A if the user is actively keeping their session alive.
How would you deal with the user not explicitly logging off, say by closing the browser or the session timing out.
Other problems???
Any guidance would be appreciated.
We have opted to handle concurrency at the application level, using a simple table that stores the users session id. If a new session for the application is created elsewhere, the existing session is terminated with the use being redirected back to the STS with a message (carried in the query string) the reason why they've been redirected back to the login page.
I wish to secure individual (dynamic) pages in an ASP.NET MVC application.
I do not want to use a full blown authentication system - we are already using forms authentication for the administrators of the site. Instead, this is so that we can send out links to a page with a password for specific users.
The way I am handling this currently is when a valid password is submitted we create an encrypted cookie containing the page id (Guid) and their session id and redirect them to the page. In our "Page" controller action we then validate this cookie.
So first question, is this the best (most secure) approach (aside from using forms authentication)?
Second question, can I read the machine key used by Forms Authentication to perform the encryption, or better yet use the FormsAuthentication to encrypt the cookie (the only overload I can see is one that requires a FormsAuthenticationTicket)?
Since we always generate a new machine key before deploying it would be better if all our encryption used the same key.
[Update]
Regarding how to access the machine key I found my answer at http://rich-rogers.com/archive/asp.net-c-sharp-encrypt-hash-using-machinekey-values
[Update 2]
I realize after asking this question that since I will need to maintain a list of pages that they do have access to, I would probably be better off just storing these in the current session. I can store a list of security tokens with an expiry date. Since I already have a wrapper around session, this should be easy to unit test too.
To make really secure your cookie-key, you need to make it available only on SSL pages, or else some one can get it, and even if its encrypted he can use it.
Request.Cookies[cookieName].HttpOnly = true;
Request.Cookies[cookieName].Secure = true;
Also to read: Can some hacker steal the cookie from a user and login with that name on a web site?