Публикуем разбор нескольких важных вопросов по безопасности веб-приложений от Кутлымурата Мамбетниязова, старшего эксперта по информационной безопасности (ИБ) в компании BTS Digital.
Планируем рубрику с такими практическими разборами сделать регулярной, чтобы вы могли обмениваться опытом с коллегами. Если Вам есть чем поделиться — пишите на editor@digitalbusiness.kz!
Как я стал безопасником
До прихода в информационную безопасность я занимался разработкой веб-приложений и сервисов. Писал CRM-системы, интернет-магазины, разного рода порталы, пока в один день не увидел на своем сайте примерно такую картину:
Сайт был взломан, как они представились, турецкими хакерами. Тогда я не понимал, зачем кому-то взламывать свежий сайт без финансового оборота. Позже, изучая вопрос, понял, что черным хакерам всегда нужны свежие чужие IP-адреса и сайты, чтобы они могли, не попадая в бан, отправлять спам и фишинговые письма. Или использовать взломанный сайт как прокси-сервер (т.е. пускать весь поток данных через взломанный сайт) для сокрытия следов новых атак.
А это означает, что информационная безопасность важна для всех, в независимости от масштабов проекта.
Сегодня есть отдельные специалисты по ИБ для проверки мобильных приложений, веб-приложений, инфраструктуры, сетей, умных устройств, АСУ ТП, облачных решений. Есть даже специалисты социальной инженерии, кто занимается самым уязвимым звеном любой компании — людьми. Они отправляют фишинговые письма для проверки бдительности сотрудников компании, а самые продвинутые могут переодеться в сантехника или электрика и прийти прямо в офис компании, чтобы миновать охрану и сесть за свободный компьютер в офисе.
Далее речь пойдет про безопасность веб-приложений. В пяти пунктах я покажу основные ошибки, допускаемые казахстанскими разработчиками, и советы по недопущению их.
1. Про секретные ключи
Разработка системы означает неминуемое использование разного рода паролей, секретных ключей, подписей. Но речь пойдет про одну из популярных технологий авторизации среди разработчиков — JSON Web Token (JWT). Это — токены, которые используются для авторизации в разных системах. Подробнее можно узнать и поиграть на jwt.io.
JWT токены состоят из 3-х частей, закодированные в base64.
Пример токена:
eyJhbGciOiJIUzI1N
Точки разделяют токен на части. Первая часть отвечает за тип шифрования токена. Вторая — содержит основную информацию, например имя, уровень доступа или роль авторизованного пользователя. Третья часть содержит сигнатуру (подпись) токена на основе секретного ключа. С помощью подписи можно узнать, действительно ли этот токен был выпущен вашей системой или его кто-то подделал.
Это означает, что любой, кто знает секретный ключ JWT токенов, с вероятностью 90% захватит контроль над вашим приложением. Конечно, любой подумает, что его ключ никому не доступен или вообще не подозревает, что ключ давно известен всему интернету. Как это происходит?
Попробуем ввести в поисковик: «jwt java example spring boot».
Google выдает 753 тысяч результатов с вариантами реализации JWT токенов на базе Java Spring Boot.
Открываем первую же ссылку и видим следующее:
В данном случае «javainuse» является тем самым секретом, который используется для проверки токенов. Какие шансы, что вы скачаете этот код и забудете поменять секрет?
Проверяем дальше:
Как минимум этот «секрет» уже добрался до репозиториев на Github:
Энтузиасты в интернете уже собрали по всем открытым библиотекам и туториалам такие секретные ключи и сделали из них словарь секретов.
2. Про кодогенерацию
Нынешние фреймворки здорово помогают при разработке систем, генерируя необходимые условия для успешной разработки, в том числе автоматически создают CRUD.
CRUD (Create, Read, Update, Delete) — объединенное название нескольких операций работы над данными.
Например, таблица в базе данных такого вида:
Превращается в код такого вида (на скриншоте код из Yii2):
Это позволяет искать данные пользователей таким образом:
https://testsite.kz/users/index?UsersSearch[PARAM]=VALUE
Где PARAM — название параметра, а VALUE — необходимое нам значение.
Это удобно, и не нужно каждый раз писать один и тот же функционал для реализации CRUD. Например, поиск пользователя по имени выглядит так:
Но что будет, если вместо параметра name передать параметр password?
Появится возможность перебирать пароли (или хэш пароля) пользователей посимвольно.
3. Про SQL-инъекции
Существует множество векторов атак на веб-приложения. Один из вариантов — это SQL-инъекция.
Всегда найдутся те, кто скажет, что у них есть фреймворки, которые автоматически убирают эту проблему. Но с ними не согласна, как минимум, статистика.
Существуют платформы, которые платят за находку уязвимостей на сайтах крупных компаний. Одна из таких платформ Hackerone. По ссылке 261 случаев находки SQL-инъекций в крупных компаниях.
Неполный список компаний, у которых нашли SQL-инъекцию: Starbucks, Razer, Mail.ru, QIWI, Acronis, Zomato, Uber, OLX, GitLab, IBM и другие.
Посмотрите, сколько приходится платить компаниям из-за того, что разработчики забыли добавить 1 строчку в код:
Откуда берутся SQL-инъекции?
Первое, что делают веб-разработчики, изучая какой-либо язык, ищут в поисковике «как написать регистрацию и авторизацию на языке X». Я попробовал сделать так же:
Не выбирая, открыл первые 3 ссылки.
1-я ссылка. Сайт продает курсы по программированию.
Параметр $login без фильтрации попадает в SQL-запрос, что позволяет внедрять свои инструкции, например, вывести пароли пользователей.
2-я ссылка. Та же проблема. При этом автор пишет, что нужно фильтровать входящие параметры, но сам этого не делает правильно.
3-я ссылка. Точно такая же картина
Туториалы не всегда пишутся опытными разработчиками. К тому же у каждого свое представление об опытности.
4. Про фильтры
Прелесть программирования в том, что легко найти библиотеку, в которой, казалось бы, реализован нужный вам функционал. Это даже могут быть стандартные библиотеки самого языка программирования.
Речь пойдет про функции фильтрации, а именно про фильтрацию e-mail адресов.
Представьте, что вы отправили пять писем на эти адреса:
test@murat.one
test+test1@murat.one
test+qwe@murat.one
test+sdfgdfghdfg@murat.one
test+anything@murat.one
В таком случае все пять писем придут на адрес test@murat.one. Так как по стандарту RFC это все один и тот же адрес, а все, что идет после знака плюс, является категорией письма.
Поэтому если на вашем сайте критично, чтобы 1 человек не мог зарегистрироваться несколько раз, то запретите знак «+» для поля e-mail.
Рассмотрим два варианта стандартных фильтров для Golang и Python.
В языке Golang в пакете net/mail есть функция ParseAddress, которая отвечает за валидацию e-mail: pkg.go.dev/net/mail#ParseAddress
Простой пример кода:
А теперь попробуйте ввести в эту функцию следующие e-mail адреса:
test@murat.one
test+test@murat.one
test{{7*7}}test@murat.one
te'+union+select+1--+g't@murat.one
Вы заметите, что функция считает правильными все эти e-mail адреса. Как так произошло? А потому, что функция соблюдает стандарт RFC, который позволяет использовать почти все символы.
Вот список e-mail адресов из Wikipedia, который кажется неправильным, но на самом деле соответствуют всем стандартам:
Попробуем то же самое сделать для Python. Из пакета email.utils используем функцию parseaddr: docs.python.org/3/library/email.utils.html#email.utils.parseaddr
Получим следующий результат:
То есть все e-mail адреса успешно прошли валидацию, хоть и содержали в себе вредоносный SQL-запрос и HTML-код.
Но и на этом сюрпризы не заканчиваются. Например, для почтовых серверов Gmail точки в адресе не играют никакой роли:
И почты johnsmith@gmail.com, john.smith@gmail.com, jo.hn.smi.th@gmail.com, jo.hns.m.i.t.h@gmail.com и т.д. являются идентичными. Если для вас важно, чтобы люди не регистрировали мультиаккаунты, то следует для Gmail написать отдельный фильтр, который удаляет точки.
5. Про отладку
Режим отладки помогает разработчикам быстро выявить источник проблемы в коде, но если его оставлять включенным в продуктовой среде, это может помочь злоумышленникам быстрее собрать чувствительную информацию о системе. Кроме чувствительной информации, некоторые функции отладки могут содержат скрытый или незаметный в документации функционал.
Например, в языке программирования PHP для отладки используется функция phpinfo() — www.php.net/manual/en/function.phpinfo.php
Отладка запускается 1 строкой кода:
<?php phpinfo(); ?>
Но доступная всем такая функция:
- позволяет временно загружать файлы в папку %TMP%
- позволяет смотреть защищенные cookies
- выдает информацию по установленными модулям и ОС
- выдает настоящий IP-адрес сервера
Согласитесь, теперь все это выглядит не так безобидно. Вот пример загрузки файла через phpinfo:
Охватить все отладочные функций из всех языков программирования и фреймворков невозможно, поэтому приведу пример из фреймворка Django, написанный на языке Python.
В Django, если в конфигурации указан DEBUG=true, то включается режим отладки:
docs.djangoproject.com/en/4.1/ref/settings/#debug
Режим отладки в Django позволяет узнать всю структуру функционала системы, включая адрес админ-панели:
А если интегрировать Django с другой системой, например, с Sentry, то режим отладки может выдать секретный ключ для токенов:
Что позволяет выполнять любые команды в системе из-за особенностей десериализации.
Например, один исследователь заработал $5000 за находку такой ошибки на серверах Facebook.
Заключение
Глобальное развитие технологий, а также создание фреймворков и библиотек позволяют программистам работать более эффективно, даже если они не имеют достаточных знаний в основах программирования. Это способствует созданию большого числа специалистов и облегчает жизнь всем. Но такой подход может привести к разработке небезопасных решений для современного ИТ-мира.
Для того чтобы стать отличным программистом, необходимо не только знать инструменты, но и понимать основы технологий, которые используются в работе, постоянно совершенствоваться, проводя дополнительные исследования.
Кроме того, не стоит стесняться обращаться за советами к более опытным коллегам.
Новости по теме информационной безопасности:
Казахстанский банк оставил пароли к внутренним сервисам в открытом доступе
Мнение: Казахстану нужно больше тратить на информационную безопасность