Безпека в інтернеті, або 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 досі є досить серйозною загрозою ваших веб-проектів — можемо переходити до розгляду способів захисту.
Фільтрація/санитизация даних
Мабуть, самий надійний спосіб захиститися від міжсайтового скриптинга — це фільтрація (або санитизация) даних. Не думаю, що я зможу розповісти вам щось нове, але хотів би підкреслити основні моменти:
- Не довіряти нікому (з вашого API теж можуть прийти небезпечні з точки зору HTML дані, на стороні сервера працюють такі ж програмісти, які могли забути щось відфільтрувати).
- Не намагайтеся написати санитизатор самостійно, ви не розумніші десятків тисяч хакерів, які намагаються обійти всі варіанти фільтрації. Вже існує безліч зрілих рішень для будь-якої технології.
- Загальна санитизация не підходить, якщо ми не знаємо тип вмісту, який збираємося відфільтрувати (адже для URL і для HTML небезпечними будуть різні кодові комбінації).
Використання 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, або ще щось. Залежно від цього браузер або сам об'єкт міг би валідувати/фільтрувати дані, нічого не ламаючи.
Веб-платформа або полифилы надає набір готових типів:
- TrustedHTML
- TrustedScript
- TrustedURL
- TrustedScriptURL
Але при цьому ми можемо додавати свої в разі необхідності.
Як підключити 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