Безпека в інтернеті, або TrustedTypes як новий спосіб захисту від XSS

У цій статті я спробую вас переконати, що небезпека XSS, незважаючи на всі сучасні фреймворки, як і раніше існує. Також ми розглянемо основні способи захисту. Основну увагу приділимо нового революційного підходу — DOM TrustedTypes, який, незважаючи на те що ще знаходиться в розробці, обіцяє підняти безпеку браузерів на новий рівень.

Безпека в інтернеті

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

Однак після великих зломів таких компаній, як MySpace (XSS), eBay (XSRF) і багатьох інших, до компаній почало доходити, що потрібно щось робити.

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

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

У своїй статті я хочу зосередитися на XSS-атаки, а також розглянути новий спосіб захисту від неї — DOM TrustedTypes.

Чому XSS все ще небезпечний

XSS (англ. Cross-Site Scripting — «міжсайтовий скриптінг») — атака не нова, і велика частина програмістів з нею знайомі. Хакер використовує уразливість (як правило, це неотфильтрованный користувальницький введення даних), щоб розмістити шкідливий код.

З кожним роком фронтенд-додатків стає все більше, і самі додатки стають все об'ємніше і складніше. Тому логічно, що з'являється все більше XSS атак безпосередньо в DOM.

Давайте розглянемо приклад. Програмісти веб-додатки написали ось такий код:

Чим це може бути небезпечно? Тим, що ми виводимо неотфильтрованные дані прямо в DOM. Хакер може підкинути для нашого користувача-жертви ось таке посилання:

При цьому в DOM ми отримаємо шкідливий код:

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

Багато хто вважають, що основна мета XSS — стягнути пароль кукісів, ну а якщо пароль/токен в кукіси не зберігається (а наприклад, у localStorage), то XSS сайту не страшний. Це оману. Крім стягування кукісів, c XSS можна робити багато різних маніпуляцій, наприклад:

Як ви, напевно, здогадалися — цей код призначений, щоб красти номери кредитної картки.

А ось цей буде займатися майнингом кріптовалюти за рахунок браузера нашого користувача:

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

Якщо ви до цього не стикалися з такою утилітою, як BeEF — дуже рекомендую звернути увагу. Вона допомагає розкрити потенціал уразливості і забезпечує зручний графічний інтерфейс.

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

Щоб розвінчати цей міф, досить привести приклад Google: основна функція — пошук піддалася XSS в кінці минулого року. А привела до цього уразливість в бібліотеці Closure .

Як захиститися від XSS

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

Фільтрація/санитизация даних

Мабуть, самий надійний спосіб захиститися від міжсайтового скриптинга — це фільтрація (або санитизация) даних. Не думаю, що я зможу розповісти вам щось нове, але хотів би підкреслити основні моменти:

Використання CSP-заголовків

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

Як працює CSP

CSP — це спеціальна додаткова інформація, яка передається браузеру або в HTTP заголовках, або в мета-тегах сайту. У ній задаються правила, як браузер повинний поводитися з різними типами ресурсів: дозволити або блокувати.

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

Він просто не виконається в браузері.

Включення CPS в режимі звіту

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

CSP в реальному житті

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

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

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

Курйозний момент: завдяки CSP ми виявили, що одна з наших бібліотек використовує всередині себе метод eval. Довелося відписати їм, щоб оновили. Так CSP допомагає вам не тільки в цілому захиститися від можливих атак, але також виявити і усунути проблемні місця в ваших проектах.

Більш детально про CSP можна почитати в MDN-документації .

Використання Trusted Types в DOM

Якщо ми копнемо глибше — з-за чого відбуваються всі проблеми — то виявиться, що ми просто довіряємо браузеру робити занадто багато магії, при цьому не ставлячи контент для виконання. Найпоширеніший випадок — innerHTML= '.......' Тобто ми передаємо рядок, в якій може бути все що завгодно, у тому числі шкідливий скрипт.

При цьому ви можете працювати з innerHTML не безпосередньо — це може робити одна з ваших допоміжних бібліотек (так, наприклад, робила jQuery в методі append).

Так, може, нам просто заблокувати innerHTML на глобальному рівні? Ні, на жаль: innerHTML — не єдине проблемне місце в DOM, є багато властивостей-методів, які роблять приблизно таку ж магію з переданої рядком:

Але що, якщо б існувала можливість передавати значення не рядок, а як об'єкт? Адже DOM вже давно підтримує таку можливість:

При цьому цей об'єкт знав би свій скоуп, тобто або це URL, або це HTML, або ще щось. Залежно від цього браузер або сам об'єкт міг би валідувати/фільтрувати дані, нічого не ламаючи.

Веб-платформа або полифилы надає набір готових типів:

Але при цьому ми можемо додавати свої в разі необхідності.

Як підключити Trusted Types

Для підключення Trusted Types необхідно лише додати в CSP додаткову інструкцію:

Content-Security-Policy: trusted-types *

І тепер, якщо ми спробуємо зробити щось таке в коді:

Браузер просто видасть помилку:

Uncaught TypeError: Failed to set the 'innerHTML' property on 'Element': This document requires `TrustedHTML` assignment.

Але якщо ми зробимо привласнення через спеціальний об'єкт:

Всі відпрацює правильно!

Плюс до всього, ми ще можемо лімітувати можливі варіанти політик: додамо замість зірочки ім'я політики (класу), яку використовуємо:

Content-Security-Policy: trusted-types myPolicy

Незважаючи на те що цей проект все ще залишається у форматі Draft , ви вже можете погратися в Chrome 73+, використовуючи спеціальний прапор:

chrome --enable-blink-features=TrustedDOMTypes

TrustedTypes в реальному житті

Навіть при тому, що DOM TrustedTypes як і раніше є експериментальною функцією, якщо ви хочете в подальшому (з прийняттям стандарту) активувати його на проекті — зміни потрібно починати готувати вже зараз.

Ми на своєму проекті використовуємо Angular, який вже частково включає концепцію TrustedTypes: якщо правильно використовувати фреймворк, то він буде контролювати всю передачу і відображення даних у шаблоні, при цьому Angular чітко знає тип переданих даних і екранує їх в залежності від типу. Більше того, вже навіть є Pull Request додати TrustedTypes нативно під фреймворк.

P. S. Я сподіваюся, ця стаття допоможе вам швидше розібратися з TrustedTypes і допоможе зробити вашу систему безпечніше вже сьогодні завдяки використанню лише кількох додаткових налаштувань.

Опубліковано: 06/12/19 @ 11:00
Розділ Безпека

Рекомендуємо:

Микросервисный підхід у веб-розробці: micro frontends
Поради сеньйорів: як прокачати знання junior security specialist
Зарплатне опитування
«Я просто роблю те, що мені подобається». Як 18-річний студент навчає дітей програмуванню та видає підручники з Python
Go дайджест #11: мови 10 років, новинки в Go 1.14