Як працює заголовок Access-Control-Allow-Origin?


1152

Мабуть, я повністю зрозумів її семантику. Я думав про щось подібне:

  1. Клієнтська DOWNLOADS JavaScript код MyCode.js з http://siteA- початку координат .
  2. Заголовок відповіді MyCode.js містить Access-Control-Allow-Origin:,http://siteB що, на мою думку, означало, що MyCode.js може робити посилання на сайт B.
  3. Клієнт запускає деяку функціональність MyCode.js, яка, в свою чергу, робить запити http://siteB, що повинно бути нормальним, незважаючи на те, що запити перехресно походять.

Ну я помиляюся. Це взагалі не працює так. Отже, я прочитав перехресне походження ресурсів і спробував прочитати перехресне походження ресурсів у рекомендації w3c

Одне впевнене - я досі не розумію, як я повинен використовувати цей заголовок.

Я маю повний контроль як над сайтом A, так і над сайтом B. Як я дозволяю коду JavaScript, завантаженому з сайту A, отримувати доступ до ресурсів на сайті B за допомогою цього заголовка?

PS

Я не хочу використовувати JSONP.


3
Я не впевнений, але я вважаю, що встановлення заголовка таким чином дозволяє коду на сайті B отримати http://siteA/MyCode.js.
pimvdb

6
Але як??? Щоб отримати значення заголовка, потрібно спочатку отримати ресурс, але ресурс має перехресне походження, і чи не повинен браузер блокувати запит?
позначити

Те, що ви описали, насправді нагадує іншу практику, Політика безпеки вмісту
Алекс

3
@mark Не потрібно отримувати ресурс, щоб отримати заголовки. Метод HTTP HEADER поверне лише заголовки. У випадку CORS передпольотна перевірка проводиться методом HTTP OPTIONS, який також не повертає тіло. відповідь apsillers описує це добре stackoverflow.com/posts/10636765/reitions .
Матвій

Відповіді:


1444

Access-Control-Allow-Originє заголовком CORS (Cross-Origin Resource Sharing) .

Коли Сайт A намагається отримати вміст із сайту B, сайт B може надіслати Access-Control-Allow-Originзаголовок відповіді, щоб повідомити браузеру, що вміст цієї сторінки доступний до певного джерела. ( Походження - це домен, плюс схема та номер порту .) За замовчуванням сторінки Б не доступні для будь-якого іншого джерела ; за допомогою Access-Control-Allow-Originзаголовка відкриває двері для перехресного доступу через конкретні запити про походження.

Для кожного ресурсу / сторінки, яку Сайт B хоче зробити доступним для сайту A, сайт B повинен обслуговувати його сторінки із заголовком відповіді:

Access-Control-Allow-Origin: http://siteA.com

Сучасні веб-переглядачі не будуть блокувати запити між доменами прямо. Якщо Сайт A вимагає сторінки з сайту B, браузер насправді отримає запитувану сторінку на мережевому рівні та перевірить, чи вказано заголовки відповідей на сайті A як дозволений домен запитувача. Якщо об'єкт B не вказано , що сайт A має право доступу до цієї сторінки, браузер запустить XMLHttpRequest«s errorподія і заперечують дані відповіді на запитувач коду JavaScript.

Непрості запити

Те, що відбувається на рівні мережі, може бути дещо складнішим, ніж пояснено вище. Якщо запит є "непростим" запитом , браузер спочатку надсилає запит OPTIONS, що не містить даних, перед "полетом", щоб переконатися, що сервер прийме запит. Запит не простий, коли: або (або обидва):

  • використання дієслова HTTP, відмінного від GET або POST (наприклад, PUT, DELETE)
  • використання непростих заголовків запитів; Єдиними простими заголовками запитів є:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(Це тільки просто , коли його значення application/x-www-form-urlencoded, multipart/form-dataабо text/plain)

Якщо сервер відповідає на попередній перехід OPTIONS відповідними заголовками відповідей ( Access-Control-Allow-Headersдля непростих заголовків, Access-Control-Allow-Methodsдля непростих дієслів), які відповідають непростим дієсловам та / або непростим заголовкам, тоді браузер надсилає фактичний запит.

Припустимо, що Сайт A хоче надіслати запит PUT для /somePage, з непростим Content-Typeзначенням application/json, браузер спочатку надішле попередній запит:

OPTIONS /somePage HTTP/1.1
Origin: http://siteA.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

Зауважте, що Access-Control-Request-Methodі Access-Control-Request-Headersдодаються браузером автоматично; додавати їх не потрібно. Цей передвибій OPTIONS отримує успішні заголовки відповідей:

Access-Control-Allow-Origin: http://siteA.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type

Під час надсилання фактичного запиту (після попереднього польоту) поведінка ідентична тому, як обробляється простий запит. Іншими словами, непростий запит, попередній політ якого пройшов успішно, трактується так само, як і простий запит (тобто сервер повинен все-таки відправити Access-Control-Allow-Originще раз для фактичної відповіді).

Браузери надсилають фактичний запит:

PUT /somePage HTTP/1.1
Origin: http://siteA.com
Content-Type: application/json

{ "myRequestContent": "JSON is so great" }

І сервер надсилає назад Access-Control-Allow-Origin, як і для простого запиту:

Access-Control-Allow-Origin: http://siteA.com

Див. Поняття XMLHttpRequest над CORS для отримання додаткової інформації про непрості запити.


4
Але MyCode.js не може досягти сайту B в першу чергу! Як цей заголовок прибуде до клієнта? BTW, кудо для легкого планера життя в аватарі.
відмітка

8
Я відредагував із уточненням: браузер насправді виконує вибір мережі на сайті B для перевірки Access-Control-Allow-Originзаголовка, але він може не надати відповідь на JS-код на сайті A, якщо заголовок не дозволяє сайту A мати його. (PS Спасибі :))
апспіллери

2
Дійсно, я не бачу жодної записи про завантаження у Fiddler, якщо тільки не буде схвалено запит перехресного походження. Цікаво ...
відмітити

23
@ Jwan622 Основоположне " чому? " Таке питання, ймовірно, поза сферою для цієї конкретної відповіді, що стосується лише правил та механіки. В принципі, браузер дозволяє вам , людина сидить за комп'ютером, див будь-який ресурс з будь-якого походження. Він забороняє сценарії (які може бути написаний будь-ким) від читання ресурсів із джерел, що відрізняються від походження сторінки, на якій працює сценарій. Деякі пов'язані з цим питання - програмісти.stackexchange.com/q/216605 та яка модель загрози для тієї ж політики походження?
апспіллери

3
У разі використання автентифікації Access-Control-Allow-Originне приймає *в деяких браузерах (FF та Chrome AFAIK). Тому в цьому випадку вам потрібно вказати значення із Originзаголовка. Сподіваюсь, що це комусь допоможе.
Zsolti

123

Cross-Origin Resource Sharing - CORS(запит AKA Cross-Domain AJAX) - це проблема, з якою можуть зіткнутися більшість веб-розробників. Відповідно до політики Same-Origin-поля, браузери обмежують JavaScript клієнта в пісочниці безпеки, зазвичай JS не може безпосередньо спілкуватися з віддаленим сервером з іншого домену. У минулому розробники створювали багато складних способів досягнення запиту на ресурси між доменними, найчастіше використовуючи такі способи:

  1. Використовуйте Flash / Silverlight або сторону сервера як "проксі" для спілкування з дистанційним.
  2. JSON З накладкою ( JSONP ).
  3. Вставляє віддалений сервер у iframe та спілкується через фрагмент або window.name, зверніться сюди .

У цих хитрих способів виникають більш-менш деякі проблеми, наприклад, JSONP може спричинити загрозу в безпеці, якщо розробники просто "зрівняють" це, і №3 вище, хоча це працює, обидва домени повинні будувати між собою суворий контракт, він не є гнучким і не елегантним ІМХО :)

Компанія W3C представила перехресне походження ресурсів (CORS) як стандартне рішення, щоб забезпечити безпечний, гнучкий та рекомендований стандартний спосіб вирішення цієї проблеми.

Механізм

З високого рівня ми можемо просто вважати, що CORS - це договір між клієнтським дзвінком AJAX з домену A та сторінкою, розміщеною на домені B, типовим запитом / відповіддю Cross-Origin буде:

Заголовки запитів DomainA AJAX

Host DomainB.com
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language en-us;
Accept-Encoding gzip, deflate
Keep-Alive 115
Origin http://DomainA.com 

Заголовки відповідей DomainB

Cache-Control private
Content-Type application/json; charset=utf-8
Access-Control-Allow-Origin DomainA.com
Content-Length 87
Proxy-Connection Keep-Alive
Connection Keep-Alive

Сині частини, які я зазначив вище, були фактами ядра, "Заголовок запиту" Походження "вказує, звідки походить запит перехресного походження або запит перед польотом", заголовок відповіді "Доступ-контроль-дозволити-походження" вказує, що ця сторінка дозволяє віддалений запит від DomainA (якщо значення * вказати, дозволяє віддалені запити з будь-якого домену).

Як я вже згадував вище, W3 рекомендував браузеру реалізувати " запит перед польотом" перед тим, як подати насправді крос-похідний HTTP-запит, у двох словах це OPTIONSзапит HTTP :

OPTIONS DomainB.com/foo.aspx HTTP/1.1

Якщо foo.aspx підтримує дієслово OPTIONS HTTP, воно може повернути відповідь, як показано нижче:

HTTP/1.1 200 OK
Date: Wed, 01 Mar 2011 15:38:19 GMT
Access-Control-Allow-Origin: http://DomainA.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Max-Age: 1728000
Connection: Keep-Alive
Content-Type: application/json

Тільки якщо відповідь містить "Access-Control-Allow-Origin" І її значення "*" або містить домен, який подав запит CORS, виконавши цей обов'язковий стан, браузер надішле фактичний крос-доменний запит і кешуватиме результат у " Кеш-попередній політ-результат ".

Три роки тому я блогував про CORS: HTTP-запит AJAX Cross-Origin


Ця відповідь дала мені зрозуміти, чому я раптом отримував проблему, не використовуючи цей заголовок для POST та GET запитів. Я випадково відкрив файл index.html безпосередньо з диска, тому URL-адресу клієнта, що отримував доступ на node.js, вважався крос-доменом, тоді як він просто працював на localhost. Доступ через URL-адресу (як зазвичай це робиться) "вирішив" мою проблему ...
LuqJensen

Чи міг би домен у зовнішній мережі спілкуватися з доменом у внутрішній мережі?
Si8

68

Питання є занадто старим, щоб відповісти, але я публікую це для подальшого посилання на це питання.

Відповідно до цієї статті Мережі розробників Mozilla,

Ресурс робить запит HTTP-походження, коли він запитує ресурс з іншого домену або порту, ніж той, який обслуговує перший ресурс.

введіть тут опис зображення

HTML сторінка обслуговується http://domain-a.comробить <img>запит SRC для http://domain-b.com/image.jpg.
Сьогодні багато сторінок в Інтернеті завантажують такі ресурси, як таблиці стилів CSS , зображення та сценарії з окремих доменів (таким чином, це повинно бути круто).

Політика з однаковим походженням

З міркувань безпеки веб-переглядачі обмежують запити HTTP між походженнями, ініційовані зсередини сценаріїв .
Наприклад, XMLHttpRequestта Fetchдотримуйтесь політику того самого походження .
Отже, веб-додаток, що використовує XMLHttpRequestабо Fetchможе робити запити HTTP лише до власного домену .

Поширений розподіл ресурсів (CORS)

Щоб покращити веб-програми, розробники попросили постачальників браузерів дозволити міждоменні запити.

Механізм крос-походження ресурсів (CORS) надає веб-серверам керування міждоменним доступом , які забезпечують безпечну передачу даних між доменними.
Сучасні браузери використовують CORS у контейнері API - наприклад, XMLHttpRequestабо Fetch- для зменшення ризиків перехресних запитів HTTP.

Як працює CORS ( Access-Control-Allow-Originзаголовок)

Вікіпедія :

Стандарт CORS описує нові заголовки HTTP, які надають браузерам та серверам спосіб запитувати віддалені URL лише тоді, коли вони мають дозвіл.

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

Приклад

  1. Браузер надсилає OPTIONSзапит із Origin HTTPзаголовком.

    Значення цього заголовка - домен, який обслуговував батьківську сторінку. Коли сторінка із http://www.example.comспроб отримати доступ до даних користувача у service.example.comтакому заголовку запиту буде надіслана service.example.com:

    Походження: http://www.example.com

  2. Сервер service.example.comможе відповісти:

    • Access-Control-Allow-Origin(ACAO) заголовка в своїй відповіді вказує на походження яких сайти дозволені.
      Наприклад:

      Access-Control-Allow-Origin: http://www.example.com

    • Сторінка помилок, якщо сервер не дозволяє запит перехресного походження

    • Access-Control-Allow-Origin(ACAO) заголовок з груповим символом , що дозволяє все домени:

      Access-Control-Allow-Origin: *


1
Як встановити жодного, не дозволяється тузити щось на зразокAccess-Control-Allow-Origin:null
Субін Чаліл,

2
Коли я не хочу дозволити нікому отримати доступ до моїх ресурсів через CORS, яке значення я повинен встановити Access-Control-Allow-Origin? Я маю на увазі запереченняAccess-Control-Allow-Origin: *
Субін Чаліл

4
Просто не встановлюйте нічого для цього
Pmpr

24

Щоразу, коли я починаю думати про CORS, моя інтуїція щодо того, на якому веб-сайті розміщуються заголовки, неправильна, як ви описали у своєму запитанні. Для мене це допомагає продумати мету тієї ж політики походження.

Метою тієї ж політики щодо походження є захист від зловмисного JavaScript на сайтіAAA, що має доступ до приватної інформації, яку ви вибрали для обміну лише з siteB.com. Без тієї ж політики походження, JavaScript, написаний авторами siteA.com, може змусити ваш браузер робити запити на siteB.com, використовуючи ваші файли cookie для аутентифікації для siteB.com. Таким чином, siteA.com може викрасти секретну інформацію, якою ви поділитесь з siteB.com.

Іноді вам потрібно працювати з крос-доменом, звідки надходить CORS. CORS розслаблює ту саму політику походження для domainB.com, використовуючи Access-Control-Allow-Originзаголовок, щоб перелічити інші домени (domainA.com), яким довіряється запуск JavaScript, який може взаємодіяти з domainA. ком.

Щоб зрозуміти, який домен повинен обслуговувати заголовки CORS, врахуйте це. Ви відвідуєте зловмисний веб-сайт.com, який містить деякий JavaScript, який намагається зробити запит між доменами на mybank.com. Потрібно вирішувати, чи встановлює заголовки CORS, які розслаблюють ту саму політику походження, що дозволяє JavaScript з сайту malicious.com взаємодіяти з нею. Якщо malicous.com може встановити власні заголовки CORS, що дозволяють власному JavaScript отримати доступ до mybank.com, це повністю скасує ту саму політику походження.

Я думаю, що причина моєї поганої інтуїції - це точка зору, яку я маю при розробці сайту. Це мій сайт із усім моїм JavaScript, тому він не робить нічого шкідливого, і мені належить визначати, з якими іншими сайтами мій JavaScript може взаємодіяти. Насправді я повинен думати, які інші сайти JavaScript намагаються взаємодіяти з моїм сайтом, і чи варто використовувати CORS, щоб дозволити їх?


1
З огляду на параграф 2, чи є у вас сайтAA, siteB назад у пункті 3? Я міг би бути непорозумінням, але, як видається, попередній абзац передбачає його сайтA, на якому працює спільний JS?
cellepo

11

1. Клієнт завантажує javascript-код MyCode.js з http: // siteA - джерело.

Код, який виконує завантаження - ваш html-скрипт тегу або xhr з javascript або будь-якого іншого - походить з, скажімо, http: // siteZ . І коли браузер запитує MyCode.js, він надсилає заголовок Origin: "Origin: http: // siteZ ", тому що він може бачити, що ви звертаєтесь до siteA і siteZ! = SiteA. (Ви не можете зупинити це чи перешкоджати цьому.)

2. Заголовок відповіді MyCode.js містить Access-Control-Allow-Origin: http: // siteB , що, на мою думку, означало, що MyCode.js має право робити посилання на сайт B.

ні. Це означає, що лише сайтB може робити цей запит. Тож ваш запит на MyCode.js від siteZ натомість отримує помилку, а браузер зазвичай нічого не дає. Але якщо ви змусите ваш сервер повернути ACAO: siteZ замість цього, ви отримаєте MyCode.js. Або якщо він надсилає "*", це спрацює, це все дозволить. Або якщо сервер завжди надсилає рядок із заголовка Origin: ... але ... для безпеки, якщо ви боїтесь хакерів , ваш сервер повинен дозволяти джерела лише в шорт-лісті, яким дозволено робити ці запити.

Потім MyCode.js надходить із сайтуA. Коли він надсилає запити до siteB, всі вони мають перехресне походження, браузер надсилає Origin: siteA, а siteB повинен взяти сайтA, визнати його в короткому списку дозволених запитувачів і повернути назад ACAO: siteA. Тільки тоді браузер дозволить вашому сценарію отримати результат цих запитів.


11

Використовуючи React та Axios , приєднайте проксі-посилання до URL-адреси та додайте заголовок, як показано нижче

https://cors-anywhere.herokuapp.com/ + Your API URL

Просто додавши проксі-посилання буде спрацьовувати, але воно також може повторно помилитися без доступу. Тому краще додати заголовок, як показано нижче.

axios.get(`https://cors-anywhere.herokuapp.com/[YOUR_API_URL]`,{headers: {'Access-Control-Allow-Origin': '*'}})
      .then(response => console.log(response:data);
  }

18
Будь ласка, не робіть цього. Використання проксі-посилання - це як передача файлів cookie користувача середньому чоловікові. Має бути незаконним IMHO
anthonymonori

це було корисно для мене! За винятком випадків, ніж використовувати * (який має проблеми із безпекою), я обмежив Контроль доступу точною адресою, яку я використовую, щоб дізнатися з ... у моєму випадку ' reqres.in/api/register '
C-Note187

9

Якщо ви хочете просто протестувати міждоменну програму, в якій браузер блокує ваш запит, ви можете просто відкрити веб-переглядач у небезпечному режимі і протестувати свою програму, не змінюючи код і не роблячи свій код небезпечним. З ОС MAC ви можете це зробити з термінальної лінії:

open -a Google\ Chrome --args --disable-web-security --user-data-dir

9

Якщо ви використовуєте PHP, спробуйте додати наступний код на початку файлу php:

Якщо ви використовуєте localhost, спробуйте це:

header("Access-Control-Allow-Origin: *");

Якщо ви використовуєте зовнішні домени, такі як сервер, спробуйте це:

header("Access-Control-Allow-Origin: http://www.website.com");

7

Я працюю з Express 4 і вузлом 7.4 і кутовим, у мене була така ж проблема, що я допомагаю в цьому:
а) сторона сервера: у файлі app.js я даю заголовки всім відповідям, як:

app.use(function(req, res, next) {  
      res.header('Access-Control-Allow-Origin', req.headers.origin);
      res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
      next();
 });  

це повинно бути перед усім маршрутизатором .
Я бачив багато доданих цих заголовків:

res.header("Access-Control-Allow-Headers","*");
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');

але мені цього не потрібно,
б) клієнтська сторона: у відправці ajax вам потрібно додати: "withCredentials: true", наприклад:

$http({
     method: 'POST',
     url: 'url, 
     withCredentials: true,
     data : {}
   }).then(function(response){
        // code  
   }, function (response) {
         // code 
   });

Щасти.


res.header('Access-Control-Allow-Origin', req.headers.origin);те саме, щоres.header('Access-Control-Allow-Origin', '*');
Аельфін

4

Для спільного походження, встановіть заголовок: 'Access-Control-Allow-Origin':'*';

Php: header('Access-Control-Allow-Origin':'*');

Вузол: app.use('Access-Control-Allow-Origin':'*');

Це дозволить ділитися вмістом для різних доменів.


4

У Python я користуюся Flask-CORSбібліотекою з великим успіхом. Це робить справу з CORS супер легко і безболісно. Я додав код з документації бібліотеки нижче.

Встановлення:

$ pip install -U flask-cors

Простий приклад, який дозволяє CORS для всіх доменів на всіх маршрутах:

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"

Більш конкретні приклади дивіться в документації. Я використав простий приклад, описаний вище, щоб обійти проблему CORS в іонному додатку, який я будую, який повинен мати доступ до окремого сервера колби.


4

З мого власного досвіду важко знайти просте пояснення, чому CORS навіть викликає занепокоєння.

Як тільки ви зрозумієте, чому саме там, заголовки та обговорення стають набагато зрозумілішими. Я дам йому зняти в декількох рядках.


Вся справа в печиві. Файли cookie зберігаються на клієнті за їх доменом.

Приклад історії: На вашому комп’ютері є файл cookie для yourbank.com. Можливо, ваша сесія там.

Ключовий момент: Коли клієнт робить запит на сервер, він надсилає файли cookie, що зберігаються під доменом, на якому працює клієнт.

Ви ввійшли у свій веб-переглядач yourbank.com. Ви просите переглянути всі ваші облікові записи. yourbank.comотримує купу файлів cookie та повертає свою відповідь (ваші рахунки).

Якщо інший клієнт подає запит на перехресне походження на сервер, ці файли cookie надсилаються разом, як і раніше. Рух рох.

Ви переглядаєте malicious.com. Зловмисники подають купу запитів у різні банки, в тому числі yourbank.com.

Оскільки файли cookie перевірені, як очікувалося, сервер дозволить відповісти.

Ці файли cookie збираються та надсилаються разом - і тепер malicious.comє відповідь від yourbank.

Yikes.


Отже, зараз стає очевидним кілька питань та відповідей:

  • "Чому б ми просто не заблокували браузер цього робити?" Так. CORS.
  • "Як нам це обійти?" Попросіть сервер повідомити запит, що CORS в порядку.

3

Просто вставте наступний код у файл web.config.

Помітивши це, ви повинні вставити наступний код під <system.webServer>тег

    <httpProtocol>  
    <customHeaders>  
     <add name="Access-Control-Allow-Origin" value="*" />  
     <add name="Access-Control-Allow-Headers" value="Content-Type" />  
     <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />  
    </customHeaders>  
  </httpProtocol>  

0

Заголовок відповіді Access-Control-Allow-Origin вказує, чи можна відповідати спільним запитом коду із заданим початком.

Header type Response       header
Forbidden header name      no

Відповідь, яка повідомляє веб-переглядачу дозволити коду будь-якого походження отримати доступ до ресурсу, буде містити наступне:

Access-Control-Allow-Origin: *

Для отримання додаткової інформації відвідайте тут ....


0

Nginx і Appache

На додаток до відповіді apsillers, я хотів би додати графік wiki, який показує, коли запит простий чи ні (а OPTIONS запит перед польотом надсилається чи ні)

Введіть тут опис зображення

Для простого запиту (наприклад, гарячих посилань ) вам не потрібно змінювати конфігураційні файли вашого сервера, але ви можете додавати заголовки в додаток (розміщений на сервері, наприклад, в php), як Мелвін Герреро згадує у своїй відповіді - але пам’ятайте : якщо ви додасте повне заголовки cors на вашому сервері (конфігурації), і в той же час ви дозволяєте простим козам при застосуванні (наприклад, php), це взагалі не буде працювати.

А ось конфігурації для двох популярних серверів

  • увімкніть CORS на Nginx ( nginx.confфайл)

  • увімкніть CORS на Appache ( .htaccessфайл)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.