Шпаргалки по безопасности: REST
REST — чрезвычайно популярная архитектура веб-приложений. Для вызова функций на сервере используются обычные HTTP-запросы с задаваемыми параметрами (для структуризации параметров обычно используют JSON или XML), при этом, строгого стандарта для REST-архитектуры не существует, что добавляет ей гибкости (и, конечно, немного хаоса).
REST позволяет гибко подойти к вопросу безопасности, или, чем грешат многие, не подойти к вопросу совсем. Основываясь на OWASP, мы подготовили список советов, которые помогут вам улучшить безопасность вашего REST-приложения.
В качестве отправной точки в тех редких случаях, когда это тут нужно, мы используем python и Django.
Правило 0
HTTPS
Просто настройте. Пожалуйста. Защита передаваемых данных еще никому не вредила. Даже если вы думаете, что защищать в данный момент нечего, это не всегда будет так и вам все равно придется настраивать HTTPS. Так что настройте его лучше сразу, и всем будет хорошо.
Правило 1
Аутентификация
Казалось бы, очевидный совет, которым, однако, многие предпочитают пренебрегать. На приложении всегда должна быть аутентификация, даже если вы сейчас думаете, что будете им пользоваться только внутри компании и нет разницы, кто из сотрудников это делает. А если дополнить еще и журналами, то расследование инцидентов и анализ активности станет несоизмеримо проще.
В качестве токенов доступа в данный момент считается хорошим тоном использовать JWT (JSON web tokens). Не используйте токены со значением {«alg»: «none»}, для контроля за целостностью, токены всегда должны содержать подпись или MAC! Подпись предпочтительнее в силу того, что ключи генерации и верификации у MAC совпадают (или могут быть легко вычислены друг из друга), то есть если сервер в состоянии проверить значение MAC, то он может и генерировать его значения. Подпись к тому же подтверждает не только целостность сообщения, но и личность отправителя.
Правило 2
Запретите те HTTP методы, которыми вы не пользуетесь
Сконфигурируйте на сервере белый список тех методов, с которыми вы работаете (GET, POST, PUT и т. д.) и отклоняйте все, что под этот список не подпадает. Вряд ли на продакшене вашему серверу нужно обрабатывать запросы типа TRACE (данный метод является составляющей XST-атаки (Cross-Site Tracing), которая состоит из XSS-атаки и метода TRACE или TRACK. Это атака позволяет, например, украсть куки пользователя, даже если они помечены как HttpOnly). Чем меньше информации о вашей инфраструктуре доступно снаружи — тем лучше.
Правило 3
Разграничьте доступ
Всем ли вашим пользователям нужны все методы, например, DELETE? Если вы не хотите, чтобы какие-то пользователи имели возможность проводить определенные операции — настройте разграничение доступа в вашем приложении. Например, обычному пользователю доступен только метод GET на часть функций, менеджеру — GET и POST и т. д. Для этого стоит задать роли в БД, которые можно будет выдавать пользователям для удобства управления доступом.
В итоге у каждой функции будет блок проверки приблизительно такого типа:
if request.POST and user.is_manager:
do_stuff()
Правило 4
Задумайтесь об ограничении количества запросов
Если вы думаете, что ваши пользователи не должны отправлять вам сто тысяч запросов в секунду, то следует это число ограничить. Используйте API-ключи или любой другой удобный для вас механизм с целью отслеживания и ограничения количества запросов, которые будут обрабатываться в определенный период времени от одного пользователя. Для Django данную функциональность, например, предоставляет django-ratelimit, где в качестве идентификатора для ограничения можно задать различные параметры, не обязательно API-ключи, а, можно IP-адрес.
Правило 5
Обязательно производите валидацию/санитизацию входных данных
никогда не доверяйте передаваемым входным параметрам, всегда проводите валидацию/санитизацию;
- если это возможно (и там, где это возможно), поставьте ограничение на длину/тип/формат входных данных и самого запроса. Отклоняйте все запросы/передаваемые данные, превышающие заданную вами длину или не совпадающие с типом/форматом;
- при обработке строк всегда экранируйте все спецсимволы;
- если вы используете фреймворк, то большинство из них содержат собственные встроенные инструменты валидации и санитизации (навскидку из популярных — Django (python) и Yii2 (php)), так что важно изучить их возможности и, если какой-то необходимый вам аспект не покрыт — найти библиотеку, которая закрывает это, либо написать таковой функционал самостоятельно;
- ведите учет ошибок валидации, если запросы каких-то пользователей постоянно проваливают валидацию — задумайтесь над автоматической блокировкой подобных пользователей;
- убедитесь, что ваш парсер входных параметров (или его текущая версия) не подвержен каким-либо атакам сам по себе.
Правило 6
Не отдавайте больше информации, чем необходимо
Если какой-то запрос вызвал ошибку в приложении, или оно просто по какой-то причине в данный момент недоступно — не сообщайте в ответе подробности, возвращайте максимально абстрактное сообщение об ошибке. Некоторые сервера по умолчанию возвращают stacktrace после ошибки, так что убедитесь, что вы перенастроили данную логику.
Правило 7
Всегда ведите журналы
Пусть каждое событие (аутентификация, ошибка, запрос и т.д.) заносится в логи максимально подробно. Разнесите их логически для более удобного поиска по ним в случае необходимости. На всякий случай перед записью в журналы проводите санитизацию записываемых данных.
Правило 8
Правильно указывайте Content-Type — это важно!
Чтобы браузер (или клиент) корректно считывал предоставляемые данные, важно, чтобы был верно указан Content-Type, соответствующий предоставляемым данным. В случае с браузерами стоит также выставить заголовок X-Content-Type-Options: nosniff, дабы предотвратить попытки браузера обнаружить другие Content-Type помимо того, который был послан в действительности (мера противодействия XSS-атакам).
Правило 9
Проводите валидацию Content-Type
- Стоит настроить отклонение запросов, если их Content-Type отличается от их содержимого. Это позволит снизить риск неверной обработки данных, которая может привести инъекции или исполнения кода у сервера/клиента.
- Также стоит отклонять запросы, Content-Type которых вы не поддерживаете, или у которых данный заголовок вообще отсутствует. Избегайте также прямых ответов о том, какой Content-Type функция принимает/выдает, если это не является необходимым (это поможет избежать XXE-атак).
- В REST сервисах является обычным делом поддерживать несколько типов ответов (например json и xml) и клиент указывает предпочтительный тип ответа в заголовке Accept при запросе. Избегайте копирования содержимого заголовка Accept в Content-Type ответа в качестве механизма выставления данного заголвка. Отклоняйте также запросы, у которых заголовок Accept прямо не содержит хотя бы один из поддерживаемых типов.
Правило 10
Не забывайте про настройку Cross-Origin Resource Sharing (CORS)
CORS — это стандарт, описывающий работу с кросс-доменными запросами. Если ваше приложение не поддерживает CORS, отключите работу с этим типом заголовков. Если же его использование у вас необходимо, политика доступа должна быть максимально конкретной и строгой.
Правило 11
Не раскрывайте параметры в URL
Все чувствительные данные (пароли, ключи, токены и логины желательно тоже) должны находится внутри тела запроса или в заголовках, но ни в коем случае не появляться в URL. Если вам необходимо передать чувствительные данные через метод GET, то поместите их в заголовок.
Нельзя:
example.com/controller/123/action? apiKey=a53f435643de32
Можно:
example.com/controller/123/action
Заголовки:
Host: example.com
User-Agent: Mozilla…
X-APIkey: a53f435643de32
Правило 12
Задумайтесь над защитой от CSRF атак
Подробнее про все виды защиты вы можете прочитать здесь, а так самым популярным способ снижения риска проведения атак данного типа считается использование CSRF-токенов.
Правило 13
Изучите свой фреймворк
Если для реализации REST в своем приложении вы используете какой-либо фреймворк, то стоит изучить его, а не слепо использовать, как это часто делают. Прочтите внимательно документацию, особенно ту часть, которая касается безопасности. Не оставляйте его работать на настройках по умолчанию! У каждого фреймворка есть свои нюансы, особенно когда это касается безопасности, так что знать их очень важно.