Защита веб-приложений #
Васильев Андрей Михайлович, 2024
Версии презентации
Виды информации #
В рамках веб-приложений приходится работать с разными видами информации:
- Публичная: описание товаров, статьи и т.д.
- Конфиденциальная: ФИО пользователей, пароли, банковские данные
Конфиденциальную информацию необходимо защищать:
- Компании-конкуренты могут неправомерно использовать эту информацию
- Вредители могут перехватить управление веб-приложением, причинив финансовый вред как владельцу сайта, так и его пользователям
- Вредители могут воспользоваться этой информацией для вреда клиентам сайта
Веб-браузеры включают в себя множество средств защиты информации, на стороне веб-приложения разработчику тоже необходимо поддерживать должный уровень безопасности
Безопасность и конфиденциальность #
Данные концепции являются различными, но сильно связными
- Безопасность — это процесс защиты личных данных и систем от несанкционированного доступа
- Конфиденциальность — это предоставление пользователю возможность контролировать как его данные собираются, хранятся, используются и что они не используются безответственно
Хорошая безопасность является необходимой для обеспечения конфиденциальности
Обеспечение исключительно конфиденциальности может привести к краже или порче данных системы, т.к. небезопасная передача данных может привести к проблемам.
Функции безопасности веб-браузера #
Рассмотрим базовые возможности по обеспечению безопасности в веб-браузерах
- Шифрование канала передачи данных
- Контроль источника запроса, CORS
- Контексты безопасности (важно для подключения JavaScript-документов)
- . . .
Многие вопросы не будут рассмотрены в данной лекции, однако рекомендуется самостоятельно ознакомиться со всеми актуальными угрозами и способами их противодействия
Шифрование канала передачи данных #
Корректно зашифрованный поток передачи данных обеспечивает безопасную передачу данных между веб-браузером и сервером: если данные будут перехвачены третьей стороной, то они не смогут их проанализировать
На настоящий момент безопасным считается шифрование с помощью протокола TLS, лежащего в основе протокола HTTPS (HTTP Secure)
Для реализации шифрования необходимо:
- Зафиксировать доменное имя сервера (получить доступ или приобрести)
- Получить подписанный сертификат, являющийся основой для шифрования данных
- Можно заплатить доверенному центру сертификации за выпуск сертификата
- Можно обратиться к сервису автоматизированного управления сертификатами (Automated Certificate Management Environment, ACME), например Let’s Encrypt
- Подключить сертификат к веб-серверу либо внутри веб-приложения, либо на уровне прокси-сервера
Без корректной реализации шифрования обеспечить безопасность невозможно
Особенности шифрования #
- Веб-сервер может установить заголовок Strict-Transport-Security, чтобы указать необходимость использования HTTPS
- Прозрачность сертификатов позволяет веб-браузеру отслеживать некорректно используемые сертификаты
- Веб-браузер может отображать смешанное содержимое, т.е. когда часть данных получена по шифрованному каналу, а часть данных по незашифрованному каналу. Даже частичная передача данных по нешифрованному каналу может значительно снизить безопасность. По умолчанию такое поведение блокируется, что хорошо
- Сервер может использовать устаревшие схемы шифрования, что значительно снижает безопасность пользователя, своевременно обновляйте настройки шифрования, которые устанавливает сервер
Контроль источника запроса #
Следующие технологии используются для реализации контроля источника запроса
- Политика того же источника определяет как документ или скрипт, загруженный с одного источника может взаимодействовать с ресурсом из другого источника
- Cross-Origin Resource Sharing (CORS) — механизм, использующий дополнительные заголовки, позволяющий агенту получить разрешение на доступ к ресурсам сервера
Первая технология запрещает доступ к ресурсам сервера, если запрос был сделан со страницы, загруженной с другого домена
Особенности работы с CORS #
- При запросе дополнительных данных с того же источника они допускаются
- При запросе данных с других источников клиент обязуется передать заголовок
Origin
, указывающий с какого домена выполняется запрос- При запросе данных с другого источника клиент должен выполнить предварительный OPTIONS-запрос, для получения политик доступа
- Сервер должен ответить с заголовком
Access-Control-Allow-Origin
, указывающий политику разрешений доступа к ресурсу
Поддержка CORS в http4k #
В библиотеке http4k реализована поддержка CORS для серверных приложений
- ServerFilters.Cors — фильтр, который необходимо настроить для всего приложения
- CorsPolicy — параметры описания CORS-политики приложения
- OriginPolicy — набор подходов для обработки источника: любой из, только, шаблон или разрешить все
Защита окружения исполнения сервера #
Доступ к данным должен осуществляться только уполномоченными лицами, чтобы исключить неправомерное использование, изменение, удаление данных или нарушение работы веб-приложения
Для обеспечения безопасности на стороне сервера необходимо выполнять работу систематически над:
- Исходным кодом веб-приложения (ошибки должны остутствовать)
- Обновлениями безопасности веб-сервера и другой инфраструктуры
- Политикой хранения и обновления паролей (как приложения, так и инфраструктуры)
- Настройками безопасности клиентского кода
Это комплексная задача, корректное решение которой требует детального понимания всех аспектов работы приложения и его окружения
Типичные проблемы сверверных веб-приложений #
Кратко рассмотрим проблемы, связанные с передачей данных от пользователя
- Межсайтовый скриптинг (XSS)
- SQL-инъекции
- Подделка межсайтовых запросов (CSRF)
- Перехват нажатий мышкой от пользователя (clickjaking)
- Отказ в обслуживании (DoS)
- Раскрытие доступа к файловой системе
- Выполнение команд в рамках основной ОС
Список текущих актуальных угроз можно посмотреть на сайте OWASP
SQL-инъекция #
Уязвимости SQL-инъекций позволяют злоумышленникам выполнять произвольный SQL-код в базе данных, позволяя получать, изменять или удалять данные независимо от реализованной системы прав пользователя
Эта уязвимость присутствует, если пользовательский ввод, который передаётся в SQL-запрос, может изменить смысл оператора
Рассмотрим следующий код по подготовке SQL-запроса:
val statement = "SELECT * FROM users WHERE name = '$userName';"
Если переменная userName
передаётся от пользователя (например в параметрах запроса), то пользователь может передать туда следующую строку:
a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't
В результате выполнится SQL-код:
SELECT * FROM users WHERE name = 'a';DROP TABLE users; SELECT * FROM
userinfo WHERE 't' = 't';
Управление безопасностью #
- Веб-приложения, работающие в сети Интернет, находятся в небезопасном окружении, постоянно открыто для атак извне
- Атакующие постоянно находят новые подходы для обходы средств защиты
- В частности находят ошибки в уже выпущенном программном обеспечении
- Чем больше функций предоставляет приложение, тем больше возможностей есть у атакующего
- Ввод от пользователя может и будет содержать проблемные данные
- Злоумышленники будут пытаться взломать и защиту программного обеспечения, в рамках которой запущено веб-приложение
- Разработчику веб-приложений необходимо постоянно заниматься изучением новых угроз и способов их противодействию
Управление конфиденциальными данными #
После реализации системы безопасности приложения зачастую необходимо реализовать разделение полномочий пользователей для обеспечения конфиденциальности
Пример. Деление на читателей и администрацию #
- Большинство пользователей может только просматривать информацию
- Администрация может добавлять новые элементы на ресурс
- Если пользователи могут добавлять данные, то администрация имеет возможность модерировать данные, введённые пользователем
Сложные системы разделения полномочий #
- Пользователю может быть доступна только часть функций
- Предоставление функций может определяться динамически приложением
Идентификация, аутентификация и авторизация #
- Идентификация — процедура, в результате выполнения которой для субъекта идентификации выявляется его идентификатор, однозначно определяющий её в информационной системе, например имя пользователя
- Аутентификация — это процедура проверки подлинности субъекта, например путём сравнения введённого им пароля с паролём, сохранённым в базе данных
- Авторизация — предоставление субъекту прав на выполнение определённых действий, например на просмотр конфиденциальной информации
Пример процедуры аутентификации #
- Пользователь заходит на сайт и нажимает на кнопку «войти в систему»
- Система показывает экран для входа в систему
- Пользователь вводит имя пользователя, идентифицирует себя
- Пользователь вводит пароль, задаёт данные для аутентификации
- Пользователь нажимает на кнопку «Войти», начинает процедуру аутентификации
- Система получает введённые имя пользователя и пароль, выполняет их проверку и выполняет аутентификацию
- Если введённые данные верны, то аутентификационный токен передаётся пользователю в ответе
- При следующих обращениях к серверу браузер передаёт данный токен, сервер использует его для авторизации действий
Почему так сложно? #
- Аутентификация без идентификации невозможна — нельзя определить субъекта, операцию выполнить невозможно
- Авторизация без аутентификации невозможна — нельзя понять какие действия можно разрешать данному пользователю
Авторизация без идентификации возможна — публичная информация обычно доступна любому пользователю. Но даже в этом случае неявная авторизация выполняется
Как оно может стать ещё сложнее:
- Вариантов решения задач идентификации, аутентификации и авторизации много
- Зачастую требуется реализовать несколько вариантов аутентификации в рамках одного приложения
Самостоятельно придуманные процессы аутентификации могут быть небезопасны, в приложениях следует реализовывать сценарии, проверенные специалистами по безопасности
Варианты аутентификации пользователей #
- Аутентификация согласно встроенным возможностям протокола HTTP
- Аутентификация с сохранением сессии
- Аутентификация с использованием сертификатов
- Аутентификация по одноразовому паролю
- Аутентификация по ключам доступа
- Аутентификация по токенам
- Стандарты аутентификации OAuth и OpenID Connect
Аутентификация по протоколу HTTP #
Задача аутентификации возникла давно и в рамках стандарта HTTP утверждено несколько способов аутентификации: IANA
- Поддерживаемый способ аутентификации сервер передаёт в HTTP-заголовоке
WWW-Authenticate
- Данные на сервер передаются клиентом в заголовке
Authorization
- После успешной авторизации пользователя браузер сохраняет данные в своём кеше
- При каждом следующем запросе они заголовк
Authorization
отправляется - При обработке каждого запроса сервер извлекает данные из заголовка и выполняет авторизацию
- При каждом следующем запросе они заголовк
Базовая HTTP-аутентификация, RFC7617 #
Особенности базовой аутентификации #
- Браузер сохраняет введённые данные и передаёт их ко всем последующим запросам серверу через заголовки
- Для сброса введённых данных необходимо перезапустить браузер
- Данные базовой аутентификации передаются в открытом виде и могут быть перехвачены. Необходимо использовать зашифрованное соединение
- Данные базовой аутентификации можно передать в рамках URL-запроса
https://username:password@example.com/
Имя пользователя и пароль передаются в формате имя:пароль
и отделяются от пути символом @
Будьте аккураты с копированием ссылок — в них могут содержаться конфиденциальные данные
Куки в HTTP протоколе #
HTTP cookie — это небольшой фрагмент данных, отправляемый сервером на браузер пользователя, который может сохранить и отсылать обратно с новым запросом к серверу
Используются в веб-приложениями для:
- Управления сеансами пользователя, аутентификации, паполнения корзины
- Персонализации контента
- Мониторинга поведения пользователя
Заголовки Set-Cookie
и Cookie
#
Заголовки Set-Cookie
устанавливаются сервером в рамках своего ответа
Простой заголовок может выглядит так:
Set-Cookie: <имя cookie>=<значение cookie>
Таких заголовков в ответе сервера может быть несколько:
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
Браузер будет передавать их клиенту в рамках заголовка Cookie
:
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
От клиента на сервер будут переданы все куки, которые соответствуют пути запроса документа от веб-браузера
Время жизни куков #
- Сессионные cookie удаляются при закрытии клиента, то есть существуют только на протяжении текущего сеанса
- Правила завершения сеанса определяет разработчик веб-браузера
- Сеанс может длится бесконечно (невообразимо долго)
- Постоянные cookie удаляются не по завершении сессии, а при наступлении определённого интервала времени, например:
Set-Cookie: id=5aoeu; Expires=Wed, 20 Nov 2010 10:15:00 GMT;
Пользователь может удалить или установить куки на стороне клиента самостоятельно
Безопасность HTTP куков #
- Данные передаются в открытом виде, для защиты нужно использовать SSL-шифрование
- Для защиты данных куки от доступа из контекста кода на JavaScript, необходимо использовать атрибут
HttpOnly
- Можно явно указать доменные имена, с которых будет доступна информация с помощью атрибутов
Domain
и пути с помощьюPath
- «Раздражающее» сообщение о куках необходимо по законодательству стран сообщать пользователю об их использовании веб-приложением и их назначением
- Многие JavaScript-библиотеки добавляют свои куки, чтобы следить за пользователем
Аутентификация по имени и паролю #
Нет строгого стандарта (RFC), который определял бы процедуру авторизации с использованием формы, каждый разработчик может реализовать логику самостоятельно
Ключевая задача — сформировать токен аутентификации и сохранить его в куки
Следующие запросы будут выполнять аутентификацю по токену из куков
Сложности аутентификации по паролю #
- Что делать, если злоумышленник получил доступ к кукам?
- Как упростить жинь пользователю корпоративных систем, чтобы он один вводил пароль один раз (и не записывал его на бумажке у компьютера)
Для решения этих задач процедуру аутентификации делегируют единой системе, а другие приложения интегрируются с ней с помощью протоколов OpenID Connect и OAuth 2.0
Реализация аутентификации по паролю #
Для решения задачи аутентификации своими силами необходимо:
- Корректно сохранять данные для идентификации и аутентификации
- Сохранять средства быстрой проверки аутентификации, токены
Хранение аутентификационных данных #
В рамках приложения необходимо организовать хранилище для зарегистрированных пользователей. Данное хранилище должно включать объекты с полями:
- Имя пользователя
- Зашифрованный пароль пользователя
Пароль пользователя нельзя хранить в открытом виде, т.к. при потере данных (которая точно случится) вся защищённая информация станет доступна
Для защиты пароля рекомендуется использовать схему с добавлением соли
Базы данных обычно предоставляют возможности по выполнению хеширования
Особенности использования соли #
- Соль должна хранится отдельно от хеша пароля, иначе схема теряет смысл
- Соль должна быть достаточно длинная, т.к. небольшую соль достаточно легко подобрать по таблицам
- Соль должна обладать высоким уровнем энтропии
- Соль нельзя хранить в исходном коде приложения, которое устанавливается на многих компьютерах, т.к. она может быть потеряна
- Соль может создаваться динамически, например согласно стандарту PBKDF2
Хранение и проверка сессионных токенов #
Для верификации сессионных токенов необходимо:
- доверять данным, пришедшим от пользователя, т.е. отказаться от проверки данных
- хранить сессионный токен на стороне сервера для сверки
- применять схемы с шифрованием данных или подписью данных внутри токена
Хранение сессионных токенов на стороне сервера несёт ряд сложностей:
- сколько токенов может быть выдано конкретному пользователю?
- сколько пространства потребуется для хранения всех выданных токенов?
- сколько времени уйдёт на выявление их корректности?
Последняя схема имеет ряд проблем:
- надо корректно реализовать криптографические схемы
- сложно отозвать скомпрометированные токены
Настройки приложения #
Приложению для работы могут потребоваться настройки для встраивания в конкретную информационную систему:
- Каталог для хранения данных на файловой системе
- Параметры подключения к базе данных
- Параметны подключения к внешним системам
- Соль для формирования токенов аутентификации и т.п.
Настройки приложения зачастую считываются при старте приложения, для их изменения приложение перезапускается
Источники настроек #
- Настройки могут быть считаны из файлов на файловой системы
- Настройки могут быть получены из сетевого хранилища (Infisical)
- Настройки могут быть переданы через переменные окружения
В рамках курса будем использовать хранение настроек на файловой системе, а для промышленного использования рекомендуется использовать сетевое хранилище
Поддержка настроек в http4k #
Библиотека http4k изначально разрабатывалась для использования в облачных окружениях, в которых параметры передаются через переменные окружения
- Источником настроек является окружение, описываемое классом Environment
- Для считывания данных из окружения можно воспользоваться линзами EnvironmentKey
В текущей реализации окруежние может быть построено на основе разных источников:
- Переменных окружения,
Environment.ENV
- Свойств JVM-окружения,
Environment.JVM_PROPERTIES
- Properties-файлов,
Environment.fromConfigFile
- Из YAML-файлов,
Environment.fromYaml
Составление цепочки из источников #
В рамках приложения удобно иметь возможность получения настроек из множества источников одновременно:
- Из файлов для продуктового использования
- Из переменных окружения для тестирования работы приложения
Для формирования цепочки из источников предлагается инфиксная функция overrides
:
val environmentSource = Environment.ENV
val fileSource = Environment.from("app.properties")
val source = evironmentSource overrides fileSource
Переменные окружения могут быть настроены как в Gradle, так и в параметрах запуска системы сборки Gradle