Обход дросселирования DRF

У меня есть приложение Django, работающее на сервере Nginx + Gunicorn, где я использую DRF-дросселирование. Всякий раз, когда я делаю API запросы к моему серверу и изменяю значение заголовка X-Forwarded-For в клиенте, я могу обойти дросселирование для неаутентифицированных пользователей и тем самым получить неограниченный доступ к API. Это, конечно, нежелательно.

Я думаю, что способ справиться с этим заключается в том, чтобы Nginx добавлял реальный IP в конец заголовка запроса X-Forwarded-For до того, как он достигнет сервера, используя прокси-параметры. Это просто не изменяет заголовок, когда я проверяю Postman / RapidApi клиент. Я предполагаю, что именно это и вызывает ошибку, но в конечном итоге я не знаю.

Nginx conf:

location / {
    include proxy_params;
    proxy_pass http://unix:/run/gunicorn.sock;
}

Файл proxy_params от Nginx включает установку заголовка запроса X-Forwarded-For следующим образом:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

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

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

  1. уязвимость безопасности: обход дросселирования
  2. Обход дросселирования на основе ip-адреса источника
  3. Внедрение дросселирования API Django-rest и обхода без аутентификации

Настоятельно рекомендуется использовать другие сторонние решения для защиты от DDOS и грубой силы.

Вы можете настроить дросселирование DRF для устранения упомянутой уязвимости. Но имейте в виду, что это не безопасное решение!

DRF throttling использует X-Forwarded-For HTTP-заголовок для генерации ключа для ограничения доступа. Как описано в официальных документах, настройка осуществляется путем наследования throttling.BaseThrottle:

class RandomRateThrottle(throttling.BaseThrottle):
    def allow_request(self, request, view) -> bool:
        # Your custom logic here...

Заголовок запроса доступен в allow_request() и вы можете использовать другие поля (например, User-Agent) для проверки уникальности создателя запроса. Вы также можете добавить немного случайности.

Посмотрите здесь список полей заголовков HTTP.

Примечание: allow_request() должен возвращать булево значение.

Примечание: Проверка других полей исправляет только упомянутую вами уязвимость, и это просто лучше, чем значения по умолчанию.

Я смог правильно получить IP-адрес клиента, установив параметр NUM_PROXIES в конфигурации DRF. Этот параметр определяет, скольким прокси-серверам DRF должен доверять в заголовке XFF и, следовательно, сможет выбрать правильный IP-адрес. Поскольку у меня был только один прокси-сервер, я установил NUM_PROXIES равным 1:

REST_FRAMEWORK = {
    …
    'NUM_PROXIES': 1
}

В дополнение к этому я изменил формат журнала Gunicorn, чтобы иметь возможность наблюдать заголовок XFF в журналах доступа. Затем я понял, что nginx действительно правильно добавляет IP в заголовок XFF.

Вернуться на верх