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.py
DEBUG = False
ALLOWED_HOSTS = ["quanlychitieu.vn", "www.quanlychitieu.vn"]
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_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.py
CSRF_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.py
INSTALLED_APPS += ["axes"]
AUTHENTICATION_BACKENDS = [
"axes.backends.AxesStandaloneBackend",
"django.contrib.auth.backends.ModelBackend",
]
AXES_FAILURE_LIMIT = 5
AXES_COOLOFF_TIME = 1 # giờ
AXES_LOCKOUT_TEMPLATE = "lockout.html"
# config/urls.py
from axes.decorators import axes_dispatch
urlpatterns = [
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

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