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

Asked

Viewed 1,324 times

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 solve the following problems:

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

In terms of concept, the form is an app that works within the third-party website.

Each form will use Angularjs to send the 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 arose my question: how to identify if the data the API receives is from an authorized form?

At the moment just make a POST for the endpoint and the data will be recorded and logical that this is a security problem.

I thought I’d use the JWT (Json Web Token) but to do so would need to pass credentials to get a token, but since we are using Angularjs in the forms, just inspect the code and remove credentials.

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

  • There are many ways to solve your security problem. The simplest of them in my opinion is to send in the header of the POST request an http-Authorization key, containing a hash that identifies each form (hash of the source url for example).

  • @touchmx but this key could easily be copied and used in a CSRF attack.

  • @Andreicoelho but the IP can be "falsified" and it is also easy to get the server IP, so it would not solve the problem.

  • So it’s simple, implement Oauth2 in the requests. If it’s not enough anyway, implement an asymmetric key system. An SSL certificate on each domain also helps.

  • @touchmx how can Oauth2 help? Isn’t this protocol to use when you want to give your data access to third parties? This is not my case, I want to identify if I am receiving data from an authorized form. Oauth2 is also used for these cases?

1 answer

4


In my view you need to implement the mechanism CORS or protection against CSRF in its application.

CORS

With CORS, you can define which sources are allowed to perform certain actions in your application - for example, submit a form.

inserir a descrição da imagem aqui

(I got the picture at that link.)

For example, suppose your application’s front-end is in the.com site domain and the back-end is in the app.sitecom domain. Before the form is actually submitted, a request is made of the type OPTIONS to the back-end (what is called preflight) in order to know if the front-end can really send that data. The back-end 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.com domain can submit requests of the type POST back-end. Any other type of request, or requests from other sources, will receive a reply with status 403 Forbidden.

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

As you are using Symfony to develop the back-end, I recommend you take a look at this Bundle that facilitates the configuration of 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 its consensus. 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 cannot obtain this value.

If you notice the class CsrfProviderInterface, will note 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();
    }
}
  • Rodrigo, but this only works if the POST is done via browser, if for example, use a console and make POST Request, this lock will no longer work, right?

  • Right, the idea of CORS is that it is a protocol respected by browsers. I will adapt the response to comment on CSRF as well.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.