Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Sessions vs Tokens for API authentication

Tags:

api

cakephp

I have built a simple test API for a CakePHP application that will let a user login from a mobile device (or any device for that matter) and get a JSON response. This API could be used for a mobile app built in PhoneGap.

The login method looks like so:

public function login()
{
    if($this->request->is('post'))
    {
        // Use custom method in Model to find record with password params
        $findUser = $this->User->findUser(
            $_POST['username_or_email'],
            AuthComponent::password($_POST['password'])
        );

        // If a user exists and matches params
        if($findUser)
        {                           
            $this->User->id = $findUser['User']['id'];

            $this->autoRender = false;
            $this->response->type('json');
            $this->response->body(json_encode(array('authenticated'=>true,'message'=>__('You have been logged in successfully'))));
        }
        else
        {
            $this->autoRender = false;
            $this->response->type('json');
            $this->response->body(json_encode(array('authenticated'=>false,'message'=>__('Username or password is incorrect'))));
        }

    }
    else
    {
        $this->autoRender = false;
        $this->response->type('json');
        $this->response->body(json_encode(array('message'=>'GET request not allowed!')));
    }
}

The mobile device (or any API user) can send their login details and then they get the request as JSON as true or false for authenticated. This boolean is NOT used to give the user access, it instead tells the mobile app if they can see certain screens and they ONLY get the data or can send data if the session exists!

As just stated, they are also actually logged into the API itself on the device so if they visit the website directly (from that device) they will have a session and see the same response for the JSON.

So essentially a user remains logged in for the duration of the session on the device they communicated with the server on. This is different to a token which would need to be passed for every request, where as in this example they have a session.

Now the questions...

  1. Is it bad practice for the user to be 'actually' logged into the API with a session like shown above? It seems like the most secure way to handle authentication for a device as it's using the same logic as the direct web root.

  2. I've seen some APIs use access tokens instead which I've also implemented (user gets their token returned instead of the boolean and no session is created). But from what I can tell, this seems like more work as then I need to check for the access token against a user record every time a request is made.

like image 589
Cameron Avatar asked Feb 17 '13 20:02

Cameron


1 Answers

edit
For the sake of clarity, I am not a supporter of REST, I AM a supporter of RESTful/RESTlike services. If you look at all of the API's on the internet, very few actually stick to one standard. Whatever scheme you choose will depend on your specific problem-space. Just try to be secure and use intuitive design choices (ie dont name a service "cats" if it returns info about "dogs")
end edit

It is good practice in RESTful API's to manage some form of session/tokenizing scheme. Really the ideal (at least in my opinion, there are many schools of thought on this problem) setup involves rolling tokens.

If you are at all concerned with the security of your API, then permissions should be managed out of your database layer. Yes, this creates a bottleneck, BUT THAT IS ACTUALLY A GOOD THING. Needing to hit the database every single time to validate a client's token adds an extra step in the entire process. This slows down the API, which is actually desireable in a secure system. You don't want a malicious individual to be able to hit your API 3000 times a second, you want their requests to hang for a (somewhat) sizeable fraction of a second.

This is similar to MD5 hashing algorithms. Many of them recalculate the hash a few hundred times, with random pauses in between. This helps to keep a malicious client from attempting to brute force a password (by making it take more time to test each variation of the password string). The same applies to your API.

The other benefit, is that if you DO have a malicious user trying to log in over and over again, if you are managing them from the database layer, then you can red flag their IP Address/username/what-have-you and just drop their requests at step 1.

Anyway, for a suggested process (with rolling tokens, you can cut out parts of this if it seems overkill, but this is hella secure):

  1. User hits a 'login' service, this requires a username/password, and returns two tokens, a Private Access Token and a Public Request Token (the server stores these tokens in the db).
  2. The client stores these Tokens in a secure place
  3. User accesses another endpoint to push/pull some data
    • Request includes a timestamp
    • Request includes the Public Request Token
    • Request includes an Access Token=> This token should be a MD5 hash of the string resulting from concatenating the timestamp string to the end of the Private Access Token string
  4. The server takes the Public Request Token, uses that to lookup the Private Access Token that was stored
    • The server takes that Private Access Token, and concatenates on the Timestamp String, it then takes the MD5 of this string
    • If the new Access Token matches the one that the client sent the server, HURRAY, this client is validated, so push/pull the data
  5. (Optional) The server generates new tokens on every request, and returns them to the client. This way every transaction invalidates the old tokens, and if there was some kind of man-in-the-middle attack occurring, if the VALID user has already completed their request, the malicious user now has invalid tokens and can't start messing with your API. This scheme tries to ensure that a malicious user can not expect to intercept a single communication between the server and the client, and still gain access to the system. If they do, then the REAL user should immediately get invalidated tokens. Which should then trigger their API client to hit the 'login' service AGAIN, getting new valid tokens. This once again kicks the malicious user out of the system.

This scheme is not 100% secure, no user access system ever will be. It can be made more secure by adding expiration dates on tokens. This scheme also has the added benefit that you can assign specific permissions to users/tokens (ie Read-Only access, only certain End-Points can be seen, etc)

This is not the only way you can do things, I would look up other Authentication Schemes and take what you want from each of them (OAUTH is a good place to start, then I'd look at Facebook/Twitter/Instagram)

like image 177
G. Shearer Avatar answered Oct 03 '22 01:10

G. Shearer