31 мая 2024

Блог

Информационная безопасность

Безопасность API в веб-сервисах

В данной статье будут рассмотрены несколько общих кейсов проблем безопасности API, которые могут встретиться так или иначе практически в любой клиент-серверной системе, к каждой проблеме будут сформированы эмпирические правила, выработанные на наших реальных проектах, которыми мы активно пользуемся.

В данной статье будут рассмотрены несколько общих кейсов проблем безопасности API, которые могут встретиться так или иначе практически в любой клиент-серверной системе, к каждой проблеме будут сформированы эмпирические правила, выработанные на наших реальных проектах, которыми мы активно пользуемся.

Основное, с чем работает серверная часть, – это информация, то есть данные. Данными могут выступать различные сущности бизнес-системы, такие как пользователи, покупки, документы, заявки и другие. Однако если говорить о доступе до этих данных, то здесь требуется ввод некоторых ограничений: например, пользователь вряд ли захочет, чтобы доступ до его данных получили другие пользователи. Первый шаг в обеспечении безопасности API – это введение идентификации и аутентификации в системе, второй – это авторизация пользователя, то есть определение его роли в системе и его полномочий. И если для современных веб-систем в случае идентификации и аутентификации выработался стандарт – авторизация на основе токенов доступа, то реализация правильной авторизации – это полностью ответственность разработчиков системы.
Токен доступа позволяет понять, какой пользователь выполняет запрос к системе, но не решает других проблем безопасности API. В связи с этим возникает потребность рассмотреть некоторые другие методы, которые призваны обеспечить безопасность ваших данных и системы в целом. Далее будут рассмотрены следующие методы:
1.    Проверка прав доступа субъекта к объекту
2.    Обеспечение сложности перебора идентификаторов
3.    Подмена реальных чувствительных идентификаторов
4.    Защита от брутфорса
5.    Валидация входных параметров


Проверка прав доступа субъекта к объекту


Представим себе реальный случай, когда API вашей серверной части принимает в запросе номер страхового полиса и в ответе отдает документ этого полиса в формате PDF. Страховой полис выступает объектом в системе, который принадлежит некоторому пользователю – владельцу данного полиса. Захотите ли вы как владелец страхового полиса, чтобы у других пользователей была возможность напрямую получить ваш личный документ, передав в запрос номер вашего полиса (опуская то, каким образом они его получили)? Большинство ответит «Нет», поэтому необходимо ограничить доступ путем проверки прав доступа пользователя, выполняющего запрос, до сущности системы.


Исходя из сказанного, можно сформулировать правило: если объект принадлежит логически субъекту A и доступ до объекта идет по некоторому идентификатору субъектом B, то необходима проверка, совпадает ли субъект B c субъектом A.


Обеспечение сложности перебора идентификаторов


Зачастую идентификаторами объекта в информационной системе выступает некоторое число, менее часто – некоторый уникальный идентификатор большой разрядности (UUID). В случае UUID сложность перебора идентификаторов, то есть процесса выполнения большого количества запросов к системе со случайными значениями с целью сбора информации, становится очень высокой, но совсем другое дело, если идентификаторами выступают числа. С учетом применения первого правила возможна следующая ситуация: пользователь выполняет запрос на получение объекта по числовому идентификатору, система загружает весь объект из базы данных в оперативную память, проверяет, что пользователь по каким-то причинам не должен иметь доступ до объекта, и возвращает ответ с ошибкой. Потенциальная проблема в такой системе – это возможность создать излишнюю нагрузку на базу данных и сервер, который будет получать много запросов с разными идентификаторами, почти на все запросы будет отвечать ошибкой. Поэтому хорошей практикой будет использование случайно сгенерированных UUID или любых других уникальных, сложно подбираемых признаков вместо целых чисел для задания идентификаторов объектам.


Подмена реальных чувствительных идентификаторов

Допустим, что ваша система использует файловые хранилища (например, S3) для сохранения ресурсов пользователей (документов, изображении и других). Доступ до ресурса в таких хранилищах идет по некоторому ключу.  Ваша система может отдавать пользователям ресурс, фактически передавая ключ ресурса на клиентскую часть, которая далее сделает запрос в хранилище напрямую и получит ресурс. Проблема такого решения заключается в том, что, заполучив ключ к ресурсу и адрес хранилища, пользователь (им может быть не только вы как честный пользователь, но и злоумышленник) сможет без ограничений выполнять запросы и получать данные из него. 
Решением данной проблемы может быть ограничение прямого доступа по ключу в хранилище. Как показал наш опыт, рабочим и достаточно быстрым в реализации методом оказалась подмена ключей (или идентификаторов) ресурсов. В таком решении пользователь видит только подменный ключ, соответствие же реального и подменного ключа хранится на серверной части. Сервер, получая подменный ключ, самостоятельно определяет реальный ключ и отдает ресурс клиенту. Такой подход позволил ограничить бесконтрольный доступ до хранилища клиентам, а также создать единую точку входа для получения ресурсов с полным контролем бизнес-логики.

Защита от брутфорса

Брутфорс, или атака полным перебором – это популярный способ злоумышленников получить доступ до данных в системе. Помимо перебора логинов и паролей для взлома аккаунтов других пользователей, понятие брутфорс можно переложить и на случай, когда злоумышленник специально делает большое количество запросов к конечной точке с большим объемом работы, чтобы вывести сервер из строя, перебирая одни типы данных для получения других.
Помимо обеспечения сложности перебора идентификаторов, в нашей практике мы используем еще один метод – это ограничение доступа к определенному ресурсу с помощью лимитов на количество попыток (запросов). Каждый факт обращения к ресурсу можно зафиксировать в базе данных, записав кортеж из идентификатора пользователя, выполняющего запрос, ключ ресурса и время доступа. Тогда при следующем запросе можно установить, сколько раз за последние Х минут данный пользователь запросил ресурс. И если таких попыток было больше, чем N, за последние Х минут, то доступ для него будет ограничен.

Валидация входных параметров

Валидация входных параметров, то есть проверка данных различных типов по критериям корректности значений, является одним из ключевых способов обеспечения безопасности API. В случае отсутствия достаточной валидации входных параметров возникает потенциально две проблемы:
1.    Сервер мог начать обрабатывать запрос с невалидными данными, потратить свои вычислительные ресурсы, за которые бизнес-заказчик платит деньги, и вернуться с ошибкой. В таком случае можно сказать о трате вычислительных ресурсов «вхолостую»;
2.    Сервер мог обработать запрос, сформировав невалидные данные внутри самой системы. 
В реальности оказывается, что вторая проблема на самом деле куда дороже первой. Если в систему попали невалидные данные и она начала далее с ними работать, то рано или поздно, наткнувшись на их невалидность, будет гораздо сложнее, во-первых, установить причину попадания невалидных данных в систему, и, во-вторых, сделать данные валидными.
Исходя из этого можно сформулировать рекомендацию – ваша серверная часть никогда не должна доверять входным данным от клиента, даже если клиент вам обещает валидные данные. Бизнес-логика на стороне серверной части в идеале уже должна работать с валидными данными и не должна делать самостоятельно каких-либо предположений.
Во многих языках программирования уже разработаны средства для обеспечения проверки входных запросов на валидность (например, пакет Spring Validation для языка Java). Более того, они же зачастую предоставляют возможность разработчикам написать свои собственные проверки запросов так, что эта проверка будет уникальной для вашей системы.

В рамках статьи были рассмотрены лишь некоторые проблемы безопасности API и способы их устранения. В мире программирования, особенно его части, связанной с веб-сервисами и Интернетом, проблем безопасности гораздо больше. К сожалению, не существует единого решения для всех таких проблем, поэтому с проблемами безопасности необходимо бороться комплексно, применяя различные выработанные практики. 

Валитов Андрей

Андрей Валитов

тимлид