CORS и безопасность

CORS (Cross-Origin Resource Sharing) — это механизм, который управляет безопасным доступом веб-приложений к ресурсам на других доменах. По умолчанию браузеры применяют политику одинакового источника (Same-Origin Policy), которая запрещает скриптам обращаться к ресурсам, находящимся на другом домене, протоколе или порте. CORS позволяет частично ослабить эти ограничения, предоставляя контролируемый доступ к внешним API и сервисам.

Заголовки CORS

Основой механизма CORS являются HTTP-заголовки, которые сервер возвращает в ответ на запрос:

  • Access-Control-Allow-Origin — указывает, какие домены могут получать доступ к ресурсу. Значение * разрешает доступ с любого домена, но использование этого варианта ограничивает возможности применения некоторых заголовков и методов, требующих авторизации.
  • Access-Control-Allow-Methods — список HTTP-методов, разрешённых для кросс-доменных запросов (GET, POST, PUT, DELETE и др.).
  • Access-Control-Allow-Headers — перечень HTTP-заголовков, которые клиент может использовать при запросе.
  • Access-Control-Allow-Credentials — разрешает отправку куки и авторизационных данных вместе с запросом.
  • Access-Control-Expose-Headers — определяет, какие заголовки ответа видимы для JavaScript-кода на клиенте.
  • Access-Control-Max-Age — задаёт время в секундах, в течение которого браузер может кешировать результаты проверки CORS (preflight-запроса).

Preflight-запросы

Для запросов, которые используют нестандартные методы или нестандартные заголовки, браузер сначала отправляет preflight-запрос методом OPTIONS. Сервер должен ответить с заголовками Access-Control-Allow-*, подтверждающими, что данный запрос разрешён.

Пример preflight-запроса:

OPTIONS /api/data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, X-Custom-Header

Ответ сервера:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
Access-Control-Max-Age: 600

CORS в JavaScript

Для отправки кросс-доменных запросов используется объект fetch или XMLHttpRequest. Основные моменты:

fetch('https://api.example.com/data', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json'
  },
  credentials: 'include' // для отправки куки и токенов
})
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

Параметр credentials: 'include' активирует отправку авторизационных данных. Если сервер не разрешает креденшиалы через заголовок Access-Control-Allow-Credentials, браузер блокирует ответ.

Ограничения и риски

  • Разрешение доступа с любого домена (*) упрощает разработку, но делает ресурс потенциально уязвимым к XSS и CSRF-атакам.
  • Креденшиалы (cookies, Authorization) нельзя использовать вместе с универсальным *. Необходимо явно указывать домен источника.
  • Неправильная настройка заголовков может привести к утечке данных или отказу в работе приложений на клиентской стороне.
  • Preflight-запросы увеличивают время отклика, поэтому важно ограничивать их использование методами и заголовками, совместимыми с простыми запросами.

Безопасное использование

  1. Явно указывать разрешённые домены в Access-Control-Allow-Origin.
  2. Ограничивать набор разрешённых методов и заголовков.
  3. Для запросов с авторизацией всегда использовать точные домены и HTTPS.
  4. Минимизировать передачу чувствительных данных через кросс-доменные запросы.
  5. Кешировать preflight-запросы через Access-Control-Max-Age для уменьшения нагрузки.

Примеры серверной настройки

Node.js с Express:

const express = require('express');
const cors = require('cors');
const app = express();

const corsOptions = {
  origin: 'https://example.com',
  methods: ['GET', 'POST'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 600
};

app.use(cors(corsOptions));

app.get('/data', (req, res) => {
  res.json({ message: 'CORS настроен корректно' });
});

app.listen(3000);

NGINX:

location /api/ {
  add_header Access-Control-Allow-Origin https://example.com;
  add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
  add_header Access-Control-Allow-Headers "Content-Type, Authorization";
  add_header Access-Control-Allow-Credentials true;
}

Взаимодействие с библиотекой Cash

При работе с cash.js для динамических запросов к API CORS играет ключевую роль. Методы $.ajax, $.get, $.post наследуют ограничения браузера и требуют правильной настройки заголовков на сервере. Важно проверять, что:

  • Метод соответствует политике CORS сервера.
  • Заголовки запроса допустимы.
  • Для передачи токенов или куки используется опция xhrFields: { withCredentials: true }.

Пример с cash.js:

$.ajax({
  url: 'https://api.example.com/data',
  method: 'POST',
  data: JSON.stringify({ key: 'value' }),
  contentType: 'application/json',
  xhrFields: {
    withCredentials: true
  },
  success: function(response) {
    console.log(response);
  },
  error: function(err) {
    console.error('Ошибка CORS:', err);
  }
});

Соблюдение этих правил обеспечивает безопасное и корректное взаимодействие фронтенда с внешними сервисами через библиотеку Cash.