С первого взгляда кажется, что безопасность — это тема devops-а или бэкенда. Но разделить зоны ответственности в этом вопросе очень сложно. В этой статье я хотел бы поговорить о моментах, связанных именно с фронтендом — т.е. об атаках, которые могут грозить приложениям и по-хорошему должны фильтроваться еще на фронте без вмешательства бэка.
Статья подготовлена по материалам внутреннего митапа по информационной безопасности.
Я работаю на проектах в финансовой сфере, где постоянно ужесточаются требования к безопасности. У меня есть возможность проходить различные курсы на эту тему, в том числе бесплатно (за счет работодателя). Когда анализирую полученную на них информацию, радуюсь, что работаю во фронтенде. 90% атак связаны именно с бэкендом — т.е. защиту можно обеспечить только настройкой на “той” стороне. Ребятам с бэка нужно знать очень много всего, поскольку споткнуться можно на каждом шаге.
Ситуация на фронте выглядит намного проще, но это не значит, что о безопасности можно забыть. Есть уязвимости, защититься от которых можно на стороне фронта. На самом деле зачастую это можно сделать либо на бэке, либо на фронте примерно с одинаковым результатом. Но чтобы не проводить одну и ту же фильтрацию данных в нескольких местах, нужно явно договориться внутри команды. И мое мнение — не стоит сваливать все на бэк. Иногда со стороны фронта это проще и эффективнее.
Что такое OWASP или типы уязвимостей
Но начну я издалека — с источников информации об уязвимостях. Основной для меня — OWASP.
OWASP — это публичный проект, который занимается различной документацией, связанной с сетевой безопасностью. На его сайте можно найти много полезной информации, в том числе гайды, помогающие улучшить любое приложение. У OWASP есть сканер, а также сводки по самым распространенным на данный момент группам уязвимостей, собранные на основе опросов и открытой статистики. Атаки в этой статистике ранжируются по шкале от одного до десяти по распространенности и масштабам последствий.
К сожалению, сейчас там собрана не самая актуальная информация. Последний раз они обновляли свой список в 2021 году. Следующую версию планируют выпустить только в сентябре этого года — как раз сейчас заканчивается очередной опрос.
OWASP — Open Web Application Security Project (руководство — https://owasp.org/www-project-web-security-testing-guide/).
На самом деле сайт OWASP — огромная кладовая без дна. Туда достаточно нырнуть и остановиться уже невозможно. И чем дальше идешь, тем больше убеждаешься, что ничего не знаешь про безопасность. Кажется, что с изучением даже одной атаки не закончить.
Помимо OWASP можно обращать внимание на:
- PTES (Стандарт проведения тестирования на проникновение) — Технические рекомендации PTES (http://www.pentest-standard.org/index.php/PTES_Technical_Guidelines);
- PCI DSS (Стандарт безопасности данных индустрии платёжных карт) — Руководство PCI DSS по тестированию на проникновение (https://www.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf);
- PTF (Фреймворк для тестирования на проникновение, http://www.vulnerabilityassessment.co.uk/Penetration%20Test.html);
- NIST 800-115 (Техническое руководство по тестированию и оценке информационной безопасности, https://csrc.nist.gov/publications/detail/sp/800-115/final);
- OSSTMM (Руководство по методологии тестирования безопасности с открытым исходным кодом) — Open Source Security Testing Methodology Manual (https://www.isecom.org/OSSTMM.3.pdf).
Лично мне OWASP кажется не очень удобным. Он не перерабатывается, а актуализируется через расширение — список только увеличивается, а отдельные элементы этого списка укрупняются. Например, XSS (Cross-Site Scripting) раньше был отдельным пунктом списка, а сейчас входит в более крупную категорию Injection. Однако штука эта популярная и на собеседованиях меня просили рассказать что-то именно на базе OWASP. А вот про другие проекты никогда не спрашивали.
О каких атаках надо помнить фронтенду
В этой статье поговорим про три на мой взгляд самые популярные атаки. Считайте это введением в тему.
XSS (Reflected, Persistent, DOM-based)
Атаки XSS подразумевают исполнение вредоносного кода браузером клиента. Самый распространенный вариант — Reflected XSS. Вот, как он выглядит:
Мы добавляем некий скрипт JavaScript в URL и отправляем в таком виде пользователю. Этот скрипт может отразиться от сервера и вернуться в таком же виде клиенту. Браузер будет считать, что код валиден, поскольку он вернулся с сервера, а значит исполнит его.
Reflected XSS — “одноразовая” уязвимость. Отправляя разным пользователям, код придется вставлять в URL каждый раз. Другой вариант — Persistent XSS. Атака реализуется, если удается засунуть вредоносный код в базу или иное хранилище на бэкенде. Далее база будет его отдавать всем пользователям по запросу.
Отчасти эта атака затрагивает проблемы фильтрации на бэке. Но поговорите со своей командой бэкенда. Вопрос, где именно выявлять вставки кода, довольно холиварный. Технически скрипт спокойно может лежать в базе и ничего не сломает — основываясь на этой мысли, бэк может ждать фильтрации на стороне фронта.
Persistent XSS, пожалуй, самый распространенный вариант атаки, с которым все мы сталкивались. К примеру, дырявый сайт на самописной CMS предлагает вам оставить комментарий. В дополнение к комментарию вы пишите скрипт с отправкой cookie или редиректом пользователя на сайт, где нужны просмотры. Скрипт попадает в базу и все, кто видят этот комментарий, исполняют в своем браузере скрипт.
Лично я видел много алертов, выскакивающих при обнаружении Persistent XSS на каких-нибудь блогах. Таким образом ломали Twitter (https://hackerone.com/reports/485748) — тогда увели довольно много учеток. Да и сам я с различными вариантами XSS встречался на работе довольно часто.
Еще одна разновидность XSS — DOM-based XSS. Это одна из немногих уязвимостей, которая не связана с бэкендом (изменение на бэке никак не поможет).
Атака реализуется, когда фронт написан настолько криво, что изменение URL может выводиться в код на странице — например, если цену товара мы берем непосредственно из URL и вставляем в фильтр. Насколько я знаю, так давно никто не делает. Лично я сталкивался с такими решениями еще до выпуска из колледжа.
Идея атаки в том, чтобы вредоносным кодом, заложенным в URL, изменить DOM-дерево. Мы можем вставить скрипт, который редиректит пользователя куда-то или выполняет некий запрос. Получив возможность выполнять запросы на стороне жертвы, идей можно реализовать массу.
XSS хорошо фильтруется:
- Можно фильтровать на бэке — все данные, которые поступают от клиента, можно проверять по определенной модели.
- На фронте мы также можем фильтровать ввод при помощи того же DOMPurify, который перед любой вставкой или отображением чего-либо будет проверять данные и не допустит исполнения кода. Причем, фильтровать надо не только JS, но и HTML и CSS, поскольку вредоносные скрипты могут быть очень разные.
- Важно обращать внимание на Content-type, который мы настраиваем. Браузеры могут по-разному реагировать на MIME тип. Если на бэке об этом не говорят явно, некоторые браузеры могут попытаться догадаться. И догадки не всегда работают так, как ты ожидаешь. Управлять догадками можно через x-content-type-options: nosniff (браузер не пытается гадать MIME).
- Настройка заголовка CSP (Content Security Policy) — я читал, что все уязвимости смотрят, настроен ли у вас CSP.
С современными фреймворками типа React реализовать XSS сложнее. Чтобы запустить тот же скрипт, надо указывать специальный атрибут и случайно этого не сделаешь. В этом смысле с React все стало проще. Проблема в том, что иногда этот атрибут указывают, чтобы просто немного поиграть стилями, не особо вдаваясь в редактирование. Так что надо помнить, что подобный костыль может выйти боком.
CSRF (подделка запросов)
Коротко говоря, CSRF — это попытка заставить пользователя отправить HTTP-запрос на незащищенный сайт. Банальный пример — когда пользователь заходил на онлайн-банкинг и у него сохранились какие-то реквизиты (cookie авторизации), а мы каким-то образом заманиваем его на наш сайт и, пользуясь cookie, делаем скрытый запрос в тот же банк от имени пользователя.
В отличие от XSS лично я такие атаки не встречал и не знаю, насколько CSRF распространены. Но защищаться надо. Простые способы:
- Использовать CSRF токены. Это кусочки информации, которые присылает бэк. Раньше это было супер-легко. Условный Laravel сам создавал форму, подставлял к ней определенный параметр и бэк всегда знал, что эта форма была сгенерирована им. Сейчас, когда фронт и бэк — отдельные миры, приходится вставлять токены в запросы, которые делает React.
- SameSite cookies (lax, strict) — настройка кук, с помощью которой мы можем лимитировать хосты, с которых и на которые эти куки могут отправляться. Можно потребовать отправку только с одного домена или поддомена.
- Валидация Origin в запросах от фронта на стороне бэка.
Clickjacking (UI-Redressing)
На мой взгляд, это самая интересная штука, которую до сих пор можно встретить на каких-нибудь торрентах третьего эшелона. Пользователя заманивают на некий сайт и над функциональной кнопкой (например, кнопкой поиска) делают прозрачный iFrame, который вытаскивается наверх с помощью z-индекса. В iFrame можно сделать какой-нибудь лайк или дизлайк на Facebook. Думая, что нажимает на функциональную кнопку, пользователь будет ставить этот невидимый лайк от своего имени.
В свое время таким образом был взломан Facebook.
Как от этого защититься:
- x-frame-options (deny, sameorigin, allow-from) — можно ограничить открытие приложения только нашим хостом или списком доверенных хостов, чтобы на любых других доменах в iFrame просто ничего не открылось.
- samesite в cookie тоже поможет.
Что на проектах делаем мы
- Используем статический анализатор кода, который запускается прямо во время merge request и ищет типовые ошибки в конфигурации, потенциально приводящие к проблемам. Он размечает минорные и критические уязвимости, при этом с последними код не пропускают в прод. Такие инструменты особенно полезны, если, допустим, у вас open source проект, где кто угодно может подложить закладку (хороший пример — https://thecode.media/backdoors-to-php/). В маленьких командах их двух-трех человек, где хватает ресурсов следить за чистотой кода друг друга, это менее критично.
- Проверяем образ по базе известных уязвимостей. Такие инструменты обычно вываливают предупреждение со ссылкой на определенный CVE, где можно прочитать, чем все это грозит.
- Тестируем результат.
Полезные ссылки
- OWASP Cheat Sheet (https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) — список рекомендаций по каждой уязвимости, можно искать по функционалу приложения. Здесь по пунктам расписано, что можно сделать для защиты.
- CWE (Common Weakness Enumeration, https://cwe.mitre.org/data/index.html) — это разработанный сообществом список типов слабых или уязвимых мест программного и аппаратного обеспечения. Можно автоматически проверять образ на возможные уязвимости из этого списка.
- https://portswigger.net/ (burp suite) — здесь можно пройти ряд практических работ. Платная версия заточена скорее на специалистов по безопасности, но бесплатная тоже помогает больше понять про взломы рядовому разработчику.
Автор: Сергей Широковских, Максилект.