4

What options are available for authentication of an MVC3 Web API application that is to be consumed by a JQuery app from another domain?

Here are the constraints/things I've tried so far:-

  • I don't want to use OAuth; for private apps with limited user bases I cannot expect end users to have their accounts on an existing provider and there is no scope to implement my own
  • I've had a fully functioning HMAC-SHA256 implemention working just fine using data passed in headers; but this doesn't work in IE because CORS in IE8/9 is broken and doesn't allow you to send headers
  • I require cross-domain as the consuming app is on a different domain to the API, but can't use jsonp becuase it doesn't allow you to use headers
  • I'd like to avoid a token (only) based approach, as this is open to replay and violates REST by being stateful

At this point I'm resigned to a HMAC-SHA256 approach that uses either the URL or querystring/post to supply the hash and other variables.

Putting these variables in the URL just seems dirty, and putting them in the querystring/post is a pain.

I was succesfully using the JQuery $.ajaxSetup beforeSend option to generate the hash and attach it to the headers, but as I mentioned you can't use headers with IE8/9.

Now I've had to resort to $.ajaxPrefilter because I can't change the ajax data in beforeSend, and can't just extend data in $.ajaxSetup because I need to dynamically calculate values for the hash based on the type of ajax query. $.ajaxPrefilter is also an issue because there is no clean/simple way to add the required variables in such a way that is method agnostic... i.e. it has to be querystring for GET and formdata for POST

I must be missing something because I just cannot find a solution that:- a) supports cross-domain a) not a massive hack on both the MVC and JQuery sides c) actually secure d) works with IE8/9

There has to be someone out there doing this properly...

EDIT

To clarify, the authentication mechanism on the API side is fine... no matter which way I validate the request I generate a GenericPrincipal and use that in the API (the merits of this are for another post, but it does allow me to use the standard authorization mechanisms in MVC, which I prefer to rolling my own... less for other developers on my API to learn and maintain)

The problem lies primarly in the transfer of authentication information from the client to the API:- - It can't rely on server/API state. So I can't pass username/password in one call, get a token back and then keep using that token (open to replay attack) - Anything that requires use of request headers is out, because IE uses XDR instead of XHR like the rest of the browsers, and it doesn't support custom headers (I know IE10 supports XHR, but realistically I need IE8+ support) - I think I'm stuck generating a HMAC and passing it in the URL somewhere (path or querystring) but this seems like a hack because I'm using parts of the request not designed for this - If I use the path there is a lot of messy parsing because at a minimum I have to pass a username, timestamp and hash with each request; these need to be delimited somehow and I have little control over delimiters being used in the rest of the url - If I use data (querystring/formdata) I need to change the place I'm sending my authentication details depending on the method I'm using (formdata for POST/PUT/etc and querystring for GET), and I'm also polution the application layer data space with these vars

As bad as it is, the querystring/formdata seems the best option; however now I have to work out how to capture these on each request. I can use a MessageHandler or Filter, but neither provide a convienient way to access the formdata.

I know I could just write all the parsing and handling stuff myself (and it looks like I will) but the point is I can't believe that there isn't a solution to this already. It's like I have (1) support for IE, (2) secure and (3) clean code, and I can only pick two.

0

2 Answers 2

3

Your requirements seem a little bit unjustified to me. You can't ever have everything at the same time, you have to be willing to give something up. A couple of remarks:

  • OAuth seems to be what you want here, at least with some modifications. You can use Azure's Access Control Service so that you don't have to implement your own token provider. That way, you have "outsourced" the implementation of a secure token provider. Last I checked Azure ACS was still free. There is a lot of clutter when you look for ACS documentation because people mostly use it to plug into another provider like Facebook or Google, but you can tweak it to just be a token provider for your own services.
  • You seem to worry a lot about replay attacks. Replay attacks almost always are a possibility. I have to just listen to the data passing the wire and send it to your server, even over SSL. Replay attacks are something you need to deal with regardless. Typically what I do is to track a cache of coming requests and add the hash signature to my cache. If I see another request with the same hash within 5 minutes, I ignore it. For this to work, I add the timestamp (millisecond granularity) of the request and some derivative of the URL as my hash parameters. This allows one operation per millisecond to the same address from the same client without the request being marked as replay attack.
  • You mentioned jQuery which puzzles me a bit if you are using the hashing method. That would mean you actually have your hash algorithm and your signature logic on the client. That's a serious flaw because by just inspecting javascript, I can now know exactly how to sign a request and send it to your server.
Sign up to request clarification or add additional context in comments.

11 Comments

Ameen, I agree with your assessment that some of the requirements seem unjustified, and that one "can't have everything". All of these requirements together would give very few options, maybe none? @Aleks - Why not just ASP.NET forms authentication? Other than being a token, it satisfies all your other requirements. And as for replay attacks, I'm not sure how any other approach would better protect against that? Basic Authentication for example would be far more susceptible to replay (or outright packet sniffing).
+1 for the comment about the client side signing. The question does imply the secret for carrying out the hash is actually in the JavaScript.
All valid points... in response:- Re OAuth... whilst it's true that I could use something like Azure, it adds another external system which I have no control over. I have some customers that are very particular about security and this adds unnecessary complexity; OAuth seems like overkill for what I am trying to achieve; however it is a valid option
Re replay attacks, I do just that; I have a millisecond timestamp as part of my HMAC-SHA256 implementation and I cache the hashes to ensure that the same hash is not used twice. This still however doesn't address the passing of the hash and the shared details (such as timestamp) which I can't do in the headers and have it still work in IE Re JQuery... correct, you can inspect the hashing algorithm, however I include the users hashed password which is already known by the API as part of the message; without that an attacker is unable to construct a message that will match on the API side
I understand I may want too much; Having to add a lot of code seems to be the only reasonable compromise; I just thought that this must be a common scenario and that someone else must have had to deal with this at some point. Whilst it may seem like overkill, I'm just surprised that I have to compromise on security. The whole thing works perfectly in firefox/chrome, just not in IE; so I either have a secure app or one that works in IE, but not both; and does that mean all other apps out there with the same architecture suffer from the same problems? how does everyone else get around it?
|
1

Simply said; there is not much special in ASP.NET WebAPI when it comes to authentication.

What I can say is that if you are hosting it inside ASP.NET you'll get support by ASP.NET for the authentication and authorization. In case you have chosen for self-hosting, you will have the option to enable WCF Binding Security options.

When you host your WebAPI in ASP.NET, you will have several authentication options:

  1. Basic Authentication
  2. Forms Authentication - e.g. from any ASP.Net project you can enable Authentication_JSON_AppService.axd in order to the forms authentication
  3. Windows Authentication - HttpClient/WebHttpRequest/WebClient
  4. Or explicitly allow anonymous access to a method of your WebAPI

2 Comments

Thanks for your response. In reply to the options you've proposed:- 1) Can't use basic authentication; as I mentioned in my question, CORS support in IE doesn't allow any additional headers, Authentication is one of them. 2) Forms aren't an option as it's an API and does't host it's own pages. This mechanism is also stateful, so not appropriate for REST, and it (seems to) passes the auth details in clear text, and in a deterministic way, which is open to replay attacks.
and... 3) Despite Windows auth not really being an option (auth need to be handled internal to the app), this doesn't address getting the auth values to the API 4) The whole point is to secure the API, so anon access is not allowed

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.