Secure Third-Party Access to a REST API

Enforcing security policies on web applications these days is ‘relatively easy’ using the correct headers in HTTP responses. Take the following example of an application:

  • https://example.org delivers the frontend of the great example applications to its users. This is the domain a user knows.
  • https://api.example.org is handling all information. If the user logs in, the frontend sends a request to this domain and changes according to the response.
control Multiple Values Access-Control-Allow-Origin
Stay in control of who can access your services.

In this case, it should not be possible for anybody else but the frontend to access the API. By now, browsers try to protect the user and try to block malicious requests. For example, such a request would be to visit https://malicious.example.org, and the web application there would try to access https://api.example.org. To prevent that, the webserver delivering the API can send the Access-Control-Allow-Origin header as follows:

Access-Control-Allow-Origin: https://example.org

This way, the malicious website has no more access to our API if the user uses a recent browser.

code Multiple Values Access-Control-Allow-Origin

No security headers set? Better use them when developing a web application.

If the application setup becomes more complex, things get much more complicated to configure. Assume that there are more systems:

  • https://staging.example.org, which holds the staging environment of the frontend.
  • https://payment.example.org, the external payment provider that needs to access the API to confirm payments
  • https://localhost:8080 is the local server that the developer uses to try out changes locally.

There is no possibility for the Access-Control-Allow-Origin header to contain multiple domains, like separating different domains via spaces or commas. Besides specifying a single domain, only ‘*’ is another valid option, which would allow access from everywhere. And this is no secure option in this case.

Therefore the API needs to check the origin of the request and adjust the header field accordingly. For example, it can use the ‘Origin’ header on the request to check who is trying to access the resource. If it is one of the allowed domains, it sets the Access-Control-Allow-Origin accordingly. Otherwise, it just sets it to https://example.org so that the browser blocks the request. The following example explains how a Laravel project can make use of middleware to set this header correctly:

<?php

class CORSMiddleware
{
   [...]
   /**
    * Add Access Control headers.
    *
    * @param \Illuminate\Http\Request  $request
    * @param \Illuminate\Http\Response $response
    *
    * @return \Illuminate\Http\Response
    */
   protected function addAccessControlHeaders($request, $response)
   {
      $possibleOrigins = [
         'https://example.com',
         'https://api.example.com',
         'https://staging.example.com',
         'https://payment.example.org',
      ];

      if (env('APP_ENV') == 'development') {
         $possibleOrigins[] = 'https://localhost:8080';
      }

      if (in_array($request->header('origin'), $possibleOrigins)) {
         $origin = $request->header('origin');
      } else {
         $origin = 'https://example.com';
      }

      $headers = [
         'Access-Control-Allow-Origin'      => $origin,
         'Vary'                             => 'Origin',
      ];
      
      foreach ($headers as $header => $value) {
         $response->header($header, $value);
      }

      return $response;
   }
}

In addition to the Access-Control-Allow-Origin header, the Vary header is set so that the browser knows that this API’s response may vary depending on the origin. Therefore it will not use any cached response when the API is called from a different frontend site in the same browser.

Sources:

See If Your Web App Or API Has Security Vulnerabilities

SCAN FOR FREE NOW