Django Security
Source: affaan-m/everything-claude-code Original file: skills/django-security/SKILL.md Category: E-Security
When to Activate
- Setting up Django authentication/authorization
- Implementing user permissions and roles
- Configuring production security settings
- Reviewing Django apps for security issues
- Deploying Django to production
Core Production Settings
DEBUG = False # CRITICAL: Never True in production
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SESSION_COOKIE_HTTPONLY = True
CSRF_COOKIE_HTTPONLY = True
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
if not SECRET_KEY:
raise ImproperlyConfigured('DJANGO_SECRET_KEY required')
Authentication
- Use a custom user model with email as
USERNAME_FIELD - Use Argon2 or BCrypt password hashers (stronger than default PBKDF2)
- Configure all password validators with minimum length of 12
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
]
Authorization
Django Permissions + DRF
# Model-level permissions
class Post(models.Model):
class Meta:
permissions = [
('can_publish', 'Can publish posts'),
('can_edit_others', 'Can edit posts of others'),
]
# View-level with mixins
class PostUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
permission_required = 'app.can_edit_others'
raise_exception = True
DRF Custom Permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
RBAC Pattern
class User(AbstractUser):
ROLE_CHOICES = [('admin', 'Admin'), ('moderator', 'Mod'), ('user', 'User')]
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='user')
def is_admin(self):
return self.role == 'admin' or self.is_superuser
SQL Injection Prevention
# SAFE: ORM (auto-parameterized)
User.objects.filter(email__iexact=email)
User.objects.filter(Q(username__icontains=q) | Q(email__icontains=q))
# SAFE: raw() with parameters
User.objects.raw('SELECT * FROM users WHERE email = %s', [email])
# DANGEROUS: String interpolation
User.objects.raw(f'SELECT * FROM users WHERE username = {username}') # NEVER
XSS Prevention
- Django auto-escapes template variables by default
- Never use
|safewith user input - Use
format_html()for dynamic HTML - Use
escapejsfilter for JavaScript contexts - Set security headers: CSP, X-Content-Type-Options, X-Frame-Options
CSRF Protection
- CSRF enabled by default; include
{% csrf_token %}in forms - For AJAX: read
csrftokencookie and send asX-CSRFTokenheader - Use
@csrf_exemptonly for external webhooks - Set
CSRF_COOKIE_SECURE,CSRF_COOKIE_HTTPONLY,CSRF_COOKIE_SAMESITE
File Upload Security
def validate_file_extension(value):
ext = os.path.splitext(value.name)[1]
valid_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf']
if ext.lower() not in valid_extensions:
raise ValidationError('Unsupported file extension.')
def validate_file_size(value):
if value.size > 5 * 1024 * 1024:
raise ValidationError('File too large. Max 5MB.')
Store uploads outside web root; use S3 or CDN for production.
API Security (DRF)
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day',
'upload': '10/hour',
},
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
Environment Variables
Use django-environ or python-decouple:
import environ
env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env()
SECRET_KEY = env('DJANGO_SECRET_KEY')
Never commit .env files.
Quick Checklist
| Check | Description |
|---|---|
DEBUG = False |
Never run DEBUG in production |
| HTTPS only | Force SSL, secure cookies |
| Strong secrets | Environment variables for SECRET_KEY |
| Password validation | All validators enabled, min 12 chars |
| CSRF protection | Enabled by default, don't disable |
| XSS prevention | Don't use ` |
| SQL injection | Use ORM, never concatenate queries |
| File uploads | Validate type and size |
| Rate limiting | Throttle API endpoints |
| Security headers | CSP, X-Frame-Options, HSTS |
| Logging | Log security events |
| Updates | Keep Django and dependencies current |
Remember: Security is a process, not a product. Regularly review and update your practices.