Сегодня мы будем практиковаться, а именно — настраивать безопасность API, используя сразу несколько изученных инструментов.
Постановка задачи
В рамках нашего учебного приложения нам нужно:
- Защитить эндпоинты API таким образом, чтобы доступ к ним был ограничен в зависимости от роли пользователя.
- Аутентифицированные пользователи с ролью "пользователь" смогут читать свои данные.
- Администраторы смогут выполнять любые операции.
- Анонимные пользователи смогут обращаться только к публичным данным.
Настроить CORS, чтобы API мог взаимодействовать с внешним фронтендом.
Включить HTTPS для обеспечения безопасного обмена данными.
Добавить мониторинг событий безопасности с помощью Django Signals:
- Логировать успешные авторизации.
- Реагировать на подозрительные действия, такие как попытки входа с неправильными паролями.
Реализация ролевой модели
Начнем с настройки доступа на основе ролей. В нашем приложении уже должна быть модель пользователей, но сейчас мы добавим поле для хранения роли пользователя.
Шаг 1: дополнение модели пользователя
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
ROLE_CHOICES = (
('user', 'User'),
('admin', 'Admin'),
)
role = models.CharField(max_length=10, choices=ROLE_CHOICES, default='user')
def is_admin(self):
return self.role == 'admin'
def is_user(self):
return self.role == 'user'
мы добавили поле role с выбором ролей. Теперь у нас есть пользователи с ролями "user" и "admin".
Шаг 2: создание кастомного разрешения
Теперь создадим разрешение, которое позволяет доступ только пользователям с определенной ролью.
from rest_framework.permissions import BasePermission
class IsAdminUserOrReadOnly(BasePermission):
"""
Разрешение для администраторов или только для чтения.
"""
def has_permission(self, request, view):
# Проверяем метод запроса. Если это безопасный (GET/HEAD/OPTIONS), доступ открыт.
if request.method in ['GET', 'HEAD', 'OPTIONS']:
return True
# Если метод небезопасный, проверяем, что пользователь администратор.
return request.user.is_authenticated and request.user.role == 'admin'
Шаг 3: применение разрешения к представлениям
Теперь добавим разрешение к эндпоинтам в нашем API.
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .permissions import IsAdminUserOrReadOnly
class UserDataView(APIView):
"""
Эндпоинт для получения данных пользователя.
"""
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({"message": "Привет, пользователь!"})
class AdminDataView(APIView):
"""
Административный эндпоинт.
"""
permission_classes = [IsAdminUserOrReadOnly]
def post(self, request):
return Response({"message": "Привет, админ!"})
Настройка CORS
Прежде чем фронтенд сможет обращаться к нашему API, нужно настроить CORS. Для этого воспользуемся библиотекой django-cors-headers.
Шаг 1: установка библиотеки
Установите пакет с помощью pip:
pip install django-cors-headers
Шаг 2: настройка CORS
Добавьте библиотеку в настройки Django:
INSTALLED_APPS += ['corsheaders']
MIDDLEWARE.insert(0, 'corsheaders.middleware.CorsMiddleware')
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000", # Домены, которым разрешен доступ
"https://example.com",
]
CORS_ALLOW_METHODS = [
"GET",
"POST",
"PUT",
"DELETE",
"OPTIONS",
]
Не забудьте указать допустимые источники (домен или localhost) в CORS_ALLOWED_ORIGINS.
Включение HTTPS
Для локальной разработки можно использовать самоподписанный сертификат. В продакшене рекомендуется использовать Let's Encrypt или другой надежный сервис.
Шаг 1: генерация сертификатов для разработки
Сгенерируйте самоподписанный сертификат:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
Добавьте SSL-сертификат в настройки проекта. В settings.py:
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
# Для разработки
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Запускайте сервер с указанием сертификатов:
python manage.py runserver_plus --cert-file cert.pem
Для использования HTTPS в продакшене ознакомьтесь с документацией Django.
Мониторинг событий через Django Signals
Мы будем логировать успешные авторизации и отправлять уведомления об ошибочных попытках входа.
Шаг 1: настройка сигналов
Создайте файл signals.py в вашем приложении.
from django.contrib.auth.signals import user_logged_in, user_login_failed
from django.dispatch import receiver
import logging
logger = logging.getLogger(__name__)
@receiver(user_logged_in)
def log_successful_login(sender, request, user, **kwargs):
logger.info(f"Успешный вход: {user.username} с IP {get_client_ip(request)}")
@receiver(user_login_failed)
def log_failed_login(sender, credentials, request, **kwargs):
logger.warning(f"Неудачная попытка входа для {credentials.get('username')} с IP {get_client_ip(request)}")
def get_client_ip(request):
"""Получение IP-адреса клиента."""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0]
return request.META.get('REMOTE_ADDR')
Шаг 2: подключение сигналов
В файле apps.py подключите файл сигналов.
class MyAppConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'
def ready(self):
import myapp.signals
Тестирование безопасности
Теперь, когда все настроено, выполните интеграционные тесты:
- Убедитесь, что эндпоинты API корректно ограничивают доступ.
- Проверьте возможности CORS с фронтендом.
- Протестируйте HTTPS с сертификатами.
- Проверьте, что события авторизации логируются.
Пример теста с использованием pytest
import pytest
from rest_framework.test import APIClient
@pytest.mark.django_db
def test_admin_access():
client = APIClient()
response = client.post('/admin-endpoint/')
assert response.status_code == 403 # Только для админов
На этом практическое занятие завершено. Мы настроили основные аспекты безопасности: ограничения по ролям, CORS, HTTPS и мониторинг событий. Эти методы обеспечат высокий уровень защиты API в реальном приложении.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ