Безопасный доступ третьих лиц к REST API

Применение политик безопасности в веб-приложениях в наши дни «относительно просто» благодаря использованию правильных заголовков в ответах HTTP. Возьмем следующий пример приложения:
- https://example.org предоставляет своим пользователям внешний интерфейс замечательных примеров приложений. Это домен, который знает пользователь.
- https://api.example.org обрабатывает всю информацию. Если пользователь входит в систему, то внешний интерфейс отправляет запрос в этот домен и изменяется в соответствии с ответом.
В этом случае ни у кого другого, кроме внешнего интерфейса, не должно быть возможности получить доступ к API. К настоящему времени браузеры пытаются защитить пользователя и пытаются блокировать вредоносные запросы. Такой запрос будет, если пользователь посещает https://malicious.example.org, а веб-приложение попытается получить доступ к https://api.example.org. Чтобы предотвратить это, веб-сервер, предоставляющий API, может отправить заголовок Access-Control-Allow-Origin следующим образом:
Access-Control-Allow-Origin: https://example.org
Таким образом, вредоносный веб-сайт больше не имеет доступа к нашему API, если пользователь использует последний браузер.

Если установка приложения становится более сложной, настройка становится намного сложнее. Предположим, что систем больше:
- https://staging.example.org, который содержит промежуточную среду внешнего интерфейса.
- https://payment.example.org, который является внешним платежным провайдером, которому необходим доступ к API для подтверждения платежей.
- https://localhost:8080 — это локальный сервер, который разработчик использует для локального опробования изменений.
Заголовок Access-Control-Allow-Origin не может содержать несколько доменов, например, разделяя разные домены пробелами или запятыми. Помимо указания одного домена, только '*' является еще одним допустимым параметром, который разрешает доступ отовсюду. И это не безопасный вариант в данном случае.
Поэтому API необходимо проверить источник запроса и соответствующим образом скорректировать поле заголовка. Он может использовать заголовок Origin в запросе, чтобы проверить, кто пытается получить доступ к ресурсу. Если это один из разрешенных доменов, он соответствующим образом устанавливает Access-Control-Allow-Origin. В противном случае он просто устанавливает https://example.org, чтобы запрос блокировался браузером. В следующем примере показано, как проект Laravel может использовать промежуточное ПО для правильной установки этого заголовка:
<?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;
}
}
В дополнение к заголовку Access-Control-Allow-Origin устанавливается заголовок Vary, чтобы браузер знал, что ответ этого API может различаться в зависимости от происхождения. Поэтому он не будет использовать какой-либо кешированный ответ, когда API вызывается с другого внешнего сайта в том же браузере.