Лучшие практики API-авторизации

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

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

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

Именно здесь на помощь приходит авторизация API.

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


Что такое авторизация API

Прежде чем перейти к лучшим практикам авторизации API, уточним разницу между авторизацией и аутентификацией. Эти понятия часто путают.

  • Аутентификация  —  это проверка личности. Речь идет о подтверждении личности пользователя с помощью имени пользователя, пароля или биометрических данных.
  • Авторизация  —  это предоставление доступа к ресурсам или функциональным возможностям после подтверждения личности. Фигурально выражаясь, можно определить аутентификацию как доступ ко входной двери, а авторизацию  —  как доступ к помещениям и службам внутри здания.

Лучшие практики авторизации API

1. Авторизации на основе токенов (JWT, OAuth)

Авторизация на основе токенов, особенно с использованием токенов JWT (JSON Web) и OAuth, предлагает безопасный и эффективный способ управления взаимодействием с API. Это выражается в следующем.

  • Токены обеспечивают аутентификацию без состояния, то есть серверу не нужно сохранять состояние сессии для каждого пользователя, что обеспечивает более высокий уровень масштабируемости.
  • Токены могут безопасно передаваться различными способами и менее подвержены атакам CSRF, чем традиционная аутентификация на основе сессий.
  • Токены можно использовать в разных доменах, что делает их идеальным решением для архитектуры микросервисов и приложений единого входа (SSO).
  • Токены, особенно OAuth, могут включать области видимости и разрешения, обеспечивая точный контроль над действиями аутентифицированного пользователя.

Как реализовать авторизацию на основе токенов с использованием JWT

Токены JWT обычно генерируются после успешной аутентификации пользователя. Они содержат полезную нагрузку с информацией о пользователе и, возможно, его разрешениями. Для реализации JWT-авторизации можно использовать такие библиотеки, как jsonwebtoken в Node.js и PyJWT в Python.

Сначала нужно сгенерировать JWT-токен. Вот как это сделать с помощью PyJWT:

import jwt
from datetime import datetime, timedelta

secret_key = 'YOUR_SECRET_KEY'
payload = {
'sub': user_id,
'iat': datetime.utcnow(),
'exp': datetime.utcnow() + timedelta(days=1)
}

token = jwt.encode(payload, secret_key, algorithm='HS256')

После этого следует проверить каждый запрос, чтобы выяснить, разрешено ли пользователю выполнять запрос. Токен обычно отправляется в заголовок Authorization каждого запроса. Сервер может расшифровать JWT с помощью секретного ключа и проверить его. Если он валиден, сервер обрабатывает запрос, если нет  —  возвращает ошибку.

from flask import Flask, request, jsonify
import jwt
import datetime

app = Flask(name)
SECRET_KEY = "your_secret_key" # Замените на свой секретный ключ

Sample route that requires token-based authorization
@app.route('/protected', methods=['GET'])
def protected():
token = request.headers.get('Authorization')
if not token:
return jsonify({'message': 'Token is missing!'}), 403

try:
# Дешифровка токена
data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
# Можете использовать данные в токене по своему усмотрению, например:
# user_id = data['user_id']

except jwt.ExpiredSignatureError:
return jsonify({'message': 'Token has expired!'}), 403
except jwt.InvalidTokenError:
return jsonify({'message': 'Invalid token!'}), 403

# Токен валиден, обработка запроса
return jsonify({'message': 'Token is valid! You have access to protected routes.'})

2. Тонкий (детальный) контроль доступа

Тонкий контроль доступа (fine-grained access control)  —  это подход к управлению безопасностью, который обеспечивает детальный контроль над разрешениями и правами доступа приложения. Он обеспечивает пользователям и службам только необходимый им доступ, придерживаясь принципа минимума полномочий.

Как реализовать тонкий контроль доступа

Реализация тонкого контроля доступа включает несколько этапов.

  • Определение ролей и разрешений. Определите различные роли пользователей в системе и конкретные действия для каждой роли.
  • Использование контроля доступа на основе ролей (RBAC). Внедрите RBAC, при котором доступ к ресурсам предоставляется на основе ролей. Каждой роли назначаются определенные разрешения.
  • Рассмотрение возможности контроля доступа на основе атрибутов (ABAC). Для более сложных сценариев можно использовать ABAC, когда решения о доступе принимаются на основе комбинации атрибутов (пользователь, ресурс, среда).

Допустим, у вас есть API, доступ к которому различается для администратора и пользователя. Механизмы управления доступом можно реализовать с помощью независимых компонентов, повторно используемых во всех приложениях.

Можете использовать такие инструменты, как Bit, для создания независимого компонента авторизатора, который будет отвечать за авторизацию запросов к API.

Вот все, что вам нужно сделать.

1. Создайте компонент Node.js под названием “authorizer”, который позволит реализовать логику авторизации, с помощью команды:

bit create node util/authorizer

Если все сделаете правильно, увидите результат:

2. Реализуйте логику авторизатора:

export function authorizer(userId: string) {
// Реализуйте логику для получения роли пользователя
if (userId === 'U001') {
return "admin";
}
return "customer"
}

3. Создайте приложение Express App с помощью команды:

bit create express-app api

Увидите результат:

4. Подключите авторизатор к приложению, обновив файл mock-route.ts. Изначально увидите следующее:

5. Добавьте новую промежуточную программу:

Это обеспечит запуск компонента авторизатора до вызова бизнес-логики. После запуска API увидите следующий результат:

С помощью цепочки функций обеспечен контроль доступа на основе ролей. Декоратор проверяет, соответствует ли роль пользователя требуемым ролям для конечной точки. Если нет  —  возвращает сообщение об отказе в доступе.

По мере обновления логики авторизатора и приложения сервер CI Bit  —  Ripple CI  —  автоматически обновляет изменения во всем дереве.

С полной реализацией можно ознакомиться в Bit Scope.

3. Конфигурация безопасного API-шлюза

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

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

Как реализовать конфигурацию безопасного API-шлюза

Существует множество провайдеров API-шлюзов для приложений. Самые популярные из них  —  Amazon API Gateway, Kong и Apigee от Google.

Планируя использовать AWS (Amazon Web Services), следуйте шагам, описанным в официальной документации, чтобы легко создать API-шлюз. Однако есть несколько дополнительных моментов, которые необходимо учесть, чтобы обеспечить безопасность и эффективность API-шлюза.

  • Настройка ограничения скорости. В консоли управления AWS перейдите к Amazon API Gateway, выберите свой API и перейдите в раздел Throttling (Дросселирование) во вкладке Protect (Защита). Там установите ограничения скорости обработки запросов и скорости передачи данных.
  • Включение SSL/TLS. Убедитесь, что ваше пользовательское доменное имя для шлюза API совместимо с сертификатом SSL/TLS в AWS Certificate Manager.
  • Реализация ограничения по IP-адресам. Используйте авторизаторы AWS Lambda для проверки IP-адресов входящих запросов. После развертывания функции можете выбрать ее, создав новый авторизатор для шлюза API во вкладке Authorization (Авторизация). Вот пример функции Lambda на языке Python для ограничения IP-адресов:
import json

def lambda_handler(event, context):
ip_address = event['requestContext']['identity']['sourceIp']
allowed_ips = ['192.168.1.1'] # Список допсутимых IP
# Добавьте сюда логику, чтобы проверить, находится ли ip_address в списке allowed_ips или нет.

if ip_address not in allowed_ips:
raise Exception('Unauthorized')

return {
'principalId': 'user',
'policyDocument': {
'Version': '2012-10-17',
'Statement': [{
'Action': 'execute-api:Invoke',
'Effect': 'Allow',
'Resource': event['methodArn']
}]
}
}
  • Включение CloudWatch Logs. В консоли управления AWS перейдите к настройкам API-шлюза. В разделе CloudWatch Settings (Настройки CloudWatch) отметьте опцию Enable CloudWatch Logs (Подключить логирование CloudWatch). Установите уровень логирования на INFO для записи всех запросов и ответов или ERROR для записи только ответов на ошибки.

4. Шифрование конфиденциальных данных при передаче и хранении

Шифрование конфиденциальных данных при передаче (in transit) и хранении (at rest)  —  один из основных методов обеспечения безопасности, позволяющий защитить данные от несанкционированного доступа и взлома. Шифрование при передаче обеспечивает защиту данных при их перемещении между клиентами и серверами, а шифрование при хранении защищает данные, хранящиеся на диске или в базе данных.

Как реализовать шифрование при передаче данных

При использовании собственного веб-сервера придется вручную настраивать и управлять сертификатами SSL/TLS. Прежде всего, получите сертификаты в удостоверяющем центре (Certificate Authority, CA), таком как Let’s Encrypt, DigiCert и GoDaddy. Затем настройте сервер на взаимодействие с этим центром. Вот как можно настроить SSL с помощью Nginx.

  • Измените конфигурационный файл Nginx (обычно он находится по адресу /etc/nginx/nginx.conf или /etc/nginx/sites-available/your_site).
  • Добавьте настройки SSL в блок сервера:
server {
listen 443 ssl;
server_name yourdomain.com;

ssl_certificate /path/to/your/fullchain.pem; # Путь к fullchain.pem из CA
ssl_certificate_key /path/to/your/privkey.pem; # Путь к приватному ключу из CA
# Сильные настройки шифрования (используйте рекомендации генератора настроек SSL от Mozilla)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256...';

# Другие конфигурации сервера...
}

При использовании облачных сервисов в вашем распоряжении будут полностью управляемые службы для работы с SSL/TLS. Можете, например, использовать AWS Certificate Manager для служб, размещенных на AWS.

Как реализовать шифрование при хранении данных

Шифрование при хранении данных можно реализовать на уровне базы данных, на уровне приложения или с помощью облачных инструментов.

1. Шифрование на уровне базы данных. Многие современные базы данных предлагают встроенные функции шифрования. 

  • SQL Server: прозрачное шифрование данных.
  • MySQL: шифрование табличного пространства InnoDB.
  • MongoDB: механизм хранения Encrypted Storage Engine.

2. Шифрование на уровне приложений. Шифрование на уровне приложения следует использовать для высокочувствительных данных, чтобы зашифровать их еще до хранения в базе данных. Для этого можно использовать библиотеки типа cryptography:

from cryptography.fernet import Fernet

# Генерация ключа
key = Fernet.generate_key()
cipher_suite = Fernet(key)

# Шифрование данных
encrypted_data = cipher_suite.encrypt(b"Sensitive Data")
# Расшифровка данных
decrypted_data = cipher_suite.decrypt(encrypted_data)

Заключение

Надежная API- авторизация имеет решающее значение для защиты цифровых активов и поддержания доверия пользователей.

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

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Chameera Dulanga: Best-Practices for API Authorization

Предыдущая статьяРазработка масштабируемых фронтендов с помощью Feature-Sliced Design
Следующая статьяПрогнозирование настроений на фондовом рынке с помощью OpenAI и Python