How to authenticate the application and authorize it to consume a restful API

6

In a given project, it was necessary to create a restful API that receives data from various forms spread across multiple sites hosted on different servers.

The API was created to resolve the following issues:

  • Maintenance: have a single data entry in the database.
  • Security: Since each form writes directly to the database, there is a huge security problem.

In concept terms, the form is an app that works inside the third-party site.

Each form will use AngularJS to send data via POST.

The problem is that at the moment the API does not have any type of authentication (calm, it is still in development) and here my question came up: How to identify if the data that the API receives is of an authorized form?

At the moment it is enough to make a POST to the endpoint and the data will be recorded and it is logical that this is a security problem.

I thought about using JWT (Json Web Token) but for this it would be necessary to pass credentials to get a token, but since we are using AngularJS in the forms, it was enough to inspect the code and remove the credentials.

On the server side I'm using Symfony to build the API.

    
asked by anonymous 13.06.2016 / 16:49

1 answer

4

In my opinion, you need to implement the CORS mechanism or protection against CSRF in your application.

CORS

With CORS, you can define which sources are allowed to perform certain actions in your application - such as submitting a form.

(I'vetakenthe image on this link .)

For example, suppose the front end of your application is in the domain site.com and the backend is in the domain app.site.com. Before the form is actually submitted, a request of type OPTIONS is made to the backend (what is called preflight ) in order to know if the front end can actually send that data. The backend then responds with something like:

Access-Control-Allow-Origin: http://site.com
Access-Control-Allow-Methods: GET, POST

This means that only applications hosted on the site.com domain can send requests of type POST to the backend. Any other type of request, or requests from other sources, will receive a response with status 403 Forbidden .

Note that CORS is respected only by more modern browsers - if you attempt to submit the form using an old browser or through the command line this mechanism will not work.

Since you are using Symfony to develop the backend, I recommend that you take a look at this bundle that makes it easy to setup CORS in your application: NelmioCorsBundle .

CSRF

An attack through CSRF, XSRF or sea-surf is characterized by the fact that the user sends malicious data to the server without his consent. These attacks can be easily avoided by requiring a cookie or a posted value valid only for that data submission or a series of submissions.

Symfony has native protection against CSRF attacks. All forms generated through the application have a hidden field whose value is a hash valid only for a given form within a given session so that malicious applications can not get that value.

If you notice the CsrfProviderInterface class, you will notice the following methods that implement CSRF protection:

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;

/**
 * Marks classes able to provide CSRF protection
 *
 * You can generate a CSRF token by using the method generateCsrfToken(). To
 * this method you should pass a value that is unique to the page that should
 * be secured against CSRF attacks. This value doesn't necessarily have to be
 * secret. Implementations of this interface are responsible for adding more
 * secret information.
 *
 * If you want to secure a form submission against CSRF attacks, you could
 * supply an "intention" string. This way you make sure that the form can only
 * be submitted to pages that are designed to handle the form, that is, that use
 * the same intention string to validate the CSRF token with isCsrfTokenValid().
 *
 * @author Bernhard Schussek <[email protected]>
 */
interface CsrfProviderInterface
{
    /**
     * Generates a CSRF token for a page of your application.
     *
     * @param string $intention Some value that identifies the action intention
     *                          (i.e. "authenticate"). Doesn't have to be a secret value.
     */
    public function generateCsrfToken($intention);

    /**
     * Validates a CSRF token.
     *
     * @param string $intention The intention used when generating the CSRF token
     * @param string $token     The token supplied by the browser
     *
     * @return Boolean Whether the token supplied by the browser is correct
     */
    public function isCsrfTokenValid($intention, $token);
}

The implementation of this interface shows that the token is only valid if it was previously generated:

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;

/**
 * Default implementation of CsrfProviderInterface.
 *
 * This provider uses the session ID returned by session_id() as well as a
 * user-defined secret value to secure the CSRF token.
 *
 * @author Bernhard Schussek <[email protected]>
 */
class DefaultCsrfProvider implements CsrfProviderInterface
{
    /**
     * A secret value used for generating the CSRF token
     * @var string
     */
    protected $secret;

    /**
     * Initializes the provider with a secret value
     *
     * A recommended value for the secret is a generated value with at least
     * 32 characters and mixed letters, digits and special characters.
     *
     * @param string $secret A secret value included in the CSRF token
     */
    public function __construct($secret)
    {
        $this->secret = $secret;
    }

    /**
     * {@inheritDoc}
     */
    public function generateCsrfToken($intention)
    {
        return sha1($this->secret.$intention.$this->getSessionId());
    }

    /**
     * {@inheritDoc}
     */
    public function isCsrfTokenValid($intention, $token)
    {
        return $token === $this->generateCsrfToken($intention);
    }

    /**
     * Returns the ID of the user session.
     *
     * Automatically starts the session if necessary.
     *
     * @return string The session ID
     */
    protected function getSessionId()
    {
        if (version_compare(PHP_VERSION, '5.4', '>=')) {
            if (PHP_SESSION_NONE === session_status()) {
                session_start();
            }
        } elseif (!session_id()) {
            session_start();
        }

        return session_id();
    }
}
    
15.06.2016 / 14:06