Есть API, которые в целом «как-то работают», но имеют проблемы с безопасностью, документацией или валидацией данных. Автор статьи объясняет, почему в современных реалиях это недопустимо, и даёт рекомендации по исправлению недостатков.

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

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

А если от публичного API напрямую зависит доход компании, то ставки по-настоящему высоки. Эта мысль подробнее раскрыта в книге «Непрерывное развитие API. Правильные решения в изменчивом технологическом ландшафте» (Мехди Меджуи, Ронни Митра и др):


а мы пойдём дальше... 

Разработчики могут быть в основном сосредоточены на том, чтобы в API была реализована вся запланированная функциональность. Но существуют «нефункциональные» требования, которые часто выполнены не в полной мере и требуют особого внимания.  

На мой взгляд, для всех API обязательно качественное выполнение следующих нефункциональных требований:

  • Безопасность
  • Документация
  • Валидация
  • Тестирование

Безопасность


Наверное, не стоит объяснять, почему безопасность оказалась на первом месте. Я выделил четыре наиболее важных задачи по обеспечению безопасности:

  1. Использование HTTPS с SSL-сертификатами
  2. Совместное использование ресурсов из разных источников (CORS)
  3. Аутентификация и JSON Web Tokens
  4. Авторизация и привилегии доступа


* Здесь и далее сузим контекст до REST и JSON API.

1. Использование HTTPS с SSL-сертификатами


Сейчас HTTPS с использованием SSL-сертификатов является де факто стандартом безопасности. Для генерации сертификатов лично я использую Let's Encrypt — бесплатный, центр автоматизированный сертификации от некоммерческой организации Internet Security Research Group (ISRG).

Эти сертификаты гарантируют, что данные, идущие от вашего API к пользователю, будут зашифрованы.

2. Совместное использование ресурсов из разных источников (CORS)


Чтобы обеспечить безопасность запросов к другим источникам, браузеры используют механизм под названием CORS. Аббревиатура CORS расшифровывается как Cross-Origin Resource Sharing — технология совместного использования ресурсов разных источников. Несмотря на то, что браузеры (в силу правила одинакового источника) не позволяют получать доступ к ресурсам из разных источников, CORS позволяет обойти эти ограничения и при этом быть уверенным, что доступ к ресурсам будет безопасным.

Пользовательские агенты (к примеру, браузеры) на основе значений дополнительных заголовков для CORS в HTTP-запросе могут выполнять запросы к другим источникам, которые без CORS были бы заблокированы.



Приведём пример:

HTML страница, обслуживаемая сервером с http://domain-a.com, запрашивает src  по адресу http://domain-b.com/image.jpg

Многие страницы загружают ресурсы вроде CSS-стилей, изображений и скриптов с разных доменов, соответствующих разным CDN (сетям доставки контента).

Сегодня достаточно популярна реализация CORS для Node.js.

3. Аутентификация и JSON Web Tokens (JWT)


Существует несколько подходов к аутентификации пользователя API, но один из лучших — это использование JWT. Эти токены подписываются с использованием различных криптографических алгоритмов.

JSON Web Token — это открытый стандарт для создания токенов доступа, основанный на формате JSON. Как правило, используется для передачи данных для аутентификации в клиент-серверных приложениях.

Для создания токена необходимо определить заголовок (header) с общей информацией по токену, полезные данные (payload), такие как id пользователя, его роль и так далее, а также подписи (signature). Простыми словами, JWT — это лишь строка в следующем формате header.payload.signature.

Когда клиент входит в систему, служба управления идентификацией предоставляет клиенту JWT. Затем клиент может использовать этот токен для выполнения запросов к API. У API есть доступ к публичному ключу или секрету, который он использует для проверки токена.

Для проверки можно использовать, например, библиотеку jsonwebtoken. Приводим ниже код на JavaScript:

import jwt from 'jsonwebtoken'

export default function (req, res, next) {

    // req.headers.authorization Bearer token

    const token = extractToken(req)

    jwt.verify(token, SECRET, { algorithms: ['HS256'] }, (err, decoded) => {

        if (err) { next(err) }

        req.session = decoded

        next()

    })

}

Больше информации про JWT, библиотеки и поддерживаемые языки программирования — на сайте JWT.io

4. Авторизация и привилегии доступа


Аутентификация важна, но так же важна и авторизация: имеет ли клиент, прошедший аутентификацию и получивший JWT, привилегию выполнить тот или иной запрос?

Чтобы проверить это, мы используем скоуп. Анализируя его, сервис API определяет, может ли этот запрос клиента быть выполнен без дорогостоящих операций поиска в списке контроля доступа.

Скоуп — это текстовый блок (обычно разделённый пробелами), который описывает привилегии доступа конечной точки API. Там описаны Ресурсы и Действия, которые к ним можно применять. Такая формализация хорошо работает для REST / JSON API, поскольку они очень похожи по структуре.

RESOURCE: ACTION (например, ARTICLE: WRITE или ARTICLE: READ, где ARTICLE — это ресурс, а READ и WRITE — действия).

Это позволяет разработчикам API сосредоточиться на функциях, а не на ролях или пользователях. Служба управления идентификацией может связывать роли и пользователей с определёнными скоупами, а затем вместе с JWT отправлять скоуп клиенту.

Спать спокойно


При разработке и развёртывании API безопасность всегда должна быть одним из наиболее важных требований. Конечно, о ней можно говорить и писать бесконечно, но грамотная реализация четырёх описанных задач на продакшене уже позволит вам спать спокойно.

Документация


Что может быть хуже отсутствия документации? Устаревшая документация!

Разработчики относятся к документированию неоднозначно. У многих явно нет большой любви к этому делу. Как бы то ни было, эта задача очень важна — особенно, когда речь идёт про публичное API. Сторонние разработчики должны иметь возможность изучить, как его использовать. Кроме того, хорошая документация поможет вам быстрее обучать новых разработчиков, пришедших в вашу команду.

Документация по API должна включать три базовых раздела:

  1. Вводная часть (README)
  2. Техническое описание (Спецификации)
  3. Примеры использования (Getting started и другие подобные подразделы)

1. README


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

Об этом пишут в файле README. Этот файл обычно тоже размещают в репозитории, он даёт разработчикам отправную точку для работы с вашим проектом.

README должен содержать:

  1. Описание API
  2. Ссылки на технические справочники и руководства
  3. Руководство по настройке для разработчика
  4. Руководство для тестировщика
  5. Руководство по развёртыванию
  6. Управление зависимостями
  7. Руководство для контрибьюторов
  8. Нормы поведения
  9. Лицензия
  10. Благодарность

Будьте лаконичны в своем README; вам не нужно объяснять все нюансы, но предоставить достаточно информации, чтобы разработчики могли погрузиться в ваш проект.

2. Спецификации


В REST / JSON API каждая конечная точка представляет собой функцию, созданную с определённой целью. Важно иметь техническую документацию, описывающую каждую конечную точку, входные и выходные данные и особенности работы сервиса с разными клиентами.

Создать собственную документацию для API можно на основе OpenAPI

Это спецификация и полноценный фреймворк для описания, создания, использования и визуализации веб-сервисовREST. Его задача — позволить системам документации синхронизировать свои обновления с изменениями на сервере. Методы, параметры, модели и другие элементы посредством OpenAPI интегрируются с программным обеспечением сервера и всё время с ним синхронизируются.

3. Примеры использования


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

Отличный способ познакомить пользователей с вашим API — создать подраздел Getting started («Начало работы»). Это поможет разобраться с типовыми сценариями использования и на их примере оценить преимущества вашего API.

Три кита


Документация — ключевой компонент любого API. Создавая документацию, помните о трёх китах успешного документирования API — README, спецификации и примеры использования. И будет вам (и вашим пользователям) счастье!

Валидация данных


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

Лучшая стратегия — запускать проверку до того, как ваш сервис произведёт какие-то операции с данными. Когда клиент отправляет вашему API свои данные, например email, дату и имя, убедитесь, что это действительно адрес электронной почты, дата правильно отформатирована, а строка соответствует требованиям к длине. Эта простая проверка сделает работу вашего сервиса более безопасной и организованной. 

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

Вы можете реализовать валидацию руками, но для этой цели также можно использовать и такие библиотеки, как Lodash или Ramda. Они отлично подходят для небольших объектов данных. Такие библиотеки, как Joi, Yup или  Zod, работают ещё лучше, поскольку они позволяют описать общую схему валидации, сэкономив время и усилия. Если вам нужно что-то не зависящее от конкретного языка программирования, посмотрите на JSON Schema.

Лучше перебдеть


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

Найдите время и организуйте валидацию для своих входных данных. Как говорится, лучше перебдеть, чем недобдеть.

Тестирование


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

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

Этот метод позволяет вам сосредоточиться только на логике обработки данных, не учитывая внутреннюю кухню бэкенд-сервисов или логику представления данных. Отсутствие зависимостей делает выполнение теста более надёжным, легким для автоматизации и более простым для включения в пайплайн непрерывной интеграции.

Для интеграционного тестирования я использую Tape, Test-server и Fetch-mock. Эти библиотеки позволяют запускать изолированные тесты для конечных точек API, проходя путь от запроса до ответа.

Игра в имитацию


Другие типы тестирования и проверки типов, безусловно, тоже полезны, интеграционное тестирование даёт наибольшее преимущество: оно показывает высокую эффективность при небольших тратах человеко-часов на написание и сопровождение тестов. А использование таких инструментов, как Fetch-mock, может обеспечить полную имитацию обмена данными.

Не останавливайтесь на достигнутом


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



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!