ASP.NET Web API REST Security Basics
Why am I writing a post on web services security? Well, for one, I ran out of time and space when writing my “ASP.NET MVC 4 Web API“ book. I covered it to some degree, but when it comes to security there’s quite a bit to talk about. And in working with Apress we wanted to keep the book relatively small – to make it easier to “go from zero to REST service hero” as quickly as possible. As such, the book only covers one of many options for securing a REST service (specifically, basic HTTP authentication).
So in this post I want to answer some important security-related questions being asked by nearly every developer building REST services with the ASP.NET Web API. Hopefully this will help you feel more comfortable and confident in securing your services.
Question number one…
How Does It Work?
Bottom line, anything being used for authentication and authorization in REST services must – yes, MUST – come from the caller’s HTTP request. Period. No exceptions. There’s no magic here. There’s no “other” channel being used, no secondary hidden request, and certainly nothing being automatically stored for you in the ASP.NET Session object. Yes, each and every request must include some kind of identifier telling you who the caller is.
There, I said it: no magic. Sorry to disappoint.
The key here is that we need to reliably identify the caller. And by that, I mean we need to be able to trust that the caller is who they claim to be. One way of doing so is with a password. That is, after all, how most of us log in to our computers everyday. Of course we could also use some kind of token – either generated by our own service, or issued by a trusted token provider.
But no matter what mechanism we use to identify our caller (e.g., username and password), it MUST be included in the caller’s request. Therefore, we generally want to sign and/or encrypt that bit of information. If the caller is giving us a username and password, we wouldn’t want them transmitted in clear-text!
Which gets us to question number two…
When do I use HTTPS?
Answer: whenever you don’t want information stolen from the HTTP request. Any data sent to your service from a caller can be tapped and stolen – unless you use HTTPS. Sure, there are some nuances with HTTPS to be aware of. But in general, using transport security (i.e. HTTPS) is a sure-fire way of making sure usernames, passwords, tokens, certificates, or anything else aren’t easily stolen.
For example, if you expect every call to your Web API service to include a username and password, you should probably use HTTPS. Or, if you expect the caller to make a “login” call as their first call, to which you respond with a token, and then expect that token on subsequent calls, you should still use HTTPS – unless you want the tokens stolen.
There are other approaches to making sure a token or other identifier isn’t stolen and reused by another party, but they typically involve storing things in a server-side database to be used on subsequent calls. For example, we might store the issued token along with the caller’s IP address (as taken from the REMOTE_ADDR server variable). That way, on subsequent calls that include the token, we can validate that a caller with the correct IP address is using the token.
Problem is, RESTful services are supposed to be stateless. So we really shouldn’t be storing anything that ties one call to another call. Each call to our Web API service should stand alone. And the easiest way to do that is make the caller pass their credentials (e.g. username and password) on each call. And if that’s the case, you need to use HTTPS for your service.
In short, to keep things simple, just use HTTPS for your entire Web API service. And then know that other approaches do exist if you aren’t able to use HTTPS for all service methods.
Speaking of username and password, let’s get on to question number three…
What is Basic HTTP Authentication?
Simply put, basic authentication means the caller is passing their username and password in the HTTP request header. And basic authentication doesn’t dictate that those credentials are even encrypted. All we have to do is:
- Create a string with username and password in the form ” username:password” (sans quotes)
- Convert that string to a base64 encoded string
- Prepend the word “Basic” and a space to that base64 encoded string
- Set the HTTP request’s Authorization header with the resulting string
For example, if the caller had username jsmith and password Popcorn, the resulting header would look like:
Authorization: Basic anNtaXRoOlBvcGNvcm4=
In my book, and in the accompanying sample source code, I show you how to use this header value from within an ASP.NET Web API service. Unfortunately, it’s not built into the framework. But you can just copy/paste my sample code. Check out the MVC4ServicesBook.Web.Api.BasicAuthenticationMessageHandler class. Doing so will let you validate the caller’s username and password against your own credential store (e.g., ASP.NET Membership, custom users table).
When using basic authentication like this, it is nice to add the WWW-Authenticate header to the HTTP response from your service if the request doesn’t contain an Authorization header. If you add the WWW-Authenticate header with a value of “Basic” (again, sans quotes), this tells the caller that they need to use basic authentication when calling your service. Further, most browsers (e.g., Chrome, IE, FireFox) will pop a login dialog with username and password boxes when they see this response. This makes it much easier to test your secured Web API service from within a browser. The MVC4ServicesBook.Web.Api.BasicAuthenticationMessageHandler class reference above contains this code, as well.
At this point, we’ve clarified that any security information you require from the caller MUST be included in the HTTP request. We’ve also stated that if you don’t want something stolen, you have to use HTTPS (this is the simplest and most-supported method of encrypting the traffic). And just to keep things simple, we’ve concluded that it’s probably simplest to use HTTPS for your entire Web API service and all of its methods. Finally, in this section, we’ve talked through what it means to support basic HTTP authentication – including a fully-functional C# class that will take care of it all for you.
With just this information, you can reliably secure your Web API service. In fact, I encourage you to give it a try. But what about OpenID? Or OAuth? And what if I want to support sessions/cookies with my service? Let’s tackle OpenID in question number four…
What is OpenID?
Think of OpenID as a protocol for decentralized authentication that lets you delegate the work of validating usernames and passwords to someone else. As a service provider, you essentially have to trust an OpenID identity provider and then receive tokens from that provider. There are quite a few out there, such as Google, AOL, Yahoo, and VeriSign. We first need to trust one or more of those providers, typically through a shared secret of some sort. The caller would then do the work of obtaining a token from one of those identity providers, and pass the obtained token in the header of a request to your service. (hence the need to use HTTPS!) The information in the token will contain this shared secret, which you can compare against the secret you obtained ahead of time from the identity provider. If they match, you know the identity included in the token is for real, and you can go about your business with the given caller’s identity.
Let’s take a look at the sequence of this particular scenario. Note the three parties: Caller, OpenID Provider, and our Web API Service:
OpenID is also used in an interactive capacity, where the end-user of a web site gets redirected to an OpenID provider’s login page to enter their credentials, and then gets redirected back to your web site. But since this post is about Web API, I’m not going to cover that anymore.
To get started with being able to support OpenID tokens in your Web API service, read the “Getting Started” page on the OpenID website. The page contains all kinds of libraries and sample code, including some for C#. Just don’t forget that you still want to use HTTP – since even an OpenID token can be stolen if not encrypted! I’ll also be posting in the near future a simple example of using OpenID with a Web API service.
Let’s move on to OAuth in question number four…
What is OAuth?
Unlike OpenID, which is meant to securely identify a caller with a trusted token (and here we use the term token loosely), OAuth is meant to let an application call another application on an end-user’s behalf – without requiring the calling application to store the user’s username and password. In short, OAuth is not intended to provide caller identity – it’s purpose is to let service applications talk to each other on behalf of end-users.
Without OAuth, the calling application must store your username and password. Beyond the obvious problem of handing out your credentials, this creates other issues. If you were using a particular password to let a whole slew of applications to talk to each other, a compromised password would give a criminal access to all of those applications. Another problem is that with password-based credential validation like this, the only way you can revoke access from a particular calling application is by changing your password. But then this would break ALL calling applications – not just the one from which you want to revoke access.
But… with OAuth, a 3rd party application is granted limited access to another application on your behalf. Think about Mint.com connecting to your online bank account to download transactions. Mint either needs to store your username and password. Or, it can use OAuth to gain access. The social web application Twitterfeed.com does exactly this – uses OAuth to gain access to your Twitter and Linkedin accounts after you grant it access to those services on your behalf.
So what’s the difference between OpenID and OAuth? Let’s discuss a few of the more important aspects in question number five…
What’s the difference between OpenID and OAuth?
With OpenID, the token included in the HTTP request header actually identifies the caller. So as soon as your service receives the token, you know exactly who the caller is. This would be similar to using basic authentication, without having to validate the caller’s credentials first (since the caller already did that with their OpenID provider).
With OAuth, the token provided doesn’t identify the caller at all. It merely lets your service know that the caller is allowed to call that method or REST URL. Typically, when using OAuth in this capacity, the resource being accessed is a unique URL (e.g., REST URL, RSS feed), so there’s no need to identify the caller. As long as the called service can validate that the provided token provides access to the desired URL, you’re good to go.
For example, if an application like Twitter wanted to call my service on behalf of a user named bigbob, it could theoretically obtain an OAuth token from my service that allows Twitter to access the resource http://server/api/accounts/bigbob/transactions. So when I get a request for that resource that includes that OAuth token (either in an HTTP header or via request string argument), my service simply says “sure, here’s your data”. I don’t even need to know who the caller is.
Of course, once it has an OAuth token from my service, Twitter might be able to make an API call whereby I tell it the identity of the OAuth token. For example, I might offer a method like http://server/api/whois in which I examine the incoming token and respond with the name of the user for which the OAuth token represents. That’s what we call “pseudo-authentication with OAuth” – because it isn’t really authentication. It simply takes an OAuth token, which is already authenticated and trusted, and asks the service “who does this token belong to?”
For a good visual comparison of the OpenID and OAuth, check out this great pic from the OpenID Wikipedia article:
It is possible to support OpenID and OAuth with ASP.NET Web API. But, my apologies, I’m going to save the technical details for another blog post.
Ok… two more questions… first…
When Do I Use Basic, OpenID, or OAuth?
Use basic authentication when your own service is going to validate the caller’s credentials. In other words, they will give you a username and password, and you will check those against either the ASP.NET Membership provider, Windows Active Directory, a custom database table, or some other credential store accessible by your service. In short, the caller will claim they are a certain identity with a username/password combination, and you will check those values against some system or database.
If you want to delegate the actual work of validating the caller’s credentials to someone else, you can use OpenID. You have to publish the fact that you support OpenID tokens from, say, Google. The caller then obtains a valid token from such a provider (likely by issuing a call to them that includes their username and password), and then calls your service with the obtained OpenID token. This is beneficial for two main reasons. One, you don’t have to burden yourself with managing user credentials, lockouts, password complexity policies, password changes, etc. You simply let Google take care of that for you. Nice! And two, supporting OpenID authentication adds some bit of credibility to your REST service, making it easier for callers to trust you. Again, to get started with OpenID, read the “Getting Started” page on the OpenID web site. I will also be following up with a post on using OpenID with Web API in the very near future.
Finally, use OAuth when either you want your service to call ANOTHER service on an end-user’s behalf; or, you want to accept calls from ANOTHER service on behalf of an end-user. Maybe you are creating a service that allows LinkedIn to get information about people that work at your company. In that case, an end-user would need to authorize LinkedIn to call your service on THEIR behalf to obtain that information.
In summary, think of:
- Basic authentication – your service provides credential validation
- OpenID – a trusted identity provider provides credential validation
- OAuth – lets services talk to each other on an end-user’s behalf
What Does All This Mean for My Web API REST Service?
You have to ask yourself, “do I need to identify a caller?” If so, then you need to support either basic authentication or OpenID. Ok, yes, there are other means of authentication, but I’m trying to keep this simple. Further, these two approaches will cover at least 90% of real-world scenarios. In my book and sample code I tell you exactly how to implement basic authentication in a Web API service. And we’ll cover an OpenID and OAuth implementations in the near future. As a side-note, I encourage you to sign up for my newsletter to help stay on top of information/publications like this.
The most straight-forward way to implement basic authentication in the Web API is to implement your own DelegatingHandler. This will allow you to validate the incoming credentials and then associate an IPrincipal on the current thread. Once the principal is set up, the .NET [Authorize] attribute – applied to Web API controllers and methods – will prevent unauthorized callers from calling your service. Again, please review the code in my book and sample code for more details on how to go about this.
** For more nitty-gritty details on the ASP.NET Web API and using it to build RESTful services, be sure to check out me new book.
UPDATED 2/15/2015 – Removed Facebook as an OpenID provider (thanks to comment from Ryan Helmoski below).