Как стать властелином CORS

Как стать властелином CORS
Переведенное изображение fatimamo.com
👋
Хочешь поучаствовать в жизни сайта? Мы ищем авторов!
Перевод статьи Фатимы Мохамед: Become a CORS Wizard 🧙‍♀️
Оглавление

Это случилось. 😲 Вы работали не покладая сил над своим фронтенд-веб-приложением, но как только оно пытается получить ресурс с другого сервера, чтобы отобразить его, вы получаете эту проклятую ошибку CORS. Не бойтесь! Мы поставим ее на своей место, досконально изучив, что такое CORS, почему эти ошибки случаются в ваших приложениях, и как их разрешить разными способами.

Терминология CORS

Прежде чем мы продолжим, давайте определим некоторые важные термины.

Origin (буквально, "источник") - это url, состояющий из трех частей:

  • Схемы URI - например, http:// или https://
  • Хоста - например, twitter.com
  • Номера порта - например, 80 для HTTP и 443 для HTTPS. Вы обычно не видите их в адресной строке, потому что они дописываются автоматически, но если вы попробуете ввести в адресной строке https://twitter.com:443, то увидите, что сайт открылся правильно.

HTTP-запросы - это запросы, которые веб-приложение выполняет, чтобы обращаться к ресурсам на сервере. Существует несколько видов HTTP-запросов, в том числе GET (позволяющий получать ресурсы), DELETE (позволяющий удалять ресурсы) и т.д. Ниже представлен пример HTTP-запроса, который происходит, когда вы кликаете по чьему-нибудь профилю в twitter, чтобы увидеть их твиты. Веб-приложение отсылает HTTP-запрос на http://twitter-api.com, который отвечает списком твитов.

Переведенное изображение fatimamo.com

Запросы Cross-Origin - это запросы, сделанные с одного origin на другой origin. Например, вы можете ввести в поиск картинки с котами на https://flikr.com, и это приложение обратится к https://image.com/api, чтобы запросить эти самые картинки для вас.

Переведенное изображение fatimamo.com

Заголовки HTTP - это данные, которые описывают отправленный запрос или ответ. По сути они они предоставляют дополнительную информацию, как, например, текст "обо мне" в вашем профиле в twitter. Примеры заголовков Host и access-control-allow-origin можно увидеть на рисунке ниже.

Переведенное изображение fatimamo.com

Прокси - это сервер, который выступает посредником между клиентом (вашим браузером) и другим сервером.

Что такое CORS?

CORS (Cross-Origin Resource Sharing, разделение ресурсов между origin) - это набор правил, которые браузеры применяют, чтобы защитить веб-приложения от нежелательного доступа. (+1 за безопасность 🥳) Один из способов, каким браузеры это делают: они проверяют ответ HTTP, чтобы убедиться, что приложению HTTP был разрешен доступ к данному ресурсу. Это поведение по-умолчанию для всех браузеров, если только браузер еще не знает, что приложение уже обладает доступом к ресурсу.

Переведенное изображение fatimamo.com

Как работает CORS?

Если оглядеть свысока, CORS работает одним из двух способов, в зависимости от типа запроса, который выполняет приложение. Давайте рассмотрим оба способа на примере просмотра и редактирования твита (да, теперь их можно редактировать).

Когда ваше веб-приложение хочет сделать GET-запрос к твиту, не изменяя его, браузер проверяет, что ответ сервера содержит заголовок access-control-allow-origin, который задан на origin приложения-клиента (например, на https://twitter.com или на *, если все origin в интернете имеют доступ к этому ресурсу). Это типичное поведение для запросов GET.

Когда ваше приложение хочет изменить твит, дело происходит несколько иначе.

  • Браузер отправляет предварительный запрос - pre-flight request - маленький запрос с методом OPTIONS, который сообщает серверу информацию о запросе, который вы пытаетесь сделать, чтобы обновить твит. Бэкенд проверяет заголовки в предварительном запросе, чтобы убедиться, что запрос, который вы хотите сделать, вам разрешен. Некоторые заголовки, которые дополнительно добавляются в предварительный запрос - это Access-Control-Request-Method и Access-Control-Request-Headers. Они сообщают серверу, с каким методом вы обращаетесь и т.д. Этот дополнительный слой защиты нужен, потому что запросы, вносящие изменения в ресурсы, с большей вероятностью могут иметь разрущающее воздействие, чем запросы, которые не вносят изменения.
  • Как только сервер получит предварительный запрос, он ответит, разрешен ли запрос, описанный в заголовках. Взгляните на упрощенный диалог между браузером и бэкендом ниже, чтобы лучше понять это.
Переведенное изображение fatimamo.com

Почему у вас происходит ошибка CORS?

Одна из самых частых причин, почему происходит ошибка CORS, состоит в том, что сервер, которому вы отправляет запросы, не добавляет заголовок access-control-allow-origin в ответ. Или добавляет, но origin фронтенда вашего приложения нет в списке разрешенных origin. Решения ниже должны помочь решить это. Более длинный список других причин ошибок CORS вы можете найти здесь.

Несколько способов исправить ошибку CORS

Включить CORS на бэкенде

Что под этим имеется в виду? Включение CORS на API и добавление origin фронтенда в список разрешенных origin.

  • Плюсы: Вы можете решить проблему изнутри проекта несколькими строками кода.
  • Минусы: Нужно сделать обновление на бэкенде, к которому у вас может быть доступ, а может и не быть.

Пример: Вот как я это сделала в одном из своих недавних проектов, который использовал фреймворк Phoenix на бекэнде. Сперва я нашла библиотеку CORS Corsica, которая добавила бы мой фронтенд в список разрешенных origin. Затем я установила ее в качестве зависимости (зависимость - это внешний код, который требуется вашему коду для работы) в мой бэкенд. В Elixir зависимости добавляются в файл mix.exs как в примере ниже.

defp deps do
[
...
{:corsica, "~> 1.1.3"}
]
end

Затем я добавила origin моего фронтенда в список разрешенных origin, дописав plug Corsica и указав origin фронтенда в список origin, которым разрешен доступ к ресурсам моего бэкенда.

plug Corsica, origins: ["http://localhost:3000"]

Для демонстрации в этом посте я захардкодила ссылку на фронтенд-приложение, но в зависимости от того, какой фреймворк вы используете, вы можете программно брать URL origin из окружения, в котором запускается приложение. В многих других бэкенд-фреймворках CORS подключается похожим образом.

Отправка запросов через прокси, который добавляет нужные заголовки в ответ

Код такого решения целиком живет в вашем фронтенде. Но что именно оно означает? Вместо того, чтобы ваше приложение отсылало запросы к https://twitter.com/, вы можете использовать CORS-прокси наподобие CORS anywhere, который расположен на https://cors-anywhere.herokuapp.com/, вот так: https://cors-anywhere.herokuapp.com/https://twitter.com.

  • Плюсы: Ошибки CORS больше нет, а у вас есть доступ к ресурсу, который вы запрашивали, потому что прокси добавил нужные заголовки.
  • Минусы: Запрос может занять больше времени, так как скорость его обработки теперь целиком зависит от того, как быстро внешний прокси отправит, примет и добавит в ответ заголовки, которые осчастливят CORS. Кроме того, вам нужно доверять такому прокси на 100%, так как он сможет читать и делать что угодно с данными, которые вы получаете в ответ. Я убедительно советую не использовать такой прокси ни для чего, кроме самых тривиальных случаев.
Переведенное изображение fatimamo.com

Отправка запросов через собственный прокси

Чтобы сократить потенциальную задержку, которая может случиться, пока внешний прокси добавляет заголовки за вас.

  • Плюсы: Не нужно вносить изменения в бэкенд. Можно добавить мощностей серверу прокси, чтобы ускорить его.
  • Минусы: Нужно хостить прокси, но если вы уже хостите где-то свой бэкенд, то, скорее всего, это не такой большой минус для вас. Пример того, как это можно сделать, доступен здесь.

Временно отключить CORS в браузере

  • Плюсы: Можно заниматься разработкой и тестировать запросы между разными origin без лишних настроек.
  • Минусы: Это решение должно быть временных и оно только сработает на время разработки. Ошибка CORS все еще останется, когда вы задеплоете свое приложение в production.

В зависимости от используемого браузера, это можно сделать либо установкой расширения для обхода CORS, либо, если вы используете Safari, как я (да-да, осуждайте меня, мне без разницы), вы можете временно вручную отключить CORS в меню разработчика, нажав на опцию ниже:

Изображение fatimamo.com

Заключение

Короче говоря, CORS - это набор правил, которые универсально применяются каждым браузером для обеспечения безопасности веб-приложений и их пользователей. И, как и в большинстве случаев в мире разработки, существует несколько способ исправить потенциальные ошибки CORS. Я знаю, что CORS может быть головной болью, но теперь вы знаете, что это такое, и с чем его едят, у вас все получится!

А теперь идите и дайте этим ошибкам CORS по щам! 💪

Изображение fatimamo.com

Хотите погрузиться глубже?

Вот дополнительное чтение на дом:

Благодарности

Отдельное спасибо Monique, Izzy, David, Cristian, Cris, JP, Cameron и Vincent за их перечитку, продуманные предложения и слова поддержки. Вы - рок-звезды!

Материал подготовлен с ❤️ редакцией Кухни IT.

Олег Ямников

Олег Ямников

Главный кухонный корреспондент.