Django是一個擁有強大安全功能的優秀框架。然而,許多開發者,特別是在項目初期,往往犯了不考慮的錯誤,將官方文檔或教程中創建的/admin URL直接暴露在外。

這就像在告示牌上大肆宣傳「我們的前門在這裡」。全球的自動化掃描器和攻擊機器人一旦發現你的網站,最先掃描的就是example.com/admin/

這篇文章將介紹幾種保護Django管理頁面安全的關鍵方法,從簡單的URL更改到積極防範,立刻封鎖入侵者的IP。將重點放在為什麼這麼做。


1. 最基本:更改管理URL(使用環境變量)



這是最簡單、最快速、最有效的第一道防線。

🤔 為什麼要隱藏URL?

如果攻擊者不知道URL,就無法嘗試暴力破解或盜取憑證。使用類似my-super-secret-admin-path/的無法猜測的路徑,而不是所有人都知道的/admin,可以阻止99%的自動攻擊。

雖然「隱藏」(Obscurity)並不是「安全」(Security)的全部,但卻是最具成本效益的防護。

🚀 如何:通過環境變量注入

不將URL硬編碼在代碼中,而是通過環境變量(Environment Variable)來進行注入是一個最佳實踐。

  1. .env文件(或服務器環境變量設置)
# .env
# 使用一個不可猜測的複雜字符串。
DJANGO_ADMIN_URL=my-secret-admin-portal-b7x9z/
  1. settings.py
# settings.py
import os

# 設置默認值,但從環境變量中讀取
ADMIN_URL = os.environ.get('DJANGO_ADMIN_URL', 'admin/')
  1. urls.py(主項目)
# urls.py
from django.contrib import admin
from django.urls import path
from django.conf import settings # import settings 模塊

urlpatterns = [
    # 使用settings.ADMIN_URL的值來代替admin/
    path(settings.ADMIN_URL, admin.site.urls),
    # ... other urls
]

現在即使在開發環境中使用admin/,在運行服務器時也只需更改環境變量即可隱藏實際的管理路徑。


2. 建立防線:在Nginx中根據IP限制訪問

即使URL意外曝光的話,完全阻止未經授權的IP訪問管理頁面也是一種強有力的做法。

🤔 為什麼要在Nginx中阻止?

這種方式在攻擊流量到達Django(應用程序)之前,在Web服務器(Nginx)層進行阻止。也就是說,Django甚至不知道攻擊是否發生,從而不會浪費不必要的資源。如果管理者僅從特定IP(辦公室、VPN等)訪問,這是最可靠的方法。

如何:Nginx配置示例

在人Nginx設定檔案(sites-available中對應網站的設置)中添加location區塊。

server {
    # ... (現有設置)

    # 指定與ADMIN_URL環境變量相同的路徑
    location /my-secret-admin-portal-b7x9z/ {
        # 1. 允許的IP地址(例如:辦公室固定IP)
        allow 192.168.0.10;
        # 2. 允許的IP段(例如:VPN段)
        allow 10.0.0.0/24;
        # 3. 本地服務器(伺服器內部)
        allow 127.0.0.1;

        # 4. 阻止所有其他訪問
        deny all;

        # 5. 將所有處理過的請求轉發到uwsgi/gunicorn代理
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }

    # ... (其他location設置)
}

現在,除了允許的IP之外,所有用戶訪問該URL時,Django不會作出任何響應,Nginx會立即返回403 Forbidden錯誤。


3. 守衛設置:限制登錄嘗試次數(django-axes)



如果攻擊者不顧一切獲得了URL並繞過了IP限制,那麼下一步就是必須阻止暴力破解攻擊。

🤔 為什麼要限制嘗試次數?

暴力破解攻擊會自動將數千、數萬個密碼代入像「admin」這樣的常見帳戶ID。像django-axes這樣的包會創建「在短時間內登錄失敗超過5次時,鎖定該IP或帳戶若干時間」的規則。

這將使自動化腳本幾乎無用。

如何:使用django-axes

django-axes是執行此任務的標準包。

  1. 安裝pip install django-axes

  2. 在settings.py中註冊

INSTALLED_APPS = [
    # ...
    'axes', # 建議放在其他應用之前
    # ...
    'django.contrib.admin',
]

AUTHENTICATION_BACKENDS = [
    # AxesBackend必須放在最前面。
    'axes.backends.AxesBackend',
    # 基本的Django認證後端
    'django.contrib.auth.backends.ModelBackend',
]

# 登錄失敗5次后鎖定(默認值)
AXES_FAILURE_LIMIT = 5
AXES_COOLOFF_TIME = 0.166 # 0.166 * 60 = 約10分鐘
  1. 遷移python manage.py migrate

現在,如果有人連續失敗登錄5次,axes會記錄該嘗試並在設定時間內鎖定該IP/帳戶的登錄。


4. 雙重鎖:二步驟驗證(2FA)

當密碼被破解時的最後防線。

🤔 為什麼需要2FA?

管理員帳戶的密碼可能已洩露,或者使用的密碼過於簡單。二步驟驗證(Two-Factor Authentication)要求「我知道的東西(密碼)」以及「我擁有的東西(智能手機OTP)」

即使駭客偷走密碼,沒有管理員的智能手機也無法登錄。

如何:使用django-otp

django-otp是將2FA整合到Django中的核心包。

  1. 安裝pip install django-otp

  2. 在settings.py中註冊

INSTALLED_APPS = [
    # ...
    'django_otp',
    'django_otp.plugins.otp_totp', # 支持Google Authenticator等
    # ...
]

MIDDLEWARE = [
    # ...
    'django_otp.middleware.OTPMiddleware', # 在SessionMiddleware之後
    # ...
]

django-otp是基本框架,與之搭配使用的django-two-factor-auth等包可以輕鬆實現用戶掃描QR碼並註冊的整個流程。


5. 設置陷阱:與Honeypot和Fail2Ban結合

這是最積極的防護。利用攻擊者對/admin的掃描嘗試,將其從服務器中永久驅逐。

🤔 為什麼要設置陷阱?

攻擊者無論如何會繼續掃描/admin。那麼就把這個路徑設置為假陷阱(Honeypot),對試圖訪問的IP直接進行即時封鎖,視其為惡意行為。

如何:假管理員+ Fail2Ban

這種方法稍顯複雜,但效果非常好。

  1. 創建假管理員視圖:像第1步隱藏的實際管理URL設為my-secret-admin-portal-b7x9z/。然後將被放棄的/admin/路徑連接到一個假視圖。
# urls.py
from django.urls import path
from . import views # 載入假視圖

urlpatterns = [
    path('my-secret-admin-portal-b7x9z/', admin.site.urls), # 真實的
    path('admin/', views.admin_honeypot), # 假的(陷阱)
]

# views.py
import logging
from django.http import HttpResponseForbidden

# 為陷阱專用設置日誌記錄器(需要在settings.py中定義'honeypot'日誌)
honeypot_logger = logging.getLogger('honeypot')

def admin_honeypot(request):
    # 記錄試圖訪問者的IP到'honeypot'日誌
    ip = request.META.get('REMOTE_ADDR')
    honeypot_logger.warning(f"HONEYPOT: Admin access attempt from {ip}")

    # 對攻擊者顯示403錯誤
    return HttpResponseForbidden()
  1. Fail2Ban設置Fail2Ban是一個實時監控服務器日誌文件的工具,一旦檢測到特定模式(例如:「HONEYPOT: ...」),就會將產生該日誌的IP添加到iptables(Linux防火牆)中進行封鎖。

    • 設置Django以將日誌存儲到honeypot.log文件。

    • 將Fail2Ban設置為監視honeypot.log

    • 如果有人訪問/admin/views.py會記錄該日誌,Fail2Ban將檢測到此並立即禁止該IP的所有連接(SSH、HTTP等)。

總結

保護Django管理頁面的安全性關鍵在於建立多重防護。

  • (必須) 1. 更改URL: 現在就花5分鐘進行。

  • (建議) 2. IP限制: 擁有固定IP的話效果最好。

  • (建議) 3. django-axes: 阻止暴力攻擊。

  • (強烈建議) 4. 2FA: 徹底阻止管理帳戶被盜。

  • (高級) 5. 假陷阱: 以攻擊方式保護整個服務器。

/admin的放任自流是對安全的疏忽。現在就檢查你的urls.py