-1

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...

  1. 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?
  2. 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?
  3. This can't possibly be a unique requirement. Are there libraries already available which handle this?
Cœur
  • 37,241
  • 25
  • 195
  • 267
bob-the-destroyer
  • 3,164
  • 2
  • 23
  • 30
  • So AES has been cracked? Even DES takes specialized hardware. Storing credentials in session reduces the problem of hijacking an ID from the hard task of forging or manipulating a strongly encrypted auth cookie to the easier task of forging or hijacking a session cookie. In older versions of .NET you could hijack a session just by guessing the int, so about 1 billion requests, or more easily with session fixation (where I craft a session cookie and provide it to you and ASP.NET will use it because an int is as good as any int. But to forge a FormsAuth ticket, I need to crack AES) – MatthewMartin Nov 19 '12 at 01:57
  • @MatthewMartin: while I recognize the difficulty and implausibility of cracking this encrypted value, I will not concede that it's impossible nor that it's ever a good idea to push the responsibility of providing username/roles from client-side under any circumstances. Might as well go with Sessions since nothing is gained. Also, storage limits on cookies and just too much data going over the wire. So again, forms ticket is right out. – bob-the-destroyer Nov 19 '12 at 02:02
  • Then you will have to re-authenticate on each request. – MatthewMartin Nov 19 '12 at 02:05
  • @MatthewMartin: that's the idea here, except instead of re-authenticating and populating HttpContext.User using data stored client-side like FormsAuthentication does, I'm asking about a way to re-authenticate using data stored server-side from session data. – bob-the-destroyer Nov 19 '12 at 02:12
  • You can't except through a token. If you don't like authentication tokens, then I don't see how you can use cookies anywhere. If you want to look up the roles each request, or store roles in cache or session or where ever, that's your prerogative, but if you don't want to give the user a token(a cookie), then you have to ask for their credentials each time (username & pass). Good tokens are hard to intercept, read or forge, and that means using encryption. If you want to assume that all encrypted text is plain text, then no token is secure, no channel for communicating pwds is secure – MatthewMartin Nov 19 '12 at 02:26
  • @MatthewMartin: The question isn't about whether or not to use cookies. It's about the all the information stored in the cookie vs server-side. Do you have more information on using "tokens" for authentication? That might be what I'm after. – bob-the-destroyer Nov 19 '12 at 02:39
  • HTTP is stateless. You can only give the user (the browser) some many things to store things, typically the Query String (the action value of POST), the cookie (which is a Http header treated specially by the browser, returned on each subseq request) or visually hidden form fields (ie viewstate). To tie one request to another, you have to put a token in either a cookie, query string or hidden field.The session cookie does this but has limited features to defend against forging, hijacking, etc. The cookie has enough info to tie a 2nd request to the 1st request-session-& to a specific user(auth) – MatthewMartin Nov 19 '12 at 02:59
  • @MatthewMartin: right, but the web industry has moved on to demand statefulness regardless of whether we think it a good idea or not. Thus, session and session cookies as one fix. But my problem with asp.net mvc is that I can't access its session during the initial authentication step to be able to use it as a way in for authentication. So I'm in a bind. – bob-the-destroyer Nov 19 '12 at 03:06
  • Mny projects that use session cookies as auth token create a master page, basepage or header & check the session at the top of each page. If you need to set an IIdenity on the thread in globalasax (AuthenticateRequest event) and session isn't available yet then defer using session til the next event or check the thread identity which should be set by then,or maybe use Request.Items to hold a flag that you can read in the 1st even on global asax when session is avail, ref: http://stackoverflow.com/questions/9500681/what-is-the-first-global-asax-event-that-session-is-accessible-assuming-the-cont – MatthewMartin Nov 19 '12 at 03:17

2 Answers2

1

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.

Joe
  • 122,218
  • 32
  • 205
  • 338
  • Already understood that sessions rely on session id's stored in client-side cookies. And, granted formsauthentication stores encrypted values client-side, it's still stored client-side. Thus, my comment about "no security gained, but storage size lost". I'd still argue though that this presents a security risk no matter how much encryption they use, but the topic "encryption strength" isn't really for this question. – bob-the-destroyer Nov 19 '12 at 01:21
  • @bob-the-destroyer - "no security gained" is wrong, and if you say this presents a security risk, I don't know of an alternative that is more secure. In the end you need the client to send you some sort of token, and a cookie containing a FormsAuthenticationTicket is the most common one for .NET. Alternatives such as OpenID, OAuth or whatever all use client-side tokens. – Joe Nov 19 '12 at 01:31
  • Also, isn't Session populated _after_ authentication/authorization steps (AuthorizeAttribute not withstanding)? – bob-the-destroyer Nov 19 '12 at 01:31
  • @bob-the-destroyer - yes you're right about that. I'd recommend you use System.Web.Caching.Cache to cache the roles. – Joe Nov 19 '12 at 01:34
  • thought about Cache, but I'm uneasy about making up my own Session manager attached to Cache to manage each individual user/request without interfering with any other. I was hoping to rely on mvc's built-in Session provider. Reinventing the wheel and all that. – bob-the-destroyer Nov 19 '12 at 01:38
  • To cache roles in the ASP.NET Cache you will need to config it as "VaryByCustom" and configure it to vary by user. In normal usage, Cache is shared across users. – MatthewMartin Nov 19 '12 at 01:46
  • You don't need a session manager, all you're storing is a list of roles per user, keyed by the username extracted from the FormsAuthentication ticket. Also Session has a performance overhead (it's designed to be thread-safe, so requests that use session can not run concurrently), and this solution avoids that overhead for requests that don't need Session. – Joe Nov 19 '12 at 01:47
  • @MatthewMartin - VaryByCustom is for output caching - not needed when you're caching data. Cache is shared across users, so of course your cache key needs to include the username (e.g. a fixed GUID + the username). – Joe Nov 19 '12 at 01:48
  • Okay, what I said, but nix the part about VaryByCustom. – MatthewMartin Nov 19 '12 at 01:52
0

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

enter image description here

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!

Pure.Krome
  • 84,693
  • 113
  • 396
  • 647