Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I have a single page application where everything in the UI is javascript driven so any operation requiring data or updating of data is done via Ajax requests. The way things are setup right now is that my Ajax called are made to one ajax.php file which takes care of routing them to the proper service method. This works well but there's one problem. Whenever I have operations that require a user ID, I don't want to pass them as part of an ajax request so as to prevent tampering. Since I have a session running server-side, I have a Security service from which I can retrieve the user ID of the user currently logged in. The security does so by using a Session object that abstracts the access to the session (in my case, $_SESSION in PHP) implementing an interface. My security service gets injected into other services as needed, so for example, if I have a PostService->findUserPosts() method, that method uses the getUserId() method of the injected SecurityService object to figure out the user ID instead of having it passed as an argument.

The problem is I suspect (and others seem to agree) that not passing in the user ID to the service is bad design. But how do I get around doing that without sending the user ID via ajax? I can think of a few options, none of which I really like or know of a good way to implement:

1) Add a controller layer on the server side, in which case I can call SecurityService->getUserId() in the controller and pass in a separate call to PostService->findUserPosts($userId); the problem with this approach is that it requires me to add a bunch of controllers that are for the most part pretty redundant. As it is, I already a PostService->findPosts() method that basically just calls PostGateway->findPosts(). I can live with that redundancy because there will be a lot of places where the service will need to do more. But adding a third layer with a controller with a findPosts() method, that seems too much of a pain.

2) Pass the user ID in the ajax request and come up with some kind of tampering mechanism to make sure the ajax request can't be altered. I could do that by hashing the URL string of my request and appending that as a parameter, and then on the server comparing the hash. This is more obfuscation than anything though, since the hash would need to be generated client side, which means somebody looking through the code could figure out how the hash is generated.

3) Keep things as they are, bearing in mind that my user ID is at least not hard coded and various implementations of the Session object could be used, that way the user ID can be retrieved in multiple ways, not tying me to the PHP $_SESSION object.

4) Use a hybrid method of #1 and the current setup where if I need to access session info I use a controller, otherwise I call the service directly. This could work, though it doesn't feel like a clean, unified approach.

I tried to figure this out with two separate questions dealing with separate smaller problems, but I think the lack of context made it difficult to answer, so figured I'd formulate a question with the entire problem. Suggestions are welcome. Suggestions are welcome. Or should I say, much needed!

UPDATE: maybe a valid compromise would be to have a PostService->findByUser($userId) method and then have a UserService->findPosts() method that gets the userId from SecurityService the same way PostService is doing now?

share|improve this question
1  
Authenticate your ajax requests. –  Robert Harvey Feb 13 '13 at 17:43
    
How would I authenticate them in a way that tampering with them isn't possible? I could ascertain the identity of the user, but how would I ensure that the userId being sent hasn't been altered by the user? Unless I'm misunderstanding what you mean by authenticating. –  Rocket04 Feb 13 '13 at 18:02
    
Allow the client to pass in a userId in the request and just validate that the userId matches the userId in the session object (if that is what is required for authorization). AOP can help you do this without muddying up your business logic. –  smp7d Feb 13 '13 at 18:25
    
@RobertHarvey I believe the user is already authenticated in this scenario. Maybe you mean "Perform authorization checks on your ajax requests"? –  smp7d Feb 13 '13 at 18:28
    
@smp7d So where would that validation take place? Within the PostService itself? In which case, what's the difference between doing that and what I have now. Passing the $userId as an argument gives flexibility in that you can technically get posts for any user, but if you add a check in there than it's pretty much the same as my current approach. If you do it outside, then seems I'd need some kind of extra step prior to invoking the service. So either a server-side controller or some convention so ajax.php does the check. Starting to think there is no pleasant solution to this. –  Rocket04 Feb 13 '13 at 18:46
show 2 more comments

1 Answer

I suggest what you do, rather than having user IDs visible at the client end, you implement a token.

It would work like this:

  1. User logs on, you take note of this on the backend and generate a token which has an expiry
  2. You return the token to the client (browser)
  3. For each subsequent request, you pass the token to the server as a security assurance. For each successful call you increment the token expiry by +n minutes/hours or whatever is suitable for you. Before executing the service request you check to ensure the token is valid and has not expired.
  4. When the user logs out, or the token expires, any subsequent service calls will fail.

My suggestion would be to require both a user ID and a token. The user ID is going to be useless without the token, and requiring the ID is going to give that little bit extra security against spoofing the tokens. Use PHPs uniqid function to generate a unique token as a starting point.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.