У меня есть простой скрипт PHP, в котором я пытаюсь выполнить междоменный запрос CORS:
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: *");
...
Тем не менее я получаю сообщение об ошибке:
Поле заголовка запроса X-Requested-With не разрешено Access-Control-Allow-Headers
Что-то я делаю неправильно?
Ответ 1
Access-Control-Allow-Headers не допускает «*» приемлемого значения.
Вместо звездочки следует отправлять принятые заголовки (сначала X-Requested-With, как указано в ошибке).
Согласно MDN Web Docs 2021:
Значение «*» считается специальным подстановочным знаком только для запросов без учетных данных (запросы без файлов cookie HTTP или информации аутентификации HTTP). В запросах с учетными данными оно рассматривается как буквальное имя заголовка «*» без специальной семантики. Обратите внимание, что заголовок авторизации не может содержать подстановочные знаки и всегда должен быть указан явно.
Ответ 2
Правильная обработка запросов CORS требует больше усилий. Вот функция, которая ответит более полно (и правильно).
/**
* Пример CORS-совместимого метода. Он разрешает любые запросы GET, POST или OPTIONS из любого
* места.
*
*/
function cors() {
if (isset($_SERVER['HTTP_ORIGIN'])) {
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // кэш на 1 день
}
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
// можно использовать в PUT, PATCH, HEAD и т. п.
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit(0);
}
echo "Это CORS!";
}
Замечания по безопасности
Когда браузер хочет выполнить междоменный запрос, он сначала подтверждает, что это нормально, с помощью «специального» запроса к URL. Разрешив CORS, вы сообщаете браузеру, что ответы с этого URL-адреса могут быть переданы другим доменам.
CORS не защищает ваш сервер. CORS пытается защитить ваших пользователей, сообщая браузерам, какие ограничения должны быть на обмен ответами с другими доменами. Обычно такой обмен категорически запрещен, поэтому CORS — это способ проделать брешь в обычной политике безопасности браузера. Этих брешей должно быть как можно меньше, поэтому всегда сверяйте HTTP_ORIGIN с каким-то внутренним списком.
Здесь есть некоторые опасности, особенно если данные, которые обслуживает URL, обычно защищены. Вы фактически разрешаете контенту браузера, который был создан на каком-то другом сервере, читать (и, возможно, манипулировать) данные на вашем сервере.
Если вы собираетесь использовать CORS, внимательно прочтите протокол (он довольно маленький) и попытайтесь понять, что вы делаете. Для этой цели в образце кода приведен ссылочный URL.
Безопасность заголовка
Было замечено, что заголовок HTTP_ORIGIN небезопасен, и это так. Фактически все заголовки HTTP небезопасны для различных значений этого термина. Если заголовок не включает проверяемую подпись/hmac или весь разговор не аутентифицирован через TLS, заголовки — это просто «то, что было отправлено браузеру».
В этом случае браузер сообщает: «Объект из домена X хочет получить ответ от этого URL-адреса. Это нормально?». Суть CORS состоит в том, чтобы иметь возможность ответить: «Да, я разрешаю это».
Ответ 3
Многие описания не упоминают, что элементов Access-Control-Allow-Origin недостаточно. Вот полный пример, который мне подходит:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, DELETE, PUT, PATCH, OPTIONS');
header('Access-Control-Allow-Headers: token, Content-Type');
header('Access-Control-Max-Age: 1728000');
header('Content-Length: 0');
header('Content-Type: text/plain');
die();
}
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
$ret = [
'result' => 'OK',
];
print json_encode($ret);
Ответ 4
Если вы хотите создать службу CORS в PHP, можно использовать этот код в качестве первого шага в вашем файле, который обрабатывает запросы:
if(isset($_SERVER["HTTP_ORIGIN"])) {
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
} else {
header("Access-Control-Allow-Origin: *");
}
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Max-Age: 600"); // кэш на 10 минут
if($_SERVER["REQUEST_METHOD"] == "OPTIONS") {
if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_METHOD"]))
header("Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT");
if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"]))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
exit(0);
}
Web