4

Creating an angularjs single page application trying to use a RESTful API and I came across a problem that I can't seem to find an answer for. I was trying to prevent session hoping, one user logged in watching the requests figures out his account ID is 13. So he performs the same request but this time alters the request to get info for account ID 14.

So after the user logged in I was setting a session variable to his account ID and was going to strip the account ID out of the ajax requests. I then tried to access the Session from a class inheriting from ApiController. After looking around I realize that storing session information is not very RESTful.

So how would I go about ensuring that one account cannot access another account's information just because they watched the ajax requests and figured out how to manipulate the ajax request?

Is restful not meant to be used with users that need to authenticated and authorized? I was thinking of maybe hashing the IDs or something but I am not sure that is the right approach.

UPDATE:

I have token based authentication working now. But I am still in the dark as to how to prevent someone from fiddling with HTTP request and getting information that doesn't belong to him. For Example, I want to retrieve all the users for account with ID 14.

HTTP Get /users/14

I have a token so I know that the person trying to use the web API at some point authenticated themselves. I can lock it down to roles. But there is nothing stopping this authenticated person form fiddling/hacking with the request and doing the following

HTTP Get /users/58

Now the person has got all of account with ID 58's information. This is information does not belong to account 14 but now he can browse through other people's information.

I need someone of knowing that the request that comes from the browser and says it is for account with ID 14 that it really is account 14. I can put that information in the token but how do I check it in a handler or check it in the ApiController?

4
  • Of course account id is insecure. Try some authentication token service like Thinktecture. Commented Sep 2, 2013 at 8:57
  • 1
    Authentication and authorization in Web API: asp.net/web-api/overview/security/…. Commented Sep 2, 2013 at 10:48
  • possible duplicate of ASP.NET Web API authorization and Authentication Commented Sep 3, 2013 at 5:50
  • The original question was answered, and your bounty should be awarded. You should not tie the answering of a different question to this bounty. Ask a separate question for this. Commented Sep 28, 2013 at 23:27

3 Answers 3

3
+150

The problem you have described is not something unique to REST-based services. In fact, this is one of the top 10 OWASP vulnerabilities (Insecure Direct Object References). You will need to first identify the user and then authenticate the user. For identification, an ID is used, such as user ID. The identifier can be anything you want. After identification, you need to authenticate the user. This is done by authenticating the credential presented to the app, such as password. The identifier and the credential can be any thing, say the good old user name/password, or may be a token or may be an API key. It actually does not matter to the problem on hand. Once authenticated, you authorize the requests based on what the user can do in your app. The authz part is a must regardless of whether you use a token, certificate or what not. Hashing user ID or using some method to hide things is security by obscurity and is not a good practice.

Say, you are using a message handler to authenticate the credential submitted by a user. Once authentication is done you can store the account number associated with the authenticated user in the properties collection of HttpRequestMessage. This is done in the server side and no user can make the system store some random account number. Only the account number associated with the authenticated user is stored in the properties collection. With that, you can now authorize requests. Say, you can write an authorization filter that pulls this account number and compare it against the account number in the URI to determine if the request is allowed or not. By applying this filter against a GET action method, you can ensure only right folks can see right data.

For example, a user with user ID 'abc' and password 'pwd1' makes a GET request to /users/14. First step is, you will authenticate the user ID. If there is a user with ID 'abc' and password 'pwd1' in your store, you will consider the user authentic. As part of this authentication, you can pull the account number associated with the user 'abc'. Say it is 15. You will store 15 in request.properties. From your authorization filter, you can get the account number from URI, which is 14 and compare it against the one in the request, which is 15. Obviously the numbers do not match and you reject the request in the OnActionExecuting method of your filter with a 401.

What I described above is a simple approach. Better one (in terms of extensibility) will be to use claims but it will not be possible to explain all that here. You can get good amount of information related to claims-based identity from Dominick's blog.

Sign up to request clarification or add additional context in comments.

6 Comments

The last part is the part that I am stuck on. I will look into sticking into the request properties. I have the auth and the token part done. I was trying to find a non "hacky" way of doing it. For example it would feel a little weird if I made my own principal implementation and in every ApiController's method get the user and perform a cast. I suppose that I could try to extend the ApiController and place that logic in some utility method.
Any reason you do not want to use an authorization filter? You don't need to create a base controller or create your own principal. Check this out - github.com/thinktecture/Thinktecture.IdentityModel.40/blob/…. Also, leastprivilege.com/2012/10/26/… and leastprivilege.com/2012/06/22/authorization-in-asp-net-web-api
I thought I recognized your name. I used your blog to roll my own SWT. Thanks. Quick question, you used a HttpRequestMessage<T> with a type parameter. Is that correct? An old version or maybe a prerelease version? lbadri.wordpress.com/2012/09/13/…
Is it better to always include the account ID in the request even if the information you are asking has a different ID? for example. HTTP GET /api/Foo/123/456. 123 is the ID of the account asking for foo 456. I can then check the SWT token's account ID against 123. I ask because almost all examples are HTTP GET /api/Foo/456. They will query for that foo object directly. If I should stick to that, that means that I must add the account ID from the token (now in properties) to the DB filter OR when I get it back out check if the account ID on foo object from the DB is also 123. Ideas?
Do not include the account ID corresponding to the user in URI. If a user 'abc' wants to look at account 456, URI will be just /api/foo/456. After authn, you know that the authenticated user maps to 123. As you process the GET for /api/foo/456, you can right away reject the request since 123 != 456, even before hitting database. If the data model is more complicated, you can run the query and from the result, see if the user 'abc' (account 123) is okay to see the data. This is okay with GET. For methods like POST, PUT, etc you cannot take action and then decide okay or not post the action.
|
2

Every request should be authenticated. If the credentials provided do not allow the user with account 13 to access data from account 14 then the request will be denied. The trick is to find a way to do authZ quickly.

4 Comments

Thanks for your answer. I am still pretty new to this way of thinking (RESTful and WebApi) and haven't come across many examples of this. So if i am meant to send up the account ID in the HTTP request everytime and that means I must need someway of knowing that this guy who says he is account 13 really is account 13. What does that look like? Most websites have a login (would the login also be WebApi Controller?) and if username and password are legit we give him an account ID. All subsequent queries must contain that ID. After that it I am in the dark.
Ok a lot of googling, video watching and article reading I think that I want to go for the token based approach. Where you use the Authorize HTTP header. From a video that I was watching the guy mentioned that it was going to be built into WebApi in the next version. Do you know if this is available?
@uriDium It is still only available as pre-release. Here is some info on it brockallen.com/2013/08/07/…
Mill. In that case is it better to go with the cookie and csrf version for now?
1

You seem to be missing the information on how you want to implement authentication. As you correctly noted, using session to keep authentication information is not very restful. Here are the alternatives

  1. Implement your own OAuth provider or use third party (for example Azure ACS)
  2. Implement STS provider (this is only for soap though)
  3. Implement a custom token system, if you don't want to deal with the above two. Basic system would take user id, salt it and encrypt with private key - but don't quote me on how secure that would be...

In all the cases, the authentication information is stored in the cookie, similar to session cookie. But it is still stateless.

The standard way on the web seems to be the oauth way, in fact the standard VS MVC template even comes with OAuth consumer implemented out of the box. So the question is, how do you link the oauth token to your internal user ID. That's really pretty simple, as you implement the "registration" step after the new user is authenticated - you keep user ID and oauth token in the database table, to link the two.

That link is quick to retrieve, and can be cached for speed. I have heard some people implement that type of credentials caching for some very big systems (google docs).

2 Comments

I think I mentioned in my update that I was using an Auth token. I was just unsure about when and how to do the checks. Because the user might be requesting a resource that isn't identified by his account ID but by some other ID. For example api/CarServiceHistory/59 where 59 is the ID of the car. Do I need to change the URL to include his account ID? so api/CarServiceHistory/59/12. And then check the account ID in the auth token against the ID in the url? That seems messy. Or just always include the account ID in the auth token in my DB query. So if car 59 belonged to someone it would return 0
Including account id here would not be restful - restful would be to just have car ID. on side note, you may want to do api/Car/59/ServiceHistory instead. the access checks for the specific car would need to be done in the code

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.