Как да активирате CORS с HTTPOnly Cookie за защитен токен?

В тази статия виждаме как да активираме CORS (Cross-Origin Resource Sharing) с бисквитка HTTPOnly, за да защитим нашите токени за достъп.

В днешно време бекенд сървърите и предните клиенти се разполагат в различни домейни. Следователно сървърът трябва да активира CORS, за да позволи на клиентите да комуникират със сървъра в браузъри.

Освен това сървърите внедряват удостоверяване без състояние за по-добра скалируемост. Токените се съхраняват и поддържат от страната на клиента, но не и от страната на сървъра като сесията. От съображения за сигурност е по-добре да съхранявате токени в HTTPOnly бисквитки.

Съдържание

Защо са блокирани заявките за Cross-Origin?

Да приемем, че нашето приложение за интерфейс е внедрено на https://app.pctechbg.net.com. Скрипт, зареден в https://app.pctechbg.net.com, може да изисква само ресурси от същия произход.

Всеки път, когато се опитаме да изпратим заявка от кръстосан произход към друг домейн https://api.pctechbg.net.com или друг порт https://app.pctechbg.net.com:3000 или друга схема http://app.pctechbg.net.com, cross-origin заявката ще бъде блокирана от браузъра.

  Как да деинсталирате BetterDiscord на компютър

Но защо същата заявка, блокирана от браузъра, се изпраща от който и да е бекенд сървър с помощта на curl заявка или се изпраща с помощта на инструменти като пощальона без проблем с CORS. Всъщност това е за сигурност, за да защити потребителите от атаки като CSRF (Cross-Site Request Forgery).

Да вземем пример, да предположим, че някой потребител е влязъл в собствения си PayPal акаунт в своя браузър. Ако можем да изпратим заявка от кръстосан произход до paypal.com от скрипт, зареден на друг домейн malicious.com, без грешка/блокиране на CORS, както изпращаме заявка от същия произход.

Нападателите могат лесно да изпратят своята злонамерена страница https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account, като я преобразуват в кратък URL, за да скрият действителния URL. Когато потребителят щракне върху злонамерена връзка, скриптът, зареден в домейна malicious.com, ще изпрати заявка за кръстосан произход до PayPal за прехвърляне на потребителската сума към акаунта на атакуващия PayPal, който ще бъде изпълнен. Всички потребители, които са влезли в акаунта си в PayPal и са кликнали върху тази злонамерена връзка, ще загубят парите си. Всеки може лесно да открадне пари без познания за потребителски акаунт в PayPal.

Поради горепосочената причина, браузърите блокират всички заявки от кръстосан произход.

Какво е CORS (Cross-Origin Resource Sharing)?

CORS е механизъм за защита, базиран на заглавка, използван от сървъра, за да каже на браузъра да изпрати заявка от кръстосан произход от надеждни домейни.
Сървърът е активиран с CORS заглавки, използвани за избягване на заявки от кръстосан произход, блокирани от браузъри.

Как работи CORS?

Тъй като сървърът вече е дефинирал своя доверен домейн в своята CORS конфигурация. Когато изпратим заявка до сървъра, отговорът ще каже на браузъра, че исканият домейн е надежден или не в заглавката му.

Има два вида CORS заявки:

  • Проста молба
  • Предполетна заявка

Проста заявка:

  • Браузърът изпраща заявката до кръстосан домейн с произход (https://app.pctechbg.net.com).
  • Сървърът изпраща обратно съответния отговор с разрешени методи и разрешен произход.
  • След като получи заявката, браузърът ще провери изпратената стойност на заглавката на източника (https://app.pctechbg.net.com) и получената стойност на access-control-allow-origin (https://app.pctechbg.net.com) са еднакви или заместващ знак

. В противен случай ще изведе CORS грешка.

  • Заявка за предварителен полет:
  • В зависимост от параметъра на персонализираната заявка от заявката от кръстосан произход, като методи (PUT, DELETE) или персонализирани заглавки или различен тип съдържание и т.н. Браузърът ще реши да изпрати предварителна заявка OPTIONS, за да провери дали действителната заявка е безопасна за изпращане или не.

След получаване на отговора (код на състоянието: 204, което означава, че няма съдържание), браузърът ще провери за параметрите за разрешаване на контрол на достъпа за действителната заявка. Ако параметрите на заявката са разрешени от сървъра. Изпратената и получена действителна заявка от друг произход

  Как да управлявате най-добрите сайтове и акценти във Firefox

Ако access-control-allow-origin: *, тогава отговорът е разрешен за всички източници. Но не е безопасно, освен ако не ви трябва.

Как да активирам CORS?

За да активирате CORS за всеки домейн, активирайте CORS заглавки, за да разрешите произход, методи, персонализирани заглавки, идентификационни данни и т.н.

  • Браузърът чете CORS хедъра от сървъра и позволява действителни заявки от клиента само след проверка на параметрите на заявката.
  • Access-Control-Allow-Origin: За да посочите точни домейни (https://app.geekflate.com, https://lab.pctechbg.net.com) или заместващ знак
  • Access-Control-Allow-Methods: За разрешаване на HTTP методите (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS), от които се нуждаем само ние.
  • Access-Control-Allow-Headers: За разрешаване само на конкретни заглавки (Authorization, csrf-token)
  • Access-Control-Allow-Credentials: Булева стойност, използвана за разрешаване на кръстосани идентификационни данни (бисквитки, заглавка за оторизация).

Access-Control-Max-Age: Указва на браузъра да кешира отговора преди полет за известно време.

Access-Control-Expose-Headers: Посочете заглавки, които са достъпни от клиентски скрипт.

За активиране на CORS в apache и Nginx уеб сървър, следвайте този урок.

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

app.get('/users', function (req, res, next) {
  res.json({msg: 'user get'})
});

app.post('/users', function (req, res, next) {
    res.json({msg: 'user create'})
});

app.put('/users', function (req, res, next) {
    res.json({msg: 'User update'})
});

app.listen(80, function () {
  console.log('CORS-enabled web server listening on port 80')
})

Активиране на CORS в ExpressJS

Да вземем примерно приложение ExpressJS без CORS:

npm install cors

В горния пример сме активирали потребителска крайна точка на API за методите POST, PUT, GET, но не и метода DELETE.

За лесно активиране на CORS в приложението ExpressJS можете да инсталирате cors

app.use(cors({
    origin: '*'
}));

Access-Control-Allow-Origin

app.use(cors({
    origin: 'https://app.pctechbg.net.com'
}));

Активиране на CORS за всички домейни

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ]
}));

Активиране на CORS за един домейн

Ако искате да разрешите CORS за произход https://app.pctechbg.net.com и https://lab.pctechbg.net.com

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST']
}));

Access-Control-Allow-Methods

За да активирате CORS за всички методи, пропуснете тази опция в модула CORS в ExpressJS. Но за активиране на специфични методи (GET, POST, PUT).

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token']
}));

Access-Control-Allow-Headers

Използва се за разрешаване на заглавки, различни от тези по подразбиране, за изпращане с действителни заявки.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true
}));

Access-Control-Allow-Credentials

Пропуснете това, ако не искате да кажете на браузъра да разрешава идентификационни данни при поискване, дори когато withCredentials е зададено на true.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600 
}));

Access-Control-Max-Age

За да накарате браузъра да кешира информацията за отговор преди полет в кеша за определена секунда. Пропуснете това, ако не искате да кеширате отговора.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    exposedHeaders: ['Content-Range', 'X-Content-Range']
}));

Кешираният отговор преди полет ще бъде достъпен за 10 минути в браузъра.

app.use(cors({
    origin: [
        'https://app.geekflare.com',
        'https://lab.geekflare.com'
    ],
    methods: ['GET', 'PUT', 'POST'],
    allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'],
    credentials: true,
    maxAge: 600,
    exposedHeaders: ['*', 'Authorization', ]
}));

Access-Control-Expose-Headers

  Как да участвате в събитието за Деня на природата „Animal Crossing: New Horizons“.

Ако поставим заместващия знак

в exposedHeaders, няма да изложи заглавката на Authorization. Така че трябва да изложим изрично, както е показано по-долу

Горното ще разкрие всички заглавки и заглавката за оторизация също.

  • Какво е HTTP бисквитка?
  • Бисквитката е малка част от данните, които сървърът ще изпрати на клиентския браузър. При по-късни заявки браузърът ще изпраща всички бисквитки, свързани със същия домейн при всяка заявка.
  • Бисквитката има свой атрибут, който може да бъде дефиниран, за да накара бисквитката да работи по различен начин според нуждите ни.
  • Име Име на бисквитката.
  • стойност: данни на бисквитката, съответстващи на името на бисквитката
  • Домейн: бисквитките ще бъдат изпратени само до дефинирания домейн
  • Път: бисквитките се изпращат само след дефинирания URL префикс път. Да предположим, че сме дефинирали нашия път към бисквитката като path=’admin/’. Бисквитките не се изпращат за URL адреса https://pctechbg.net.com/expire/, но се изпращат с URL префикс https://pctechbg.net.com/admin/
  • Максимална възраст/изтича (число в секунда): Кога трябва да изтече бисквитката. Животът на бисквитката прави бисквитката невалидна след определеното време. [Strict, Lax, None]HTTPOnly(Boolean): Backend сървърът има достъп до тази бисквитка HTTPOnly, но не и до скрипта от страна на клиента, когато е вярно. Сигурен (булев): Бисквитките се изпращат само през SSL/TLS домейн, когато са вярно.същия сайт (низ

): Използва се за активиране/ограничаване на бисквитките, изпратени при заявки между сайтове. За да научите повече подробности относно бисквитките, вижте същия сайт

MDN

. Приема три опции Строг, Лек, Няма. Стойността за защита на бисквитката е зададена на true за конфигурацията на бисквитката sameSite=Няма.

Защо HTTPOnly бисквитка за токени?

Съхраняването на токена за достъп, изпратен от сървъра в хранилище от страна на клиента, като локално хранилище, индексирана DB и бисквитка (HTTPOnly не е зададено на true), е по-уязвимо за XSS атака. Да предположим, че някоя от вашите страници е слаба на XSS атака. Нападателите могат да злоупотребят с потребителски токени, съхранени в браузъра.

HTTPOnly бисквитките се задават/получават само от сървър/бекенд, но не и от страна на клиента.

  • Скриптът от страна на клиента е ограничен за достъп до тази бисквитка само за HTTP. Така че HTTPOnly бисквитките не са уязвими на XSS атаки и са по-сигурни. Тъй като е достъпен само от сървъра.
  • Активирайте бисквитката HTTPOnly в бекенда с активиран CORS
  • Активирането на бисквитка в CORS изисква конфигурацията по-долу в приложението/сървъра.
  • Задайте заглавката на Access-Control-Allow-Credentials на true.

Access-Control-Allow-Origin и Access-Control-Allow-Headers не трябва да са заместващи символи

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

app.use(cors({ 
  origin: [ 
    'https://app.geekflare.com', 
    'https://lab.geekflare.com' 
  ], 
  methods: ['GET', 'PUT', 'POST'], 
  allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], 
  credentials: true, 
  maxAge: 600, 
  exposedHeaders: ['*', 'Authorization' ] 
}));

app.post('/login', function (req, res, next) { 
  res.cookie('access_token', access_token, {
    expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //second min hour days year
    secure: true, // set to true if your using https or samesite is none
    httpOnly: true, // backend only
    sameSite: 'none' // set to none for cross-request
  });

  res.json({ msg: 'Login Successfully', access_token });
});

app.listen(80, function () { 
  console.log('CORS-enabled web server listening on port 80') 
}); 

.

Атрибутът на бисквитката sameSite трябва да бъде None.

За да активирате стойността на sameSite на none, задайте защитената стойност на true: Разрешете бекенда със SSL/TLS сертификат, за да работи в името на домейна.

Нека видим примерен код, който задава токен за достъп в HTTPOnly бисквитка след проверка на идентификационните данни за вход.

Можете да конфигурирате CORS и HTTPOnly бисквитки, като приложите горните четири стъпки във вашия бекенд език и уеб сървър.

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://api.pctechbg.net.com/user', true);
xhr.withCredentials = true;
xhr.send(null);

Можете да следвате този урок за apache и Nginx за активиране на CORS, като следвате горните стъпки.

fetch('http://api.pctechbg.net.com/user', {
  credentials: 'include'
});

с идентификационни данни за заявка Cross-Origin

$.ajax({
   url: 'http://api.pctechbg.net.com/user',
   xhrFields: {
      withCredentials: true
   }
});

Идентификационни данни (бисквитка, оторизация), изпратени по подразбиране със заявка със същия произход. За cross-origin трябва да посочим withCredentials на true.

axios.defaults.withCredentials = true

API на XMLHttpRequest

API за извличане

JQuery AjaxАксиосЗаключение Надявам се, че горната статия ви помага да разберете как работи CORS и да активирате CORS за заявки от кръстосан произход в сървъра. Защо съхраняването на бисквитки в HTTPOnly е сигурно и как с идентификационните данни, използвани в клиентите за кръстосани заявки.