Підробка міжсайтових запитів (CSRF)

Spring передбачає комплексні засоби підтримки для захисту від підробки міжсайтових запитів (CSRF). У наступних розділах ми розглянемо:

  • Що таке CSRF-атака?

  • Захист від CSRF-атак

  • Рекомендації з приводу CSRF

Що таке CSRF-атака?

Найкраще можна зрозуміти, що таке CSRF-атака, розглянувши конкретний приклад.

Припустимо, що на сайті твого банку є форма, що дозволяє переказати гроші від поточного користувача, який увійшов до системи, на інший банківський рахунок. Наприклад, форма переведення може виглядати так:

Форма переведення

<form method="post"
      action="/transfer">
<input type="text"
       name="amount"/>
<input type="text"
       name="routingNumber"/>
<input type="text"
       name="account"/>
<input type="submit"
       value="Transfer"/>
</form>

Відповідний HTTP-запит може виглядати так:

Передача HTTP-запиту

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account=9876

Тепер уяви, що ти авторизуєшся на сайті свого банку, а потім, не виходячи із системи, відвідуєш сайт зловмисників. Вебсайт зловмисників містить HTML-сторінку з наступною формою:

Форма переведення зловмисників

<form method="post"
      action="https://bank.example.com/transfer">
<input type="hidden"
       name="amount"
       value="100.00"/>
<input type="hidden"
       name="routingNumber"
       value="evilsRoutingNumber"/>
<input type="hidden"
       name="account"
       value="evilsAccountNumber"/>
<input type="submit"
       value="Win Money!"/>
</form>

Тобі подобається вигравати гроші, тому ти натискаєш на кнопку відправки. При цьому ти ненавмисно передаєш 100 доларів зловмиснику. Це відбувається тому, що хоча сайт зловмисників і не бачить твої файли cookie, файли cookie, пов'язані з твоїм банком, все одно відправляються разом із запитом.

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

Захист від CSRF-атак

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

Spring передбачає два механізми захисту від CSRF-атак:

  • Шаблон токена синхронізатора

  • Встановлення атрибуту SameSite у файлі cookie в рамках сесії

Обидва варіанти захисту вимагають, щоб безпечні методи були ідемпотентними

Безпечні методи повинні бути ідемпотентними

Для того, щоб спрацював будь-який із варіантів захисту від CSRF, додаток повинен переконатися, що "безпечні" HTTP-методи є ідемпотентними. Це означає, що запити з HTTP-методом GET, HEAD, OPTIONS та TRACE не повинні змінювати стан програми.

Шаблон токена синхронізатора

Переважним і найбільш повним способом захисту від CSRF-атак є використання шаблона синхронізатора токенів. Це рішення полягає в тому, що кожен HTTP-запит буде вимагати, щоб на додаток до нашого сеансового файлу cookie в HTTP-запиті було присутнє безпечне випадково згенероване значення, зване CSRF-токеном.

Після відправки HTTP-запиту, сервер повинен здійснити пошук передбачуваного CSRF-токену та порівняти його з фактичним CSRF-токеном у HTTP-запиті. Якщо значення не збігатимуться, HTTP-запит повинен бути відхилений.

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

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

.

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

Давай подивимося, як зміниться наш приклад при використанні шаблону токена синхронізатора. Припустимо, що фактичний CSRF-токен повинен знаходитись у HTTP-параметрі з ім'ям _csrf. Форма перекладу в нашому додатку буде виглядати так:

Форма токена синхронізатора

<form method="post"
      action="/transfer">
<input type="hidden"
       name="_csrf"
       value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
<input type="text"
       name="amount"/>
<input type="text"
       name="routingNumber"/>
<input type="hidden"
       name="account"/>
<input type="submit"
       value="Transfer"/>
</form>

Тепер форма містить приховані вхідні дані зі значенням CSRF-токена. Зовнішні сайти не зможуть прочитати CSRF-токен, оскільки та ж політика з приводу джерел гарантує, що сайт зловмисників не зможе прочитати відповідь.

Відповідний HTTP-запит на переказ грошей виглядатиме таким чином:

Запит токена синхронізатора

POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account= 9876&_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

Ти можеш помітити, що HTTP-запит тепер містить параметр _csrf із безпечним випадковим значенням. Сайт зловмисників не зможе передати правильне значення для параметра _csrf (яке має бути явно передано на сайті зловмисників), а переклад завершиться помилкою, коли сервер порівняє фактичний CSRF-токен з очікуваним CSRF-токеном.

Атрибут SameSite

Спосіб захисту від CSRF-атак, що знаходиться в стадії становлення, полягає у зазначенні атрибуту SameSite для файлів cookie. Сервер може вказати атрибут SameSite при налаштуванні cookie, щоб позначити, що cookie не слід надсилати при надходженні із зовнішніх сайтів.

Spring Security не керує безпосередньо створенням cookie для сесії, тому він не передбачає підтримки атрибуту SameSite. Spring Session передбачає підтримку атрибуту SameSite у додатках на основі сервлетів. CookieWebSessionIdResolver у Spring Framework забезпечує підтримку атрибуту SameSite у додатках на базі WebFlux.

Приклад HTTP-заголовка відповіді з атрибутом SameSite може виглядати наступним чином:

HTTP-відповідь з атрибутом SameSite
Set-Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly; SameSite=Lax

Допустимими значеннями для атрибуту SameSite є:

  • Strict — при вказанні цього параметра будь-який запит, що надходить з того ж сайту, включатиме cookie. В іншому випадку cookie не буде включено до HTTP-запиту.

  • Lax — якщо зазначені cookies будуть відправлені при надходженні з того ж сайту або якщо запит надходить з навігації на верхньому рівні, а метод є ідемпотентним. В іншому випадку файл cookie не буде доданий до HTTP-запиту.

Давай подивимося, як наш приклад можна захистити за допомогою атрибуту SameSite. Банківська програма може захиститися від CSRF-атак, вказавши атрибут SameSite у файлі cookie для сесії.

При установці атрибуту SameSite для нашого сесійного файлу cookie браузер буде продовжувати надсилати файл cookie JSESSIONID із запитами, що надходять із банківського сайту. Однак браузер більше не надсилатиме файл cookie JSESSIONID із запитом на передачу даних, що надходить із вебсайту зловмисників. Оскільки сесія більше не значиться у запиті на переклад, що надходить із сайту зловмисників, програма буде захищена від CSRF-атаки.

Існує кілька важливих нюансів, про які слід знати при використанні атрибуту SameSite для захисту від CSRF-атак.

Встановлення атрибуту SameSite на значення Strict забезпечує більш надійний захист, але може заплутати користувачів. Розглянемо користувача, який заходить на сайт соціальної мережі, розташований за адресою social.example.com. Користувач отримує електронний лист на адресу email.example.org, у якому міститься посилання на сайт соціальної мережі. Якщо користувач натискає посилання, він цілком обґрунтовано очікує, що буде автентифікований на сайті соціальної мережі. Однак, якщо атрибут SameSite має значення Strict, файл cookie не буде надісланий, а користувач не буде автентифікований.

Ми могли б покращити захист та зручність використання механізму захисту від CSRF-атак з використанням SameSite, реалізувавши gh-7537.

Ще один очевидний нюанс: щоб атрибут SameSite захищав користувачів, браузер повинен передбачати підтримку атрибуту SameSite. Більшість сучасних браузерів підтримують атрибут SameSite. Однак старими браузерами, які все ще перебувають у побуті, така підтримка може бути не передбачена.

З цієї причини зазвичай рекомендується використовувати атрибут SameSite як глибокоешелонований захист, а не як єдиний механізм захисту від CSRF-атак.

Випадки використання захисту від CSRF

Коли слід використовувати захист від CSRF? Ми рекомендуємо використовувати захист CSRF для будь-якого запиту, який можна обробити браузером, використовуваним звичайними користувачами. Якщо ти створюєш сервіс, який будуть використовувати виключно небраузерні клієнти, то, швидше за все, тобі доведеться відключити захист від CSRF.

Захист від CSRF та формат JSON

Часте питання: "Чи потрібно мені захищати JSON-запити, які виконує JavaScript?". Коротка відповідь: "Залежить від ситуації". Однак слід пильнувати, оскільки існують CSRF-експлойти, які можуть вплинути на JSON-запити. Наприклад, зловмисник може здійснити CSRF-атаку за допомогою JSON, використовуючи наступну форму:

CSRF-атака за допомогою JSON-форми

<form action="https://bank.example.com/transfer" method="post" enctype="text/plain">
	<input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
	<input type="submit"
           value="Win Money!"/>
</form>

В результаті буде отримана наступна структура JSON

CSRF-атака за допомогою JSON-запиту

{ "amount": 100,
"routingNumber": "evilsRoutingNumber",
"account": "evilsAccountNumber",
"ignore_me": "=test"
}

Якби програма не валідувала Content-Type, то вона була б схильна до цього експлойту. Залежно від конфігурації, програма Spring MVC, яка перевіряє Content-Type, все ще може бути піддана дії експлойта шляхом оновлення суфікса URL-адреси таким чином, щоб він закінчувався на .json, як показано нижче:

CSRF-атака за допомогою JSON-форми в Spring MVC

<form action="https://bank.example.com/transfer.json" method="post" enctype="text/plain">
	<input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
	<input type="submit"
           value="Win Money!"/>
</form>

CSRF та програми для браузерів, які не зберігають стан

Що робити, якщо моя програма не зберігає стан? Цей факт не говорить про те, що ти добре захищений. Фактично, якщо користувачу не потрібно виконувати будь-які дії у веббраузері для даного запиту, він, швидше за все, все одно буде вразливий для CSRF-атак.

Наприклад, розглянемо програму, яка використовує для аутентифікації замість JSESSIONID кастомний cookie, що містить весь стан усередині нього. При здійсненні CSRF-атаки кастомний файл cookie буде відправлений разом із запитом тим же чином, що файл cookie JSESSIONID з нашого попереднього прикладу. Ця програма буде вразлива для CSRF-атак.

Програми, що використовують базову автентифікацію, також вразливі до CSRF-атак. Додаток вразливий, оскільки браузер автоматично включатиме ім'я користувача та пароль у будь-які запити тим же способом, яким у нашому попередньому прикладі був відправлений файл cookie JSESSIONID. захисту від CSRF-атак необхідно враховувати кілька особливих нюансів.

Вхід до системи

Для захисту від підробки запитів на вхід в систему HTTP-запит на вхід повинен бути захищений від CSRF-атак. Захист від підробки запитів на вхід до системи необхідний для того, щоб зловмисник не міг зчитати конфіденційну інформацію жертви. Атака здійснюється таким чином:

  • Користувач-зловмисник виконує вхід до системи за допомогою CSRF, використовуючи облікові дані користувача-зловмисника. Після цього жертва аутентифікується як користувач-зловмисник.

  • Користувач-зловмисник обманом змушує жертву відвідати зламаний сайт та ввести конфіденційну інформацію.

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

Можливе ускладнення у забезпеченні захисту HTTP-запитів на вхід до системи від CSRF-атак полягає в тому, що користувач може зіткнутися з часом очікування сесії, внаслідок чого запит буде відхилено. Час очікування сесії викликає подив у користувачів, які не очікували, що для входу в систему їм знадобиться сесія.

Вихід із системи

Для захисту від підробки запитів на вихід із системи, HTTP-запит на вихід із системи повинен бути захищений від CSRF-атак. Захист від підробки запитів на вихід із системи необхідний для того, щоб зловмисник не міг зчитати конфіденційну інформацію жертви. Більш детальну інформацію про атаку можна знайти в цьому блозі.

Можливе ускладнення у забезпеченні захисту HTTP-запитів на вихід із системи від CSRF-атак полягає в тому, що користувач може зіткнутися з часом очікування сесії, внаслідок чого запит буде відхилено. Час очікування сесії викликає подив у користувачів, які не очікували, що для виходу з системи їм знадобиться сесія. Це означає, що щойно сесія закінчиться, сервер не знайде очікуваний CSRF-токен і відхиляє HTTP-запит. Існує кілька варіантів вирішення проблеми з часом очікування, кожен з яких має свої недоліки. Потім форма оновлюється за допомогою CSRF-токену та відправляється.

  • Іншим варіантом є використання JavaScript, який повідомляє користувачеві, що термін дії його сесії закінчується. Користувач може натиснути кнопку, щоб продовжити та оновити сесію.

  • Нарешті, очікуваний CSRF-токен може зберігатися у файлі cookie. Це дозволяє очікуваному CSRF-токену пережити сесію.

    Хтось може запитати, чому очікуваний CSRF-токен не зберігається в cookie за замовчуванням. Це пов'язано з відомими експлойтами, де заголовки (наприклад, для зазначення cookies) можуть встановлюватися іншим доменом. З цієї ж причини Ruby on Rails не пропускає перевірки CSRF за наявності заголовка X-Requested-With. Іншим недоліком є те, що, видаливши стан (тобто час очікування), ти втрачаєш можливість примусово оголосити недійсним токен, якщо він був скомпрометований.

Багатокомпонентність (завантаження файлів)

Захист багатокомпонентних запитів (завантаження файлів) від CSRF-атак призводить до проблеми курки та яйця. Для запобігання CSRF-атаки необхідно прочитати тіло HTTP-запиту, щоб отримати фактичний CSRF-токен. Однак, читання тіла означає, що файл буде завантажений, а це в свою чергу означає, що зовнішній сайт зможе завантажити будь-який файл. Існує два варіанти використання захисту від CSRF за допомогою multipart/form-data. Кожен варіант має свої компроміси.

  • Розміщення CSRF-токена в тілі

  • Розміщення CSRF-токена в URL-адресі

Перш ніж інтегрувати CSRF-захист Spring Security із завантаженням багатокомпонентних файлів, спочатку переконайся, що зможеш завантажувати файли без CSRF-захисту.

Розміщення CSRF-токена в тілі

Перший варіант — додати фактичний CSRF-токен у тіло запиту. Якщо помістити CSRF-токен у тіло, тіло буде прочитано до того, як буде виконано авторизацію. Це означає, що будь-хто зможе розміщувати тимчасові файли на твоєму сервері. Однак, лише авторизовані користувачі зможуть відправити файл, який буде оброблений твоєю програмою. В цілому, це рекомендований підхід, оскільки завантаження тимчасового файлу має незначно впливати на більшість серверів. Альтернативою може бути додавання очікуваного CSRF-токену як параметр запиту до атрибуту дії у формі. Недоліком цього підходу є можливість витоку параметрів запиту. Загалом нині найбільш оптимальним способом вважається розміщення конфіденційних даних у тілі чи заголовках, що дозволяє виключити їх витік.

HiddenHttpMethodFilter

У деяких додатках параметр форми може бути використаний для перевизначення HTTP-метода. Наприклад, наведена нижче форма може бути використана для обробки HTTP-методу як delete, а не post.

Прихована від CSRF форма HTTP-методу

<form action="/process"
      method="post">
            <!-- ... -->
	<input type="hidden"
           name="_method"
           value="delete"/>
</form>

Перевизначення методу HTTP відбувається у фільтрі. Цей фільтр має бути встановлений перед засобами підтримки Spring Security. Зверни увагу, що перевизначення відбувається лише в post, тому це навряд чи викличе будь-які реальні проблеми. Проте, найбільш оптимальним є його розміщення перед фільтрами Spring Security.

Безпечні заголовки HTTP-відповідей

Існує безліч заголовків для HTTP-відповідей, які можна використовувати для підвищення безпеки веб-застосунків. Цей розділ присвячений різним заголовкам для HTTP-відповідей, для яких у Spring Security передбачена явна підтримка. При необхідності Spring Security також можна налаштувати на використання кастомних заголовків.

Заголовки безпеки за замовчуванням

Spring Security передбачає стандартний набір пов'язаних з безпекою заголовків для HTTP-відповідей, призначених для забезпечення безпечних параметрів за за замовчуванням.

За замовчуванням Spring Security містить такі заголовки:

Безпечні заголовки для HTTP-відповідей за замовчуванням

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Strict-Transport-Security є тільки added on HTTPS requests

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

  • Керування кешем

  • Параметри типу вмісту

  • HTTP Strict Transport Security

  • Заголовок X-Frame-Options

  • Заголовок X -XSS-Protection

Керування кешем

За замовчуванням Spring Security вимикає кешування для захисту вмісту користувача.

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

Заголовки HTTP-відповідей із керуванням кешем за замовчуванням
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0

Щоб забезпечити захист за замовчуванням, Spring Security додає ці заголовки за замовчуванням. Однак, якщо твоя програма передбачає свої власні заголовки управління кешем, Spring Security не буде застосовувати свої. Це дозволяє програмам забезпечити кешування статичних ресурсів, таких як CSS та JavaScript.

Параметри типу вмісту

Історично браузери, включно з Internet Explorer, робили спроби вгадати тип вмісту запиту за допомогою сніфінгу вмісту. Це дозволило браузерам покращити взаємодію з користувачем шляхом вгадування типу вмісту для ресурсів, у яких тип вмісту не було встановлено. Наприклад, якщо браузер прийме JavaScript, для якого не встановлено тип вмісту, він сам зможе визначити тип вмісту і запустити його.

У випадку якщо дозволено завантаження вмісту, є ще безліч додаткових заходів, які слід вжити (наприклад, відображати документ лише в окремому домені, переконатися, що встановлено заголовок Content-Type, піддати документ редагування тощо). Однак ці заходи виходять за межі того, що передбачає Spring Security. Також важливо відзначити, що при відключенні сніфінгу вмісту необхідно задати тип вмісту, щоб все працювало належним чином. файл, який допустимо використовувати як декілька типів вмісту) для проведення XSS-атак. Наприклад, деякі сайти можуть дозволяти користувачам надіслати на сайт і переглянути допустимий документ postscript. Користувач-зловмисник може створити postscript-документ, який також є допустимим JavaScript-файлом, і здійснити за його допомогою XSS-атаку.

У Spring Security ніффінг вмісту вимкнено за замовчуванням шляхом додавання наступного заголовка до HTTP-відповідей:

nosniff Заголовок відповіді HTTP
X-Content-Type-Options: nosniff

HTTP Strict Transport Security (HSTS)

Коли ти набираєш вебсайт свого банку, то вводиш mybank.example.com або mybank.example.com? Якщо пропускати протокол https, то це створить потенційну вразливість допосередника. Навіть якщо сайт виконує переадресацію mybank.example.com, зловмисник може перехопити початковий HTTP-запит і маніпулювати відповіддю (наприклад, переадресувати на mibank.example.com і вкрасти облікові дані).

Багато користувачів пропускають протокол https, тому було створено протокол HTTP Strict Transport Security (HSTS). Після додавання mybank.example.com як HSTS-хоста браузер може заздалегідь розуміти, що будь-який запит до mybank.example.com має бути інтерпретований як mybank.example.com. Це значно знижує ймовірність атаки типу "людина посередині".

Відповідно до RFC6797, HSTS-заголовок впроваджується тільки в HTTPS-відповіді. Щоб браузер підтвердив заголовок, він повинен спочатку довіряти центру сертифікації, який підписав SSL-сертифікат, який використовується для створення з'єднання (а не тільки SSL-сертифікату).

Одним зі способів позначити сайт як HSTS-хост є попереднє завантаження хоста у браузер. Другий спосіб — додати у відповідь заголовок Strict-Transport-Security. Наприклад, за замовчуванням Spring Security додає наступний заголовок, який наказує браузеру вважати домен HSTS-хостом протягом року (у році приблизно 31536000 секунд):

Заголовок HTTP-відповіді за специфікацією Strict Transport Security
Strict-Transport-Security: max-age=31536000; includeSubDomains ; preload

Необов'язкова директива includeSubDomains повідомляє браузеру, що піддомени (наприклад, secure.mybank.example.com) також повинні вважатися HSTS-доменами.

Необов'язкова директива preload повідомляє браузеру, що домен слід попередньо завантажувати в браузер як HSTS-домен. Більш детальну інформацію про попереднє завантаження HSTS можна знайти на сайті hstspreload.org.

HTTP Public Key Pinning ( HPKP)

З метою забезпечення пасивної роботи Spring Security все ще передбачає підтримку HPKP у сервлетних оточеннях, але з перерахованих вище причин команда з безпеки більше не може рекомендувати до використання HPKP.

HTTP Public Key Pinning (HPKP) вказує вебклієнту, який відкритий ключ використовувати з певним вебсервером для запобігання атакам типу "людина посередині" (MITM) з підробленими сертифікатами. При правильному використанні HPKP може додавати додаткові рівні захисту від компрометованих сертифікатів. Проте через складність HPKP багато фахівців більше не рекомендують його використовувати, а Chrome навіть припинив його підтримувати.

Для отримання додаткової інформації про те, чому HPKP більше не рекомендується використовувати, читай у статтях "Is HTTP Public Key Pinning Dead?" та "I'm giving up on HPKP".

Заголовок X-Frame-Options

Дозвол додавати твій сайт до фрейму може стати проблемою з точки зору безпеки. Наприклад, за допомогою хитромудрої CSS-стилізації можна змусити користувачів натиснути туди, куди вони натискати не збиралися. Наприклад, користувач, який увійшов до свого інтернет-банку, може натиснути кнопку, яка надасть доступ іншим користувачам. Цей вид атаки відомий як клікджекінг.

Ще один сучасний підхід до боротьби з клікджекінгом — використання специфікації Content Security Policy (CSP).

Існує способів протистояти атакам типу клікджекінг. Наприклад, для захисту застарілих браузерів від атак типу клікджекінг можна використовувати код. Хоча він і не ідеальний, код відключення фреймів — це найкраще, що можна зробити у випадку із застарілими браузерами. За замовчуванням Spring Security забороняє візуалізувати сторінки у вбудованому кадрі (iframe) при використанні наступного заголовка:

X-Frame-Options: DENY

Заголовок X-XSS-Protection

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

Фільтрація найчастіше активована за замовчуванням, тому додавання заголовка зазвичай просто забезпечує її активацію і повідомляє браузеру, що потрібно робити при виявленні XSS-атаки. Наприклад, фільтр може спробувати змінити вміст найменш агресивним способом, щоб візуалізувати все необхідне. Іноді така заміна може сама по собі стати XSS-уразливістю. Натомість краще заблокувати вміст, а не намагатися його виправити. За замовчуванням Spring Security блокує вміст за допомогою наступного заголовка:

X-XSS-Protection: 1; mode=block

Специфікація Content Security Policy (CSP)

Content Security Policy (CSP) — це механізм, який вебдодатки можуть використовувати для зниження ризику виникнення вразливостей до впровадження вмісту, таких як міжсайтовий скриптинг (XSS). CSP — це декларативна політика, яка надає авторам вебдодатків можливість оголошувати та насамкінець повідомляти клієнта (користувача) про джерела, з яких вебдодаток очікує завантаження ресурсів.

Content Security Policy не вирішить усі проблеми, пов'язані з вразливістю до впровадження вмісту. Натомість CSP можна використовувати для зниження шкоди, заподіяної атаками через використання вмісту. У рамках першої лінії захисту автори вебдодатків повинні валідувати свої вхідні дані та кодувати вихідні.

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

  • Content-Security-Policy

  • Content-Security-Policy-Report-Only

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

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

Приклад Content Security Policy
Content-Security-Policy: script-src https://trustedscripts.example.com

Спроба завантажити скрипт з іншого джерела, відмінного від того, що оголошений у директиві script-src, буде заблокована користувачам агентом. До того ж, якщо в політиці безпеки оголошено директиву report-uri, то про порушення користувацькийагент повідомить за оголошеною URL-адресою.

Наприклад, якщо вебдодаток порушує оголошену політику забезпечення безпеки, наступний заголовок відповіді повідомить користувацькому агенту, що необхідно надіслати звіти про порушення за URL-адресою, зазначеною в директиві report-uri політики.

Content Security Policy з використанням report-uri

Content-Security-Policy: script-src https://trustedscripts.example.com ; report-uri /csp-report-endpoint/

Повідомлення про порушення є стандартними JSON-структурами, які можуть бути отримані або через власний API вебдодатку, або за допомогою розміщеного у відкритому доступі сервісу передачі повідомлень про порушення CSP, наприклад, report-uri.com/.

Заголовок Content-Security-Policy-Report-Only надає авторам та адміністраторам вебдодатків можливість відстежувати політику забезпечення безпеки, а не нав'язувати її. Цей заголовок зазвичай використовується під час проведення експериментів та/або розробки політики безпеки для сайту. Якщо політика вважається ефективною, її можна застосувати, використовуючи натомість поле заголовка Content-Security-Policy.

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

Content Security Policy Report Only

Content-Security-Policy-Report-Only: script-src 'self' https://trustedscripts.example.com; report-uri /csp-report-endpoint/

Якщо сайт порушить цю політику, спробувавши завантажити скрипт із сайту evil.com, користувацький агент відправить звіт про порушення за URL-адресою, зазначеною директивою report-uri, але при цьому дозволить порушуючому ресурсу завантажитися. Наступні ресурси можуть принести додаткову користь при розробці ефективної політики безпеки для вашого сайту.

Вступ до Content Security Policy

Посібник з CSP — Mozilla Developer Network

Рекомендація кандидата W3C

Специфікація Referrer Policy

Referrer Policy — це механізм, який вебдодатки можуть використовувати для керування полем реферера, що містить останню сторінку, на якій знаходився користувач.

Підхід Spring Security полягає у використанні заголовка Referrer Policy, який передбачає різні політики:

Приклад Referrer Policy
Referrer-Policy: same-origin

Заголовок відповіді Referrer-Policy наказує браузеру повідомити одержувачу джерело, в якому раніше знаходився користувач.

Специфікація Feature Policy

Feature Policy — це механізм, який дозволяє веброзробникам вибірково активувати, деактивувати та змінювати логіку роботи певних API та вебфункцій у браузері.

Приклад Feature Policy
Feature-Policy: geolocation 'self'

За допомогою Feature Policy розробники можуть вибирати набір "політик", які браузер буде застосовувати до певних функцій, що використовуються на твоєму сайті. Ці політики обмежують доступ сайту до API або змінюють логіку роботи браузера за замовчуванням для певних функцій.

Специфікація Permissions Policy

Permissions Policy — це механізм, який дозволяє веброзробникам вибірково активувати, деактивувати та змінювати логіку роботи певних API та вебфункцій у браузері.

Приклад Permissions Policy
Permissions-Policy: geolocation=(self)

За допомогою Permissions Policy розробники можуть вибирати набір "політик", які браузер буде застосовувати до певних функцій, які використовуються на твоєму сайті. Ці політики обмежують доступ сайту до API або змінюють логіку роботи браузера за замовчуванням для певних функцій.

Заголовок Clear Site Data

Clear Site Data — це механізм, за допомогою якого будь-які дані – cookies, локальне сховище тощо — можна видалити на стороні браузера, якщо HTTP-відповідь міститиме цей заголовок:

Clear-Site-Data: "cache", "cookies", "storage", "executionContexts"

Це ефективний захід для очищення, що виконується при виході з системи.

Специфікація Cross-Origin Policies

Spring Security забезпечує підтримку деяких важливих заголовків специфікації Cross-Origin Policies. Цими заголовками є:

Заголовок Cross-Origin-Opener-Policy (COOP) дозволяє документу верхнього рівня розірвати зв'язок між своїм вікном та будь-якими іншими в групі контексту перегляду (наприклад, між спливаючою вікном та його відкривачем), запобігаючи прямому доступу до DOM-моделі між ними.

Активація Cross-Origin-Embedder-Policy (COEP) запобігає завантаженню в документ будь-яких ресурсів з різних джерел, які явно не надають документу дозвіл на завантаження.

Заголовок Cross- Origin-Resource-Policy (CORP) дозволяє керувати набором джерел, які мають право додавати ресурс. Це надійний захист від атак типу Spectre, оскільки він дозволяє браузерам блокувати вказану відповідь до того, як вона потрапить до процесу зловмисника.

Кастомні заголовки

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

HTTP

Усю взаємодію за протоколом HTTP, включно зі статичними ресурсами, слід захистити за допомогою TLS.

Як фреймворк, Spring Security не обробляє HTTP-з'єднання і тому не забезпечує підтримку HTTPS безпосередньо. Тим не менш, він передбачає низку функцій, які допомагають використовувати HTTPS.

Переадресація на HTTPS

Якщо клієнт використовує HTTP, до конфігурації Spring Security можна додати переадресацію на HTTPS як у сервлетному оточенні, так і в оточенні WebFlux.

Strict Transport Security

Spring Security забезпечує підтримку механізму Strict Transport Security та активує його за замовчуванням.

Конфігурація проксі-сервера

При використанні проксі-сервера важливо переконатися, що програма була налаштована правильно. Наприклад, багато програм будуть містити балансувальник навантаження, який відповідає на запит example.com/, перенаправляючи його на сервер додатків за адресою 192.168.1:8080. Без належної конфігурації сервер додатків не знатиме про існування балансувальника навантаження та обробить запит так, начебто клієнт зробив запит на 192.168.1:8080.

Це можна виправити, якщо використовувати RFC 7239, щоб встановити використання балансувальника навантаження. Щоб забезпечити сумісність програми, потрібно налаштувати сервер додатків таким чином, щоб він був сумісний із заголовками X-Forwarded. Наприклад, Tomcat використовує RemoteIpValve, а Jetty — ForwardedRequestCustomizer. До того ж, користувачі Spring можуть використовувати ForwardedHeaderFilter.

Користувачі Spring Boot можуть використовувати властивість server.use-forward-headers для конфігурування програми.