Mở đầu
Tập này khóa chặt ứng dụng quản lý chi tiêu trước khi mở cổng cho người dùng thật: bật HTTPS và HSTS, siết ALLOWED_HOSTS, CSRF, CORS và các cờ SECURE_*, rồi chặn dò mật khẩu bằng django-axes. Nối tiếp tập 10, nơi Celery đã chạy ổn định dưới systemd, đây là lớp phòng thủ cuối cùng trước khi đón traffic thật.
1. Tắt DEBUG và siết ALLOWED_HOSTS
DEBUG = True trên production là lỗ hổng nghiêm trọng nhất: trang lỗi của Django phơi bày cả settings, biến môi trường và đoạn code xung quanh lỗi. Tắt DEBUG, rồi chỉ liệt kê đúng tên miền thật trong ALLOWED_HOSTS để Django từ chối mọi request mang Host lạ.
# config/settings/prod.pyDEBUG = FalseALLOWED_HOSTS = ["quanlychitieu.vn", "www.quanlychitieu.vn"]SECURE_SSL_REDIRECT = TrueSECURE_HSTS_SECONDS = 31536000SECURE_HSTS_INCLUDE_SUBDOMAINS = TrueSECURE_HSTS_PRELOAD = TrueSESSION_COOKIE_SECURE = TrueCSRF_COOKIE_SECURE = TrueSESSION_COOKIE_HTTPONLY = TrueSESSION_COOKIE_SAMESITE = "Lax"CSRF_COOKIE_SAMESITE = "Lax"
2. Ép HTTPS và bật HSTS
SECURE_SSL_REDIRECT bắt mọi request HTTP chuyển sang HTTPS, nên không còn đường nào truyền mật khẩu hay token qua kênh trần. HSTS đi xa hơn: trình duyệt ghi nhớ chỉ dùng HTTPS với tên miền này trong một năm. Chỉ bật HSTS_PRELOAD khi mọi subdomain đều đã có HTTPS, vì gỡ khỏi danh sách preload rất khó.
3. Khóa cookie session và CSRF
Cookie session và cookie CSRF phải mang cờ Secure để trình duyệt chỉ gửi qua HTTPS, và HttpOnly để JavaScript không đọc được sessionid — một lỗ XSS nhờ đó không leo thang thành chiếm tài khoản. SameSite=Lax chặn trình duyệt đính cookie vào request từ site khác, giảm nguy cơ CSRF mà không làm hỏng luồng đăng nhập.
4. CSRF_TRUSTED_ORIGINS và CORS cho API
Từ Django 4.0, form POST qua HTTPS bị từ chối nếu origin không nằm trong CSRF_TRUSTED_ORIGINS — lỗi 403 này hay xuất hiện ngay sau khi bật HTTPS, và cách sửa đúng là khai báo đúng origin, không phải tắt CSRF. Với API DRF, django-cors-headers chỉ mở CORS cho đúng frontend của bạn.
# config/settings/prod.pyCSRF_TRUSTED_ORIGINS = ["https://quanlychitieu.vn"]CORS_ALLOWED_ORIGINS = ["https://quanlychitieu.vn"]CORS_ALLOW_CREDENTIALS = True
5. Chặn brute-force bằng django-axes
Mật khẩu mạnh vẫn bị dò nếu kẻ tấn công được thử không giới hạn. django-axes khóa tài khoản sau vài lần đăng nhập sai, ghi lại IP của từng lần thử và tự mở khóa sau thời gian chờ. Gắn axes vào admin và trang đăng nhập, nơi chứa dữ liệu tài chính.
# config/settings/prod.pyINSTALLED_APPS += ["axes"]AUTHENTICATION_BACKENDS = [ "axes.backends.AxesStandaloneBackend", "django.contrib.auth.backends.ModelBackend",]AXES_FAILURE_LIMIT = 5AXES_COOLOFF_TIME = 1 # giờAXES_LOCKOUT_TEMPLATE = "lockout.html"
# config/urls.pyfrom axes.decorators import axes_dispatchurlpatterns = [ path("admin/", axes_dispatch(admin.site.urls)),]
6. Chạy check –deploy trước mỗi lần phát hành
Django có sẵn lệnh kiểm tra bảo mật cho production: python manage.py check –deploy liệt kê mọi thiết lập chưa đạt, từ SECURE_HSTS_SECONDS tới X_FRAME_OPTIONS. Đưa lệnh này vào pipeline CI ở tập 17, và chạy tay ngay bây giờ — checklist tự động luôn đáng tin hơn trí nhớ.
python manage.py check --deploy
Áp dụng vào dự án của bạn
Áp dụng ngay trong tối nay: tắt DEBUG, điền đúng tên miền vào ALLOWED_HOSTS, bật toàn bộ cờ SECURE_* trong settings prod, khai báo CSRF_TRUSTED_ORIGINS rồi chạy check –deploy và sửa tới khi hết cảnh báo. Sau đó cài django-axes, tự thử đăng nhập sai năm lần để thấy trang khóa.
Bài viết liên quan
- Django vận hành production #10: Celery trên production — worker, beat, Flower, systemd và retry
- Django vận hành production #9: Cache với Redis — cache framework, pattern invalidation, tránh cache sai dữ liệu
- Django vận hành production #3: Gunicorn + Nginx — chạy Django sau reverse proxy
Câu hỏi thường gặp
Bật HSTS rồi muốn quay lại HTTP có được không?
Không dễ: trình duyệt đã ghi nhớ sẽ tự ép HTTPS trong suốt thời gian HSTS_SECONDS, và danh sách preload gần như không gỡ được. Vì vậy chỉ bật HSTS khi HTTPS đã chạy ổn định vài tuần.
Lỗi 403 CSRF sau khi bật HTTPS sửa thế nào?
Thêm đúng origin HTTPS vào CSRF_TRUSTED_ORIGINS, ví dụ https://quanlychitieu.vn, rồi kiểm tra Nginx có chuyển đúng header X-Forwarded-Proto hay không. Tuyệt đối không tắt CsrfViewMiddleware để “cho chạy được”.
django-axes khóa nhầm người dùng thật thì sao?
Người dùng chờ hết AXES_COOLOFF_TIME rồi đăng nhập lại, hoặc admin mở khóa tay trong giao diện quản trị của axes. Đặt giới hạn thất bại ở mức 5 lần và bật thông báo để phát hiện khóa hàng loạt bất thường.
Nguồn tham khảo
- Django Documentation — Deployment checklist: https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
- django-axes Documentation: https://django-axes.readthedocs.io/
- OWASP Cheat Sheet — Django Security: https://cheatsheetseries.owasp.org/cheatsheets/Django_Cheat_Sheet.html
