Статья по безопасности REST (REST Security Cheat Sheet)
Введение
REST (или REpresentational State Transfer) — архитектурный стиль, впервые описанный в диссертации Ph.D. Роя Филдинга «Architectural Styles and the Design of Network-based Software Architectures».
Он развивался по мере того, как Филдинг писал спецификации HTTP/1.1 и URI, и было доказано, что он хорошо подходит для разработки распределенных гипермедийных приложений. Хотя REST более широко применим, он чаще всего используется в контексте взаимодействия со службами через HTTP.
Ключевой абстракцией информации в REST является ресурс. Ресурс REST API идентифицируется URI, обычно URL-адресом HTTP. Компоненты REST используют соединители для выполнения действий с ресурсом, используя представление для фиксации текущего или предполагаемого состояния ресурса и передачи этого представления.
Основные типы соединителей — клиент и сервер, вторичные — кэш, преобразователь и туннель.
REST API не имеют состояния. API с отслеживанием состояния не соответствуют архитектурному стилю REST. Состояние в аббревиатуре REST относится к состоянию ресурса, к которому обращается API, а не к состоянию сеанса, в котором вызывается API. Хотя могут быть веские причины для создания API с отслеживанием состояния, важно понимать, что управление сеансами сложное и трудно обеспечить безопасность.
Службы с отслеживанием состояния выходят за рамки этой статьи: Передача состояния от клиента к серверной части, при этом технически делая сервис без сохранения состояния, является антишаблоном, которого также следует избегать, поскольку он подвержен атакам повторного воспроизведения и олицетворения.
Чтобы реализовать потоки с помощью REST API, ресурсы обычно создаются, считываются, обновляются и удаляются. Например, сайт электронной торговли может предлагать методы для создания пустой корзины покупок, добавления товаров в корзину и проверки корзины. Каждый из этих вызовов REST не имеет состояния, и конечная точка должна проверить, авторизован ли вызывающий объект для выполнения запрошенной операции.
Ещё одной ключевой особенностью приложений REST является использование стандартных HTTP-глаголов и кодов ошибок в целях устранения лишних расхождений между разными сервисами.
Еще одной ключевой особенностью приложений REST является использование HATEOAS или гипермедиа как двигатель состояния приложения. Это придает приложениям REST самодокументируемый характер, что упрощает разработчикам взаимодействие со службой REST без предварительных знаний.
HTTPS
Безопасные службы REST должны предоставлять только конечные точки HTTPS. Это защищает учетные данные аутентификации при передаче, например пароли, ключи API или веб-токены JSON. Это также позволяет клиентам аутентифицировать услугу и гарантирует целостность передаваемых данных.
См. Статья по безопасности транспортного уровня для получения дополнительной информации.
Рассмотрите возможность использования сертификатов на стороне клиента с взаимной проверкой подлинности, чтобы обеспечить дополнительную защиту веб-служб с высоким уровнем привилегий.
Контроль доступа
Частные службы REST должны выполнять контроль доступа на каждой конечной точке API. Веб-сервисы в монолитных приложениях реализуют это посредством аутентификации пользователей, логики авторизации и управления сеансами. Это имеет несколько недостатков для современных архитектур, которые составляют несколько микросервисов в стиле RESTful.
- Чтобы минимизировать задержку и уменьшить связь между службами, решение по управлению доступом должно приниматься локально конечными точками REST.
- аутентификация пользователей должна быть централизована в поставщике удостоверений (IdP), который выдает токены доступа.
JWT
Кажется, существует тенденция к использованию Веб-токены JSON (JWT) в качестве формата токенов безопасности. JWT — это структуры данных JSON, содержащие набор утверждений, которые можно использовать для принятия решений по управлению доступом. Криптографическая подпись или код аутентификации сообщения (MAC) могут использоваться для защиты целостности JWT.
- Убедитесь, что целостность JWT защищена либо подписью, либо MAC. Не разрешайте незащищенные JWT:
{"alg":"none"}.- См. здесь
- В общем, подписи следует отдавать предпочтение перед MAC для защиты целостности JWT.
Если MAC используются для защиты целостности, каждая служба, способная проверять JWT, также может создавать новые JWT, используя тот же ключ. Это означает, что все службы, использующие один и тот же ключ, должны взаимно доверять друг другу. Другим следствием этого является то, что компрометация любой службы также ставит под угрозу все другие службы, использующие тот же ключ. См. здесь для получения дополнительной информации.
Доверяющая сторона или потребитель токена проверяет JWT, проверяя его целостность и содержащиеся в нем утверждения.
- Проверяющая сторона должна проверить целостность JWT на основе своей собственной конфигурации или жестко запрограммированной логики. Он не должен полагаться на информацию заголовка JWT для выбора алгоритма проверки. См. здесь и здесь
Некоторые утверждения стандартизированы и должны присутствовать в JWT, используемом для управления доступом. Должны быть проверены, по крайней мере, следующие стандартные утверждения:
issили эмитент - это доверенный эмитент? Это предполагаемый владелец ключа подписи?audили аудитория — входит ли проверяющая сторона в целевую аудиторию этого JWT?expили время истечения — это текущее время до окончания срока действия этого токена?nbfили не раньше времени - это текущее время после начала срока действия этого токена?
Поскольку JWT содержат сведения об аутентифицированном объекте (пользователе и т. д.), между JWT и текущим состоянием сеанса пользователя может произойти отключение, например, если сеанс завершается раньше времени истечения срока действия из-за явного выхода из системы или тайм-аута простоя. Когда происходит событие явного завершения сеанса, дайджест или хэш всех связанных JWT должен быть отправлен в список запрещенных в API, который сделает этот JWT недействительным для любых запросов до истечения срока действия токена. См.JSON_Web_Token_for_Java_Cheat_Sheet для получения более подробной информации.
API-ключи
Публичные службы REST без контроля доступа рискуют оказаться на ферме, что приведет к чрезмерным расходам на пропускную способность или вычислительные циклы. Ключи API можно использовать для снижения этого риска. Они также часто используются организациями для монетизации API; вместо блокировки высокочастотных звонков клиентам предоставляется доступ в соответствии с приобретенным планом доступа.
Ключи API могут снизить воздействие атак типа «отказ в обслуживании». Однако, когда они выдаются сторонним клиентам, их относительно легко скомпрометировать.
- Требовать ключи API для каждого запроса к защищенной конечной точке.
- Возвращаться
429 Too Many RequestsКод ответа HTTP, если запросы поступают слишком быстро. - Отозвать ключ API, если клиент нарушает соглашение об использовании.
- Не полагайтесь исключительно на ключи API для защиты конфиденциальных, критических или ценных ресурсов.
Ограничить методы HTTP
- Примените список разрешенных методов HTTP, например.
GET,POST,PUT. - Отклонять все запросы, не соответствующие белому списку, с кодом ответа HTTP.
405 Method not allowed. - Убедитесь, что вызывающая сторона имеет право использовать входящий метод HTTP для сбора, действия и записи ресурсов.
В частности, в Java EE это может быть сложно реализовать должным образом. См. Обход веб-аутентификации и авторизации с помощью подделки HTTP-глаголов для объяснения этой распространенной неправильной конфигурации.
Предотвращение внеочередного выполнения API
Современные API REST часто реализуют бизнес-процессы через последовательность конечных точек (например, создать → проверить → утвердить → завершить). Если серверная часть явно не проверяет переходы состояний рабочего процесса, злоумышленники могут вызывать конечные точки вне последовательности, чтобы обойти предполагаемые элементы управления.
Проблема
Выполнение API вне очереди происходит, когда злоумышленник:
- Пропускает необходимые этапы рабочего процесса путем прямого вызова конечных точек более позднего этапа.
- Воспроизводит или повторно использует токены за пределами рабочего процесса.
- Использует предположения о том, что интерфейс обеспечивает правильную последовательность
Поскольку каждая конечная точка может проходить индивидуальную аутентификацию и авторизацию, традиционные проверки контроля доступа часто не позволяют обнаружить эти проблемы.
Пример
Рабочий процесс оформления заказа предполагает следующую последовательность:
POST /checkout/create
POST /checkout/pay
POST /checkout/confirmЕсли серверная часть не проверяет переходы состояний рабочего процесса, злоумышленник может напрямую вызвать:
POST /checkout/confirmбез завершения оплаты.
Руководство по профилактике
- Обеспечьте проверку состояния рабочего процесса на стороне сервера для каждого запроса.
- Моделируйте рабочие процессы явно, используя конечные состояния или конечные автоматы.
- Привязывайте токены или идентификаторы к конкретным этапам рабочего процесса.
- Не полагайтесь на логику внешнего интерфейса для обеспечения соблюдения последовательности.
- Отклоняйте недопустимые или неупорядоченные переходы с четкими ответами на ошибки.
Контрольный список тестирования
- Могут ли конечные точки вызываться вне последовательности?
- Каждая конечная точка проверяет текущее состояние рабочего процесса?
- Можно ли повторно использовать токены на этапах рабочего процесса?
- Постоянно ли отвергаются недопустимые переходы между состояниями?
Проверка ввода
- Не доверяйте входным параметрам/объектам.
- Подтвердите ввод: длину/диапазон/формат и тип.
- Обеспечьте неявную проверку входных данных, используя в параметрах API строгие типы, такие как числа, логические значения, даты, время или фиксированные диапазоны данных.
- Ограничьте ввод строк с помощью регулярных выражений.
- Отклонять неожиданный/незаконный контент.
- Используйте библиотеки проверки/санации или платформы на вашем конкретном языке.
- Определите соответствующий предел размера запроса и отклоняйте запросы, превышающие этот предел, со статусом ответа HTTP 413 Request Entity Too Large.
- Рассмотрите возможность регистрации ошибок проверки ввода. Предположим, что тот, кто выполняет сотни неудачных проверок ввода в секунду, не замышляет ничего хорошего.
- Подробное объяснение можно найти в статье по проверке ввода.
- Используйте безопасный парсер для анализа входящих сообщений. Если вы используете XML, обязательно используйте парсер, который не уязвим для XXE и подобные атаки.
Проверка типов контента
Тело запроса или ответа REST должно соответствовать предполагаемому типу контента в заголовке. В противном случае это может привести к неправильной интерпретации на стороне потребителя/производителя и привести к внедрению/выполнению кода.
- Документируйте все поддерживаемые типы контента в своем API.
Проверка типов контента запроса
- Отклонять запросы, содержащие неожиданные или отсутствующие заголовки типов контента со статусом ответа HTTP.
406 Unacceptableили415 Unsupported Media Type. Для запросов сContent-Length: 0однако,Content-typeзаголовок не является обязательным. - Для типов контента XML обеспечьте соответствующее усиление синтаксического анализатора XML, см.XXE статья.
- Избегайте случайного раскрытия непредусмотренных типов контента, явно определяя типы контента, например. Джерси (Ява)
@consumes("application/json"); @produces("application/json"). Это позволяет избежать XXE-атака векторы, например.
Отправлять типы контента безопасного ответа
Службы REST обычно допускают несколько типов ответов (например,application/xml или application/json, а клиент указывает предпочтительный порядок типов ответов в заголовке Accept в запросе.
- Не просто скопируйте
Acceptзаголовок кContent-typeзаголовок ответа. - Отклонить запрос (в идеале с помощью
406 Not Acceptableответ), еслиAcceptзаголовок конкретно не содержит ни одного из допустимых типов.
Службы, включающие в свои ответы код сценария (например, JavaScript), должны быть особенно осторожны, чтобы защититься от атаки с внедрением заголовка.
- Убедитесь, что в вашем ответе отправляются заголовки предполагаемого типа контента, соответствующие содержимому вашего тела, например.
application/jsonи неapplication/javascript.
Конечные точки управления
- Избегайте раскрытия конечных точек управления через Интернет.
- Если конечные точки управления должны быть доступны через Интернет, убедитесь, что пользователи должны использовать надежный механизм аутентификации, например многофакторный.
- Предоставляйте конечные точки управления через разные порты HTTP или хосты, желательно на другом сетевом адаптере и в ограниченной подсети.
- Ограничьте доступ к этим конечным точкам с помощью правил брандмауэра или с помощью списков управления доступом.
Обработка ошибок
- Отвечайте общими сообщениями об ошибках — избегайте ненужного раскрытия подробностей сбоя.
- Не передавайте клиенту технические детали (например, стеки вызовов или другие внутренние подсказки).
Журналы аудита
- Записывайте журналы аудита до и после событий, связанных с безопасностью.
- Рассмотрите возможность регистрации ошибок проверки токена для обнаружения атак.
- Позаботьтесь об атаках путем внедрения журнала, предварительно очистив данные журнала.
Заголовки безопасности
Существует ряд заголовки, связанные с безопасностью которые могут быть возвращены в ответах HTTP, чтобы указать браузерам действовать определенным образом. Однако некоторые из этих заголовков предназначены для использования с ответами HTML и, как таковые, могут обеспечить незначительные преимущества или вообще не обеспечить безопасность API, который не возвращает HTML. Обратите внимание: если API используется только клиентами, не являющимися браузерами (например, мобильными приложениями, межсерверными вызовами, инструментами командной строки), большинство этих заголовков не будут иметь никакого эффекта, поскольку они являются директивами для браузеров.
Следующие заголовки должны быть включены во все ответы API, которые могут использоваться клиентами браузера:
| Заголовок | Обоснование |
|---|---|
Cache-Control: no-store |
Заголовок, используемый для прямого кэширования браузерами. Предоставление no-store указывает, что любые кэши любого типа (частные или общие) не должны хранить ответ, содержащий заголовок. Браузер должен делать новый запрос каждый раз, когда вызывается API, чтобы получить последний ответ. Этот заголовок с no-store Значение предотвращает кэширование или сохранение конфиденциальной информации. |
Content-Security-Policy: frame-ancestors 'none' |
Заголовок, используемый для указания, может ли ответ быть оформлен в виде <frame>, <iframe>, <embed> или <object> элемент. Для ответа API не требуется, чтобы он был заключен в какой-либо из этих элементов. Предоставление frame-ancestors 'none' не позволяет любому домену формировать ответ, возвращаемый вызовом API. Этот заголовок защищает от перетаскивание кликджекинг-атаки в стиле. |
Content-Type |
Заголовок для указания типа содержимого ответа. Это должно быть указано в соответствии с типом контента, возвращаемого вызовом API. Если он не указан или указан неправильно, браузер может попытаться угадать тип содержимого ответа. Это может вернуться при атаках с перехватом MIME. Одним из распространенных значений типа контента является application/json если ответ API — JSON. |
Strict-Transport-Security |
Заголовок, сообщающий браузеру, что доступ к домену должен осуществляться только через HTTPS и что любые будущие попытки доступа к нему через HTTP должны автоматически конвертироваться в HTTPS. Этот заголовок гарантирует, что вызовы API выполняются через HTTPS, и защищает от поддельных сертификатов. |
X-Content-Type-Options: nosniff |
Заголовок, указывающий браузеру всегда использовать тип MIME, объявленный в Content-Type заголовок, а не пытаться определить тип MIME на основе содержимого файла. Этот заголовок с nosniff Значение не позволяет браузерам выполнять анализ MIME и некорректно интерпретировать ответы как HTML. |
X-Frame-Options: DENY |
Устаревший заголовок заменен на Content-Security-Policy: frame-ancestors 'none' (см. выше). По-прежнему рекомендуется для совместимости со старыми браузерами, не поддерживающими CSP Level 2.DENY не позволяет любому домену формировать ответ. |
Заголовки ниже предназначены только для обеспечения дополнительной безопасности, когда ответы отображаются в формате HTML. Таким образом, если API будет никогда возвращать HTML в ответах, то эти заголовки могут не понадобиться. Однако если есть какая-либо неопределенность в отношении функции заголовков или типов информации, которую API возвращает (или может вернуть в будущем), рекомендуется включить их в рамках подхода глубокоэшелонированной защиты.
| Заголовок | Пример | Обоснование |
|---|---|---|
| Политика безопасности контента | Content-Security-Policy: default-src 'none' |
Большая часть функций CSP влияет только на страницы, отображаемые в формате HTML. |
| Политика разрешений | Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), navigation-override=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=() |
Раньше этот заголовок назывался Feature-Policy. Когда браузеры учитывают этот заголовок, он используется для управления функциями браузера с помощью директив. В примере отключаются функции с пустым списком разрешенных для ряда разрешенных имена директив. Применяя этот заголовок, убедитесь, что директивы актуальны и соответствуют вашим потребностям. Пожалуйста, взгляните на это статья для подробного объяснения того, как управлять функциями браузера. |
| Политика рефереров | Referrer-Policy: no-referrer |
Ответы, отличные от HTML, не должны вызывать дополнительные запросы. |
КОРС
Совместное использование ресурсов между источниками (CORS) — это стандарт W3C, позволяющий гибко указывать, какие междоменные запросы разрешены. Доставляя соответствующие заголовки CORS, ваш REST API сигнализирует браузеру, каким доменам, источникам AKA, разрешено выполнять вызовы JavaScript к службе REST.
- Отключите заголовки CORS, если междоменные вызовы не поддерживаются/не ожидаются.
- Будьте как можно более конкретными и настолько общими, насколько это необходимо, при настройке источников междоменных вызовов.
Конфиденциальная информация в HTTP-запросах
Веб-службы RESTful должны быть осторожны, чтобы предотвратить утечку учетных данных. Пароли, токены безопасности и ключи API не должны появляться в URL-адресе, поскольку они могут быть зафиксированы в журналах веб-сервера, что делает их чрезвычайно ценными.
- В
POST/PUTКонфиденциальные данные запросов должны передаваться в теле запроса или заголовках запроса. - В
GETконфиденциальные данные запросов должны передаваться в HTTP-заголовке.
ХОРОШО:
https://example.com/resourceCollection/[ID]/action
https://twitter.com/vanderaj/lists
НЕ ОК:
https://example.com/controller/123/action?apiKey=a53f435643de32 потому что apiKey находится в URL-адресе.
HTTP-код возврата
HTTP определяет код состояния. При разработке REST API не просто используйте 200 для успеха или 404 за ошибку. Всегда используйте для ответа семантически подходящий код состояния.
Вот неисчерпывающий выбор REST API, связанного с безопасностью. коды состояния. Используйте его, чтобы убедиться, что вы возвращаете правильный код.
| Код | Сообщение | Описание |
|---|---|---|
| 200 | ХОРОШО | Ответ на успешное действие REST API. Метод HTTP может быть GET, POST, PUT, PATCH или DELETE. |
| 201 | Созданный | Запрос выполнен и ресурс создан. URI для созданного ресурса возвращается в заголовке Location. |
| 202 | Принял | Запрос принят в обработку, но обработка еще не завершена. |
| 301 | Переехал навсегда | Постоянное перенаправление. |
| 304 | Не изменено | Ответ, связанный с кэшированием, который возвращается, когда клиент имеет ту же копию ресурса, что и сервер. |
| 307 | Временное перенаправление | Временное перенаправление ресурса. |
| 400 | Неверный запрос | Запрос имеет неправильный формат, например ошибка формата тела сообщения. |
| 401 | Несанкционированный | Указан неверный идентификатор аутентификации/пароль или он не указан. |
| 403 | Запрещенный | Он используется, когда аутентификация прошла успешно, но аутентифицированный пользователь не имеет разрешения на доступ к ресурсу запроса. |
| 404 | Не найдено | Когда запрашивается несуществующий ресурс. |
| 405 | Метод неприемлем | Ошибка неожиданного метода HTTP. Например, REST API ожидает HTTP GET, но используется HTTP PUT. |
| 406 | неприемлемо | Клиент представил в заголовке Accept тип контента, который не поддерживается API сервера. |
| 413 | Полезная нагрузка слишком велика | Используйте его, чтобы сигнализировать о том, что размер запроса превысил заданный предел, например. относительно загрузки файлов. |
| 415 | Неподдерживаемый тип носителя | Запрошенный тип контента не поддерживается службой REST. |
| 429 | Слишком много запросов | Ошибка используется, когда может быть обнаружена DOS-атака или запрос отклонен из-за ограничения скорости. |
| 500 | Внутренняя ошибка сервера | Непредвиденное условие помешало серверу выполнить запрос. Имейте в виду, что ответ не должен раскрывать внутреннюю информацию, которая поможет злоумышленнику, например. подробные сообщения об ошибках или трассировки стека. |
| 501 | Не реализовано | Служба REST еще не реализует запрошенную операцию. |
| 503 | Сервис недоступен | Служба REST временно не может обработать запрос. Используется для информирования клиента о необходимости повторить попытку позже. |
Дополнительную информацию об использовании кода возврата HTTP в REST API можно найти. здесь и здесь.
© Перевод на русский язык. Оригинал: OWASP REST Security Cheat Sheet и серия OWASP Cheat Sheet Series.
Этот проект использует материалы OWASP, распространяемые по лицензии CC BY-SA 4.0.