Сьогодні ми будемо практикуватися, а саме — налаштовувати безпеку 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 у реальному додатку.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ