Skip to main content

Django security app - IP/email/country blocking, rate limiting, login tracking, auto-blocking

Project description

NAI Security

PyPI version Django Packages

Django security package for IP blocking, country blocking, email blocking, rate limiting, and login tracking.

Features

  • IP Blocking - Block specific IPs manually or automatically
  • Country Blocking - Block/allow countries using GeoIP
  • Email Blocking - Block disposable emails and specific addresses
  • Domain Blocking - Block email domains (disposable, spam, etc.)
  • User Agent Blocking - Block bots, scrapers, attack tools
  • Rate Limiting - Custom rate limit rules per endpoint
  • Login History - Track user logins with anomaly detection
  • Auto-Blocking - Automatically block IPs/countries based on attack patterns
  • Security Logs - Comprehensive logging of all security events
  • Axes Integration - Dynamic login attempt limits, cooloff time, and per-attempt expiry via admin panel (requires django-axes >= 8.3)
  • Whitelisted Users - Exempt specific users from security checks

Installation

pip install nai-security

With all optional dependencies:

pip install nai-security[all]

Or install from GitHub:

pip install git+https://github.com/nematiai/nai-security.git

Quick Start

1. Add to INSTALLED_APPS

INSTALLED_APPS = [
    ...
    "nai_security",
]

2. Add Middleware

Important: nai_security.middleware.SecurityMiddleware must be placed after django.contrib.auth.middleware.AuthenticationMiddleware. The package validates this at startup and raises ImproperlyConfigured if misordered.

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",  # must be before
    "django.contrib.messages.middleware.MessageMiddleware",
    "nai_security.middleware.SecurityMiddleware",               # after auth
    "nai_security.middleware.RateLimitLoggingMiddleware",       # optional
]

3. Configure Settings

GEOIP_PATH = "/path/to/GeoLite2-Country.mmdb"

# Optional: override default exempt paths (default: /health/, /ready/, /favicon.ico)
NAI_SECURITY_EXEMPT_PATHS = ["/health/", "/health", "/ready/", "/ready", "/favicon.ico"]

4. Run Migrations

python manage.py migrate

5. Download GeoIP Database

python manage.py download_geoip

Dependencies

Required:

  • Django >= 4.2
  • geoip2 >= 4.0
  • redis >= 4.0

Optional:

  • django-axes >= 8.3 — login attempt tracking and lockout; without it axes features are silently disabled
  • django-ratelimit >= 4.0 — rate limiting per endpoint
  • django-import-export >= 3.0 — admin import/export for blocked emails/domains; without it those buttons are hidden
  • django-unfold >= 0.10 — admin UI theme; without it falls back to standard Django admin
  • celery — background tasks (auto-block processing, sync, reports); without it tasks are no-ops

Axes Integration

Enable brute-force protection with dynamic settings controlled from the admin panel:

# settings.py
INSTALLED_APPS = [
    ...
    "axes",
    "nai_security",
]

AXES_HANDLER = 'nai_security.handlers.axes_integration.DynamicAxesHandler'

AUTHENTICATION_BACKENDS = [
    'axes.backends.AxesStandaloneBackend',
    'django.contrib.auth.backends.ModelBackend',
]

This gives you admin-configurable control over:

Setting Description
Max login attempts Failed attempts before lockout (default: 5)
Cooloff time Minutes before locked accounts auto-unlock (0 = permanent)
Attempt expiry Each failed attempt expires independently — requires cooloff > 0

All changes take effect immediately — no server restart required.

Validation: Enabling attempt expiry with cooloff set to 0 will raise a validation error in the admin panel.

Whitelist bypass

DynamicAxesHandler short-circuits all axes checks (is_allowed, is_locked, user_login_failed) when the request matches any active whitelist:

  • The client IP (HTTP_X_FORWARDED_FOR or REMOTE_ADDR) appears in WhitelistedIP — bypass works even when the request has no credentials (e.g. GET /login/).
  • The login username/email resolves to a user with an active WhitelistedUser row — regardless of exemption_type. The exemption_type field still controls middleware-level bypasses (IP, country, rate-limit); for axes lockout the rule is binary.
  • Username lookup tolerates USERNAME_FIELD='username' deployments where the login form posts an email — falls back to email__iexact automatically.

When a whitelisted request fails authentication, no AccessAttempt row is recorded — the table stays clean for whitelisted admins.

Management Commands

# Download GeoIP database
python manage.py download_geoip

# Sync disposable email domains and bad bot lists
python manage.py sync_security_lists
python manage.py sync_security_lists --domains-only
python manage.py sync_security_lists --bots-only

Celery Tasks

from celery.schedules import crontab

CELERY_BEAT_SCHEDULE = {
    'security-auto-blocks': {
        'task': 'security.process_auto_blocks',
        'schedule': crontab(minute='*/5'),
    },
    'security-cleanup-expired': {
        'task': 'security.cleanup_expired_blocks',
        'schedule': crontab(minute=0, hour='*'),
    },
    'security-sync-lists': {
        'task': 'security.sync_security_lists',
        'schedule': crontab(minute=0, hour=0, day_of_week=0),
    },
    'security-daily-report': {
        'task': 'security.generate_security_report',
        'schedule': crontab(minute=0, hour=6),
    },
}

Models

Model Description
BlockedIP Blocked IP addresses
BlockedCountry Blocked countries
AllowedCountry Allowed countries (whitelist mode)
BlockedEmail Blocked email addresses
BlockedDomain Blocked email domains
BlockedUserAgent Blocked user agents
WhitelistedIP IPs that bypass all checks
WhitelistedUser Users exempted from security checks (see exemption types below)
RateLimitRule Custom rate limit rules
LoginHistory User login tracking
SecurityLog Security event logs
SecuritySettings Global settings (singleton)

User Exemptions (WhitelistedUser)

Exempt specific users from security checks via the admin panel or ORM:

Exemption Type Bypasses
all Entire security middleware — IP, country, user-agent
ip_block IP blocking only
geo_block Country/geo blocking only
rate_limit Rate limit logging only

Exemptions support optional expiration (expires_at) and can be toggled via is_active.

Axes lockout note: any active WhitelistedUser row exempts the user from django-axes lockout, regardless of exemption_type. The exemption_type field controls only the SecurityMiddleware checks (IP/country/rate-limit). See Axes Integration → Whitelist bypass.

Upgrading to 1.10.1

Bug fixes (no breaking changes):

  • Whitelisted users were still being locked out by django-axes. Three independent paths caused this:
    1. WhitelistedIP was not consulted by the axes handler — whitelisted IPs could still be locked, especially with AXES_LOCKOUT_PARAMETERS=['ip_address'].
    2. The handler's whitelist check was hard-coded to exemption_type='all' — users with 'ip_block', 'rate_limit', or 'geo_block' were still locked.
    3. User lookup used USERNAME_FIELD only and silently failed when the login form posts an email but USERNAME_FIELD='username'.
  • All three are fixed in nai_security.handlers.axes_integration.DynamicAxesHandler via a unified _is_request_whitelisted() helper. Failed login attempts from whitelisted requests are no longer recorded in AccessAttempt.
  • Verified end-to-end with 100x failed-login smoke test (scripts/smoke_100x_lockout.py) and 9 regression tests in tests/test_axes_integration.py.

Upgrading to 1.9.1

Breaking changes:

  • SecurityMiddleware now requires AuthenticationMiddleware to be placed before it in MIDDLEWARE. If misordered, the app raises ImproperlyConfigured at startup. Previously the middleware silently failed to resolve users.
  • NAI_SECURITY_USER_RESOLVER setting has been removed. User resolution now uses request.user directly (guaranteed by middleware ordering).
  • parse_user_agent now correctly detects Android, iOS, and Opera (previously misidentified as Linux, macOS, and Chrome respectively).

Testing

python -m pytest

License

MIT License

Author

Ali Nemati - NEMATI AI

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nai_security-1.10.1.tar.gz (48.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nai_security-1.10.1-py3-none-any.whl (48.2 kB view details)

Uploaded Python 3

File details

Details for the file nai_security-1.10.1.tar.gz.

File metadata

  • Download URL: nai_security-1.10.1.tar.gz
  • Upload date:
  • Size: 48.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for nai_security-1.10.1.tar.gz
Algorithm Hash digest
SHA256 4814dc74d8e35e3b41cbd6b8ba07af7b7b2af304dd3acfd0c105f63b6834528e
MD5 a1469dff11d7c2390458922d3e628084
BLAKE2b-256 f873d35068bf08cde2fae8b26bdadca7ff51b8cd1fd462b688cc9ae96d67ab2b

See more details on using hashes here.

File details

Details for the file nai_security-1.10.1-py3-none-any.whl.

File metadata

  • Download URL: nai_security-1.10.1-py3-none-any.whl
  • Upload date:
  • Size: 48.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for nai_security-1.10.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9ab6abb2a0612bd0aaf478d8677d8bafa731524a6dd55fa0b1d60a71931bed71
MD5 86d63aa5cd3ad14e4a53f8dbb11653f4
BLAKE2b-256 4a38c4cf00525c3b7bb8a99d8ca3199c97d98efdbb79fccc73b154026d834876

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page