Symfony a RESTFul app: Security ( Securing the token path – FIXED )

In the previous post we set up a basic symfony project aiming to develop a RESTFul solution, we also talk about the security and how to implement an OAuth2 service, but we also saw a security flaw in one of the most important end points, the /oauth/v2/token one which will deliver the access token to the user.

As always all the code on this series can be found on this github repository under the symfony2_restful_example branch.

Apology:

First i want to apologize since the previous post aims to fix that security flow of the user password as plain text on the request and after a while i realize i didn’t solve it since it was required even when we use the “rest” authentication provider that we implement on that post. This post will fix that.

The issue:

Lets point the issue one more time, the path to request the access token requires us to provide several data (client_id, client_secret, grant_type, redirect_uri, username, password) been the first four identification of which oauth client are you using and the remaining two as user authentication.

FOSOAuthBundle uses oauth2-php which implements the oauth2 protocol defined in the following draft. That document in the section #4.3.2 explains that in order to request an access token the user need to provides his username and password, so FOSOAuthBundle requires you to pass those fields along in the request as query parameters following the specification.

The issue comes since they are relying in the user provider you configure to perform the authentication (username and password validation) which in most of the cases, included our example; we use FOSUserBundle and this one requires the password in plain text.

How to fix it:

The draft do not point to any specific authentication mechanism so its OK if we made some tweaks and try to fix this issue with another approach.

So what we really need its to send the user password hashed and then on server side validate that password.

To accomplish this we will need to implement an Storage service that FOSOAuthServerBundle will use.

<?php

namespace AppBundle\Services;

use FOS\OAuthServerBundle\Model\ClientInterface;
use FOS\OAuthServerBundle\Storage\OAuthStorage as OAuthStorageBase;
use OAuth2\Model\IOAuth2Client;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

class OAuthStorage extends OAuthStorageBase
{
    public function checkUserCredentials(IOAuth2Client $client, $username, $password)
    {
        if (!$client instanceof ClientInterface) {
            throw new \InvalidArgumentException('Client has to implement the ClientInterface');
        }

        try {
            $user = $this->userProvider->loadUserByUsername($username);
        } catch (AuthenticationException $e) {
            return false;
        }

        if ($user->getPassword() !== $password) {
            return false;
        }

        return array(
            'data' => $user,
        );
    }
}

And the configuration

# Learn more about services, parameters and containers at
# http://symfony.com/doc/current/book/service_container.html
parameters:
#    parameter_name: value

services:
#    service_name:
#        class: AppBundle\Directory\ClassName
#        arguments: ["@another_service_name", "plain_value", "%parameter_name%"]
    oauth.storage:
        class: AppBundle\Services\OAuthStorage
        arguments:
            - "@fos_oauth_server.client_manager"
            - "@fos_oauth_server.access_token_manager"
            - "@fos_oauth_server.refresh_token_manager"
            - "@fos_oauth_server.auth_code_manager"
            - "@fos_oauth_server.user_provider"
            - "@security.encoder_factory"
        public: false

As you can see we overwrite the checkUserCredentials function and use a simple compare between the user current password and the provided one.

Lets point the FOSOAuthServerBundle configuration to use our new service:

fos_oauth_server:
...
    service:
        ...
        storage:       oauth.storage

Now we can sent our user password hashed to the token path.

PR:

There is a PR #357 on the FOSOAuthServerBundle that i create with this solution, lets wait if it gets approved or a better solutions comes out.

Conclusion:

We need to secure user data as much as possible and there are a lot of ways to doing that, which one its the better one? Hard to say, you need to study a lot about this topics and then find what ever you think its the best option for your current project.

Series:

  1. Motivation
  2. REST Levels 0, 1, 2 ( FOSRestBundle, JMSSerializerBundle )
  3. REST Levels 0, 1, 2 ( FOSUserBundle )
  4. REST Levels 0, 1, 2 ( NelmioApiDocBundle )
  5. REST Levels 3 ( BazingaHateoasBundle )
  6. Security ( FOSOAuthServerBundle )
  7. Security ( Securing the token path )
  8. Security ( Securing the token path – FIXED )
Advertisements

7 Comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s