Inventario implements multiple layers of security following Django best practices. This guide covers the security features and configuration requirements.
SECRET_KEY Management
The SECRET_KEY is Django’s most critical security setting. It’s used for:
- Cryptographic signing of sessions
- CSRF token generation
- Password reset tokens
- Signed cookies and data
Configuration
SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-641xw29uuar#$5&nj!kxjozc+#2#=f6s+4lmziq&_8hnh$#@6$')
The default key is marked as insecure and must never be used in production. Generate a new key:python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
Best Practices
- Never commit
SECRET_KEY to version control
- Rotate regularly (requires re-issuing sessions)
- Store securely in environment variables only
- Use different keys for different environments
- Keep secret - anyone with the key can forge signatures
If SECRET_KEY is compromised:
- Generate a new key immediately
- Update environment variables in all environments
- Restart all application instances
- All users will be logged out (sessions invalidated)
- All password reset tokens will be invalidated
CSRF Protection
Cross-Site Request Forgery (CSRF) protection is enabled by default and configured for production use.
Middleware
MIDDLEWARE = [
# ...
'django.middleware.csrf.CsrfViewMiddleware',
# ...
]
Cookie Configuration
CSRF_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_SECURE = not DEBUG # True in production
Settings breakdown:
CSRF_COOKIE_SAMESITE = 'Lax': Allows CSRF cookie on top-level navigation
CSRF_COOKIE_SECURE = True: Requires HTTPS for CSRF cookie (production only)
Trusted Origins
For HTTPS deployments, configure trusted origins:
CSRF_TRUSTED_ORIGINS = os.environ.get(
'CSRF_TRUSTED_ORIGINS',
'http://127.0.0.1:8000'
).split(',')
Environment variable:
CSRF_TRUSTED_ORIGINS=https://myapp.railway.app,https://www.example.com
Must include the full URL with https:// protocol. Without this, POST requests will fail with CSRF errors.
CSRF Token Usage
In templates, include CSRF token in all forms:
<form method="post">
{% csrf_token %}
<!-- form fields -->
</form>
For AJAX requests, include CSRF token in headers:
fetch('/api/endpoint/', {
method: 'POST',
headers: {
'X-CSRFToken': getCookie('csrftoken'),
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
Session Security
Session cookies are secured based on the DEBUG setting.
Session Cookie Configuration
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_SECURE = not DEBUG # True in production
Settings breakdown:
SESSION_COOKIE_SAMESITE = 'Lax': Prevents CSRF while allowing normal navigation
SESSION_COOKIE_SECURE = True: Requires HTTPS for session cookies (production)
Session Management
MIDDLEWARE = [
# ...
'django.contrib.sessions.middleware.SessionMiddleware',
# ...
]
Django’s session framework:
- Stores session data server-side (database)
- Only session ID is stored in cookie
- Session ID is cryptographically signed with
SECRET_KEY
- Sessions expire on browser close by default
Custom Middleware
'applications.cuentas.middleware.ForzarCambioPasswordMiddleware'
Custom middleware to enforce password changes. Users flagged for password reset are redirected to the password change page.
Password Validation
Inventario uses custom password validators for enhanced security.
AUTH_PASSWORD_VALIDATORS = [
{'NAME': 'applications.cuentas.validators.LongitudMinimaValidator'},
{'NAME': 'applications.cuentas.validators.ContraseñaComunValidator'},
{'NAME': 'applications.cuentas.validators.ContraseñaNumericaValidator'},
]
Custom Validators
- LongitudMinimaValidator: Enforces minimum password length
- ContraseñaComunValidator: Rejects common passwords
- ContraseñaNumericaValidator: Prevents fully numeric passwords
These validators are located in applications/cuentas/validators.py.
Password Storage
Django uses PBKDF2 algorithm with SHA256 hash by default:
- Passwords are never stored in plain text
- Uses key stretching (320,000 iterations as of Django 5.2)
- Salted to prevent rainbow table attacks
- Automatically upgraded when algorithms improve
HTTPS Configuration
Security Middleware
MIDDLEWARE = [
'django.middleware.locale.LocaleMiddleware',
'django.middleware.security.SecurityMiddleware', # First after locale
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...
]
SecurityMiddleware adds security headers and HTTPS redirects.
HTTPS-Only Cookies
In production (DEBUG=False):
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
These settings ensure cookies are only sent over HTTPS, preventing interception.
Protocol Configuration
ACCOUNT_DEFAULT_HTTP_PROTOCOL = 'https'
Forces HTTPS for allauth (OAuth) redirect URIs.
Clickjacking Protection
MIDDLEWARE = [
# ...
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Prevents the application from being embedded in iframes, protecting against clickjacking attacks.
Default behavior: Sets X-Frame-Options: DENY header.
Authentication Security
Custom User Model
AUTH_USER_MODEL = 'cuentas.Usuario'
Inventario uses a custom user model for enhanced control over authentication.
Authentication Backends
AUTHENTICATION_BACKENDS = (
'allauth.account.auth_backends.AuthenticationBackend',
'django.contrib.auth.backends.ModelBackend',
)
Supports:
- OAuth via django-allauth: Google Sign-In
- Model backend: Traditional username/password
OAuth Configuration
Google OAuth 2.0 settings:
SOCIALACCOUNT_PROVIDERS = {
'google': {
'SCOPE': ['profile', 'email'],
'AUTH_PARAMS': {'access_type': 'online'},
'VERIFIED_EMAIL': True,
'APP': {
'client_id': os.environ.get('GOOGLE_CLIENT_ID', ''),
'secret': os.environ.get('GOOGLE_CLIENT_SECRET', ''),
'key': ''
}
}
}
Secure OAuth credentials:
- Store
GOOGLE_CLIENT_SECRET in environment variables only
- Never commit OAuth secrets to version control
- Configure authorized redirect URIs in Google Cloud Console
- Use separate OAuth credentials for development and production
Email Verification
ACCOUNT_EMAIL_VERIFICATION = 'none'
ACCOUNT_EMAIL_REQUIRED = True
Email is required but verification is disabled. Consider enabling verification in production:
ACCOUNT_EMAIL_VERIFICATION = 'mandatory' # Require email verification
Database Security
Connection String Security
DATABASES = {
'default': dj_database_url.config(
default=f'sqlite:///{BASE_DIR / "db.sqlite3"}',
conn_max_age=600
)
}
Database credentials are passed via DATABASE_URL environment variable:
DATABASE_URL=postgresql://user:password@host:5432/dbname
Never hardcode database credentials in settings.py. Always use environment variables.
Connection Pooling
conn_max_age=600: Keeps database connections open for 10 minutes, improving performance while limiting connection count.
API Key Security
Inventario integrates with external services requiring API keys.
Secure Storage
All API keys are stored in environment variables:
OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY', '')
RESEND_API_KEY = os.environ.get('RESEND_API_KEY', '')
TWILIO_AUTH_TOKEN = os.environ.get('TWILIO_AUTH_TOKEN', '')
Rotation Strategy
- Regular rotation: Rotate API keys every 90 days
- Immediate rotation if compromised
- Monitor usage: Check for unusual API usage patterns
- Scope limits: Use minimal required permissions
API keys with empty defaults ('') will fail silently. Monitor logs for authentication errors when features are enabled.
Production Security Checklist
Before deploying to production:
Critical Settings
Authentication
Infrastructure
Monitoring
Updates
Consider adding these security headers via SecurityMiddleware settings:
# Recommended additions to settings.py
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = not DEBUG
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
These settings:
- Force HTTPS connections (HSTS)
- Prevent XSS attacks
- Prevent MIME type sniffing
- Redirect HTTP to HTTPS
Incident Response
If a security incident occurs:
- Immediate: Rotate
SECRET_KEY and affected API keys
- Investigate: Check logs for unauthorized access
- Notify: Inform affected users if data was compromised
- Patch: Fix the vulnerability
- Deploy: Push updates to production immediately
- Document: Record the incident and response
- Review: Update security practices to prevent recurrence