Кроссдоменные запросы разрешить
XMLHttpRequest: кросс-доменные запросы
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/fetch-crossorigin.
Обычно запрос XMLHttpRequest может делать запрос только в рамках текущего сайта. При попытке использовать другой домен/порт/протокол – браузер выдаёт ошибку.
Существует современный стандарт XMLHttpRequest, он ещё в состоянии черновика, но предусматривает кросс-доменные запросы и многое другое.
Большинство возможностей этого стандарта уже поддерживаются всеми браузерами, но увы, не в IE9-.
Впрочем, частично кросс-доменные запросы поддерживаются, начиная с IE8, только вместо XMLHttpRequest нужно использовать объект XDomainRequest.
Кросс-доменные запросы
Разберём кросс-доменные запросы на примере кода:
- Мы создаём XMLHttpRequest и проверяем, поддерживает ли он событие onload . Если нет, то это старый XMLHttpRequest , значит это IE8,9, и используем XDomainRequest .
- Запрос на другой домен отсылается просто указанием соответствующего URL в open . Он обязательно должен быть асинхронным, в остальном – никаких особенностей.
Контроль безопасности
Кросс-доменные запросы проходят специальный контроль безопасности, цель которого – не дать злым хакерам™ завоевать интернет.
Серьёзно. Разработчики стандарта предусмотрели все заслоны, чтобы «злой хакер» не смог, воспользовавшись новым стандартом, сделать что-то принципиально отличное от того, что и так мог раньше и, таким образом, «сломать» какой-нибудь сервер, работающий по-старому стандарту и не ожидающий ничего принципиально нового.
Давайте, на минуточку, вообразим, что появился стандарт, который даёт, без ограничений, возможность делать любой странице HTTP-запросы куда угодно, какие угодно.
Как сможет этим воспользоваться злой хакер?
Он сделает свой сайт, например http://evilhacker.com и заманит туда посетителя (а может посетитель попадёт на «злонамеренную» страницу и по ошибке – не так важно).
Когда посетитель зайдёт на http://evilhacker.com , он автоматически запустит JS-скрипт на странице. Этот скрипт сделает HTTP-запрос на почтовый сервер, к примеру, http://gmail.com . А ведь обычно HTTP-запросы идут с куками посетителя и другими авторизующими заголовками.
Поэтому хакер сможет написать на http://evilhacker.com код, который, сделав GET-запрос на http://gmail.com , получит информацию из почтового ящика посетителя. Проанализирует её, сделает ещё пачку POST-запросов для отправки писем от имени посетителя. Затем настанет очередь онлайн-банка и так далее.
Спецификация CORS налагает специальные ограничения на запросы, которые призваны не допустить подобного апокалипсиса.
Запросы в ней делятся на два вида.
Простыми считаются запросы, если они удовлетворяют следующим двум условиям:
- Простой метод: GET, POST или HEAD
- Простые заголовки – только из списка:
- Accept
- Accept-Language
- Content-Language
- Content-Type со значением application/x-www-form-urlencoded , multipart/form-data или text/plain .
«Непростыми» считаются все остальные, например, запрос с методом PUT или с заголовком Authorization не подходит под ограничения выше.
Принципиальная разница между ними заключается в том, что «простой» запрос можно сформировать и отправить на сервер и без XMLHttpRequest, например при помощи HTML-формы.
То есть, злой хакер на странице http://evilhacker.com и до появления CORS мог отправить произвольный GET-запрос куда угодно. Например, если создать и добавить в документ элемент
Комментарии
- Если вам кажется, что в статье что-то не так — вместо комментария напишите на GitHub.
- Для одной строки кода используйте тег , для нескольких строк кода — тег
, если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
- Главная   /  Уроки   /  Уроки JavaScript   /  Глава 10. AJAX   /  
- XMLHttpRequest и кросс-доменные запросы
XMLHttpRequest и кросс-доменные запросы
Здравствуйте! Обычно запрос XMLHttpRequest может делать запрос только на текущий сайт. При попытке использовать иной домен – браузер выдаёт ошибку. Делается это из соображений безопасности, чтобы не было возможности провести атаку типа XSS взлом.
Существует и современный стандарт XMLHttpRequest, он ещё правда в состоянии черновика, но предусматривает кросс-доменные запросы и многое другое.
Большинство возможностей этого стандарта уже поддерживаются всеми браузерами.
Кросс-доменные запросы
Давайте рассмотрим кросс-доменные запросы на примере кода:
- Мы создаём XMLHttpRequest и проверяем, поддерживает ли он событие onload. Если нет, то это старый XMLHttpRequest, значит это IE8,9, и надо использовать XDomainRequest.
- Запрос на другой домен отсылается просто указанием соответствующего URL в open. Он обязательно должен быть асинхронным, в остальном – никаких особенностей.
Контроль безопасности
Кросс-доменные запросы проходят специальный контроль безопасности, цель которого – не дать взломать сайт.
Давайте, вообразим, что появился стандарт, который даёт, без ограничений, возможность делать любой странице HTTP-запросы куда угодно и какие угодно.
Как сможет этим воспользоваться злой взломщик?
Он сделает свой сайт, например http://evil.com и заманит туда посетителя.
Когда посетитель зайдёт на http://evil.com, он автоматически запустит скрипт на странице. Этот скрипт сделает HTTP-запрос на почтовый сервер, к примеру, http://gmail.com. А ведь обычно HTTP-запросы идут с куками посетителя и другими авторизующими заголовками.
Поэтому хакер сможет написать на http://evil.com код, который, сделав GET-запрос на http://gmail.com, выдаст информацию из почтового ящика посетителя. Проанализировав её, сделаем ещё пачку POST-запросов для отправки писем от имени посетителя. Затем настанет очередь онлайн-банкинга и так далее.
Спецификацией CORS налагаются специальные ограничения на запросы, которые призваны не допустить подобного взлома.
Запросы в ней делятся на 2 вида.
Простыми считаются запросы, если они удовлетворяют следующим двум условиям:
- Простой метод: GET, POST или HEAD
- Простые заголовки – только из списка:
- Accept
- Accept-Language
- Content-Language
- Content-Type со значением application/x-www-form-urlencoded, multipart/form-data или text/plain.
«Непростыми» считаются все остальные, например, запрос с методом PUT или с заголовком Authorization, который не подходит под ограничения выше.
Принципиальная разница между ними заключается в том, что «простой» запрос можно сформировать и отправить на сервер и без XMLHttpRequest, например при помощи HTML-формы.
Что такое CORS
Многие из нас встречались с подобной ошибкой:
Access to XMLHttpRequest at ‘XXXX’ from origin ‘YYYY’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource..
Эта статья рассказывает что означает эта ошибка и как от нее избавиться.
Создадим тестовый сайт на Node.js с открытым API и запустим его по адресу http://127.0.0.1:3000.
Пусть там будет примерно такая функция получения GET запроса:
Пусть там будет простая функция входа в систему, где пользователи вводят общее секретное слово secret и им затем ему устанавливается cookie, идентифицируя их как аутентифицированных:
И пусть у нас будет некое приватное API для каких нибудь личных данных в /private, только для аутентифицированных пользователей.
Запрос нашего API через AJAX из других доменов
И допустим у нас есть какое-нибудь клиентское приложение работающее с нашим API. Но учтем что, наше API находится по адресу http://127.0.0.1:3000/public, а наш клиент размещен на http://127.0.0.1:8000, и на клиенте есть следующий код:
И это не будет работать!
Если мы посмотрим на вкладку network в консоле Хрома при обращение c http://127.0.0.1:8000 к http://127.0.0.1:3000 то там не будет ошибок:
Сам по себе запрос был успешным, но результат оказался не доступен. Описание причины можно найти в консоли JavaScript:
Ага! Нам не хватает заголовка Access-Control-Allow-Origin. Но зачем он нам и для чего он вообще нужен?
Same-Origin Policy
Причиной, по которой мы не получим ответ в JavaScript, является Same-Origin Policy. Эта ограничительная мера была придумана разработчиками браузеров что бы веб-сайт не мог получить ответ на сгенерированный AJAX запрос к другому веб-сайту находящемуся по другому адресу .
Например: если вы заходите на sample.org, вы бы не хотели, чтобы этот веб-сайт отправлял запрос к примеру на ваш банковский веб-сайт и получал баланс вашего счета и транзакции.
Same-Origin Policy предотвращает именно это.
«источник (origin)» в этом случае состоит из
- протокол (например http )
- хост (например example.com )
- порт (например 8000 )
Так что http://sample.org и http://www.sample.org и http://sample.org:3000 – это три разных источника.
Пару слов о CSRF
Обратите внимание, что существует класс атак, называемый подделкой межсайтовых запросов (Cross Site Request Forgery – csrf ), от которых не защищает Same-Origin Policy.
При CSRF-атаке злоумышленник отправляет запрос сторонней странице в фоновом режиме, например, отправляя POST запрос на веб-сайт вашего банка. Если у вас в этот момент есть действительный сеанс с вашим банком, любой веб-сайт может сгенерировать запрос в фоновом режиме, который будет выполнен, если ваш банк не использует контрмеры против CSRF.
Так же обратите внимание, что, несмотря на то, что действует Same-Origin Policy, наш пример запроса с сайта secondparty.com на сайте 127.0.0.1:3000 будет успешно выполнен – мы просто не соможем получить доступ к результатам. Но для CSRF нам не нужен результат …
Например, API, которое позволяет отправлять электронные письма, выполняя POST запрос, отправит электронное письмо, если мы предоставим ему правильные данные. Злоумышленнику не нужно заботится о результате, его забота это отправляемое электронное письмо, которое он получит независимо от возможности видеть ответ от API.
Включение CORS для нашего публичного API
Допустим нам нужно разрешить работу JavaScript на сторонних сайтах (например, 127.0.0.1:8000) что бы получать доступ к нашим ответам API. Для этого нам нужно включить CORS в заголовок ответа от сервера. Это делается на стороне сервера:
Здесь мы устанавливаем заголовку Access-Control-Allow-Origin значение *, что означает: что любому хосту разрешен доступ к этому URL и ответу в браузере:
Непростые запросы и предварительные запросы (preflights)
Предыдущий пример был так называемым простым запросом. Простые запросы – это:
- Запросы: GET,POST
- Тип содержимого следующего:
- text/plain
- application/x-www-form-urlencoded
- multipart/form-data
Допустим теперь 127.0.0.1:8000 немного меняет реализацию, и теперь он обрабатывает запросы в формате JSON:
Но это снова все ломает!
На этот раз консоль показывает другую ошибку:
Любой заголовок, который не разрешен для простых запросов, требует предварительного запроса (preflight request).
Этот механизм позволяет веб-серверам решать, хотят ли они разрешить фактический запрос. Браузер устанавливает заголовки Access-Control-Request-Headers и Access-Control-Request-Method, чтобы сообщить серверу, какой запрос ожидать, и сервер должен ответить соответствующими заголовками.
Но наш сервер еще не отвечает с этими заголовками, поэтому нам нужно добавить их:
Теперь мы снова может получить доступ к ответу.
Credentials и CORS
Теперь давайте предположим, что нам нужно залогинится на 127.0.0.1:3000 что бы получить доступ к /private с конфиденциальной информацией.
При всех наших настройках CORS может ли другой сайт так же получить эту конфиденциальную информацию?
Мы пропустили код реализации входа в на сервер так как он не обязателен для объяснения материала.
Независимо от того, попытаемся ли мы залогинится на 127.0.0.1:3000 или нет, мы увидим «Please login first».
Причина в том, что cookie от 127.0.0.1:3000 не будут отправляться, когда запрос поступает из другого источника. Мы можем попросить браузер отправить файлы cookie клиенту, даже если запрос с других доменов:
Но опять это не будет работать в браузере. И это хорошая новость, на самом деле.
Итак, мы не хотим, чтобы злоумышленник имел доступ к приватным данным, но что, если мы хотим, чтобы 127.0.0.1:8000 имел доступ к /private?
В этом случае нам нужно установить для заголовка Access-Control-Allow-Credentials значение true:
Но это все равно пока еще не сработает. Это опасная практика – разрешать любые аутентифицированные запросы с других источников.
Браузер не позволит нам так легко совершить ошибку.
Если мы хотим разрешить 127.0.0.1:8000 доступ к /private, нам нужно указать точный источник в заголовке:
Теперь http://127.0.0.1:8000 также имеет доступ к приватным данным, в то время как запрос с любого другого сайта будет заблокирован.
Разрешить множественные источники (origin)
Теперь мы разрешили одному источнику делать запросы к другому источнику с данными аутентификации. Но что, если у нас есть несколько других источников?
В этом случае мы, вероятно, хотим использовать белый список:
Опять же: не отправляйте напрямую req.headers.origin в качестве разрешенного заголовка CORS. Это позволит любому веб-сайту получить доступ к приватным данным.
Из этого правила могут быть исключения, но, по крайней мере, дважды подумайте, прежде чем внедрять CORS с учетными данными без белого списка.
Заключение
В этой статье мы рассмотрели Same-Origin Policy и то, как мы можем использовать CORS, чтобы разрешать запросы между источниками, когда это необходимо.
Это требует настройки на стороне сервера и на стороне клиента и в зависимости от запроса вызовет предварительный (preflight) запрос.
При работе с аутентифицированными запросами перекрестного происхождения следует проявлять дополнительную осторожность. Белый список может помочь разрешить нескольким источникам без риска утечки конфиденциальных данных (которые защищены аутентификацией).
Выводы
- Браузер использует Same-origin policy, чтобы не обрабатывать AJAX ответы от веб-сайтов расположенных на адресах отличных от адреса с которого была загружена веб страница.
- Same-origin policy не запрещает генерировать запросы к другим сайтам, но запрещает обрабатывать от них ответ.
- CORS (Cross-Origin Resource Sharing) механизм, который использует дополнительные заголовки HTTP, чтобы дать браузерам указание предоставить веб-приложению, работающему в одном источнике, доступ к ответу на запрос к ресурсам из другого источника.
- CORS вместе с credentials (с данными аутентификации) требует осторожности.
- CORS это браузерная политика. Другие приложения не затрагиваются этим понятием.
XMLHttpRequest: кросс-доменные запросы. Принцип и примеры. JavaSctipt
Обычно запрос XMLHttpRequest может делать запрос только в рамках текущего сайта. При попытке использовать другой домен/порт/протокол — браузер выдает ошибку.
Существует современный стандарт XMLHttpRequest, он еще в состоянии черновика, но предусматривает кросс-доменные запросы и многое другое.
Большинство возможностей этого стандарта уже поддерживаются всеми браузерами, но увы, не в IE9-.
Впрочем, частично кросс-доменные запросы поддерживаются, начиная с IE8, только вместо XMLHttpRequest нужно использовать объект XDomainRequest.
Кросс-доменные запросы
Разберем кросс-доменные запросы на примере кода:
Код: // (1)
var XHR = («onload» in new XMLHttpRequest()) ? XMLHttpRequest : XDomainRequest;
var xhr = new XHR();
// (2) запрос на другой домен 🙂
xhr.open(‘GET’, ‘http://anywhere.com/request’, true);
xhr.onload = function() <
alert( this.responseText );
>
xhr.onerror = function() <
alert( ‘Ошибка ‘ + this.status );
>
Мы создаем XMLHttpRequest и проверяем, поддерживает ли он событие onload. Если нет, то это старый XMLHttpRequest, значит это IE8,9, и используем XDomainRequest.
Запрос на другой домен отсылается просто указанием соответствующего URL в open. Он обязательно должен быть асинхронным, в остальном — никаких особенностей.
Контроль безопасности
Кросс-доменные запросы проходят специальный контроль безопасности, цель которого — не дать злым хакерам завоевать интернет.
Серьезно. Разработчики стандарта предусмотрели все заслоны, чтобы «злой хакер» не смог, воспользовавшись новым стандартом, сделать что-то принципиально отличное от того, что и так мог раньше и, таким образом, «сломать» какой-нибудь сервер, работающий по-старому стандарту и не ожидающий ничего принципиально нового.
Давайте, на минуточку, вообразим, что появился стандарт, который дает, без ограничений, возможность делать любой странице HTTP-запросы куда угодно, какие угодно.
Спецификация CORS налагает специальные ограничения на запросы, которые призваны не допустить подобного апокалипсиса.
Запросы в ней делятся на два вида.
Простыми считаются запросы, если они удовлетворяют следующим двум условиям:
- Простой метод: GET, POST или HEAD
- Простые заголовки — только из списка:
- Accept
- Accept-Language
- Content-Language
- Content-Type со значением application/x-www-form-urlencoded, multipart/form-data или text/plain.
«Непростыми» считаются все остальные, например, запрос с методом PUT или с заголовком Authorization не подходит под ограничения выше.
Принципиальная разница между ними заключается в том, что «простой» запрос можно сформировать и отправить на сервер и без XMLHttpRequest, например при помощи HTML-формы.
Сети в вопросах и ответах. HTTP-протокол. AJAX. JSONP. CORS.COMET.
Протокол прикладного уровня (верхний 7-й уровень модел OSI) предназначенный для передачи произвольных данных при клиент-серверном взаимодействии.
- Стартовая строка — определяет тип сообщения;
2. Заголовки — характеризуют тело сообщения, параметры передачи и прочие сведения; Не обязательная часть.
3. Тело сообщения — непосредственно данные сообщения. Не обязательная часть. Но если есть, обязательно должно отделяться от заголовков пустой строкой.
Какие методы HTTP-протокола вы знаете?
Метод — это указание операции над ресурсом.
- GET — получение данных с ресурса. Не имеет тела, информацию можно передать только через querystring. Кэшируется.
- HEAD — как GET но не возвращает данных. Используют для проверки существования сайта, получения метаданных. Кэшируется.
- POST — отправка данных к ресурсу. Не кэшируется.
- PUT — замещение данных ресурса. Не кэшируется.
- DELETE — удаление данных ресурса. Не кэшируется.
- OPTIONS — предварительный запрос к серверу при кросс-доменном запросе. Не кэшируется (. ).
Что такое AJAX запрос?
AJAX — asynchronous javascript and xml — технология, позволяющая сделать запрос к серверу без перегрузки страницы.
Какой api предоставляют браузеры для создания ajax запроса?
- конструктор XMLHttpRequest
- функция createXHR (IE до версии 7)
Каков базовый синтаксис создания, настройки и отправки AJAX запроса?
Можно ли сделать кроссдоменный AJAX запрос? Объясните почему?
Только при соответствующей подготовке сервера, а по умолчанию нет. Почему нет? Технология AJAX появилась только в 2005 году, до этого времени уже было сделано много сайтов. Если бы браузеры разрешили кроссдоменные запросы, старые сайты, не готовые к тому, что им будут слать PUT и DELETE запросы сторонние ресурсы, могли оказаться уязвимыми.
Что такое image ping?
До появления CORS (а это всего 2014 год) на сторонний домен нельзя было сделать AJAX запрос, даже если и сервер и браузер этого очень хотели. Но просто запрашивать данные со стороннего домена: картинку, скрипт не запрещалось с самого начала веба.
Image ping — старый способ организации однонаправленного кроссдоменного взаимодействия с сервером. Основан на запросе ресурса-картинки и использовался для отслеживания щелчков пользователя на странице или динамического показа рекламы.
Что такое JSONP?
JSON with padding — JSON с набивкой — устаревший но хитрый способ двунаправленного кроссдоменного взаимодействия, основанный на загрузке скрипта с другого домена.
- Сторонний сервер, с удовольствием обработающий ваш запрос.
- Ваша страница, которая хочет туда что-то отправить, получить ответ и обработать его какой-то функцией.
Что надо сделать:
- Страница объявляет и держит наготове функцию-обработчик (processUser)
- Страница формирует запрос вроде
- get_user — какую функцию на сервере дернуть
- user_id — с какими данными ее дернуть
- сallback — в какое слово завернуть ответ
Сервер должен все дернуть как надо и вернуть скрипт с примерно таким содержанием:
Как только скрипт подгрузится, браузер его тут же выполнит — и мы получили данные со стороннего сайта.
Каковы проблемы JSONP?
- Это вне стандартов.
- Это небезопасно.
- Если запрос провалился, то ничего мы никогда не узнаем, не обработаем ошибку правильно, не можем отследить судьбу запроса.
- Мы работаем только с GET — никаких модных REST API.
- И в общем, так делать не надо в 2017 году.
Современный стандарт кроссдоменных запросов?
CORS — Cross-Origin Resource Sharing.
Основная идея: а давайте разрешим кроссбраузерные AJAX запросы. Ой, мы же не можем просто так их разрешить — у нас есть старые сайты, которые не ожидают всяких PUT и DELETE со сторонних ресурсов и могут быть уязвимы.
Тогда давайте сделаем так:
- Те запросы, которые можно было отправить на другой домен и до появления AJAX (а это GET и POST запросы с определенным урезанным набором заголовков) будем просто отправлять (+ добавим в заголовки Origin — домен, который инициирует запрос, чтобы можно было послать его лесом, если он не понравится серверу). Назовем их простыми CORS запросами. В ответ на простой CORS запрос сервер должен отправить ответ с CORS-заголовками. Если ответ не будет содержать нужные заголовки, браузер проигнорирует его.
- А вот те запросы, которые на заре веба было невозможно сделать из браузера (PUT, DELETE и всякое разное сложное, с заголовками интересными), разделим на 2 этапа. На первом спросим сервер, согласен ли он обработать кроссдоменный запрос (через HTTP запрос с методом OPTIONS, который браузер отправит автоматически) и если сервер пришлет положительный ответ с определенными заголовками — разрешим этот запрос отправить.
Что такое COMET?
Методика отправки данных по инициативе сервера, разработанная поверх AJAX.
Подходы к реализации Comet:
- длинные опросы (long polling)
- потоковая передача данных (streaming)
Какие API предоставляет браузер для взаимодействия COMET?
SSE (server-side events) API — события посылаемые сервером — однонаправленное HTTP-подключение к серверу. Поддерживают короткие запросы, длинные запросы, потоковое подключение к серверу.
Web Sockets API — двунаправленное взаимодействие с сервером. Работает по собственному протоколу.