title: Django 安全性與防禦性程式設計:如何保護 Django Web 應用
date: 2024/5/13 20:26:58
updated: 2024/5/13 20:26:58
categories:
- 後端開發
tags:
- CSRF
- XSS
- SQL
- Upload
- HTTPOnly
- Password
- Session
跨站請求偽造(CSRF)
跨站請求偽造(CSRF)是一種常見的網路攻擊,它利用使用者的身份和許可權,欺騙伺服器執行非預期的操作。Django 提供了一種內建的 CSRF 保護機制,可以幫助保護應用免受 CSRF 攻擊。
Django 的 CSRF 保護機制是透過 CSRF 令牌(CSRF Token)實現的,它是一個加密字串,包含了一些關於使用者會話和請求的資訊。在每個 POST、PUT、PATCH 和 DELETE 請求中,都需要在表單或 AJAX 請求中包含這個 CSRF 令牌,以便伺服器可以驗證請求的合法性。
AD:首頁 | 一個覆蓋廣泛主題工具的高效線上平臺
在 Django 中,可以透過以下幾種方式獲取 CSRF 令牌:
-
在 HTML 模板中,使用
{% csrf_token %}
標籤,在表單中插入 CSRF 令牌。<form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">提交</button> </form>
-
在 AJAX 請求中,可以從
csrfmiddlewaretoken
的 cookie 中獲取 CSRF 令牌,並在請求頭中新增X-CSRFToken
欄位。function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = cookies[i].trim(); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken')); } } });
-
在 Django 檢視函式中,可以使用
request.META
獲取 CSRF 令牌,並在請求中驗證它的合法性。from django.middleware.csrf import get_token def my_view(request): token = get_token(request) # ... if request.method == 'POST': # ... if not request.is_csrf_token_valid(): return HttpResponseBadRequest('Invalid CSRF token.')
Django 的 CSRF 保護機制可以幫助開發人員快速實現安全的 Web 應用,但是也需要注意一些問題,例如在使用 AJAX 請求時,需要確保請求頭中包含了 CSRF 令牌,否則伺服器會拒絕處理該請求。同時,在使用 CSRF 令牌時,也需要注意防止 CSRF 令牌被洩露,例如在表單中使用 HTTP GET 方法時,需要注意 CSRF 令牌的隱藏性。
AD:專業搜尋引擎
總之,Django 的 CSRF 保護機制是一個強大的工具,可以幫助開發人員快速實現安全的 Web 應用,但是也需要注意一些問題,以確保 CSRF 令牌的安全性。
跨站指令碼(XSS)
跨站指令碼(XSS,Cross-site Scripting)攻擊是一種常見的網路安全威脅,攻擊者透過注入惡意指令碼到使用者的瀏覽器中,來竊取使用者的敏感資訊或者執行非授權操作。Django 提供了一套內建的安全特性來幫助防止 XSS 攻擊,其中包括過濾器(filters)和模板標籤(template tags)。
-
內建過濾器: Django 的模板引擎(如 Django 的
{{ }}
模板標籤)提供了safe
過濾器,用於標記字串為安全的,不會進行 HTML 實體轉義。當需要在模板中顯示使用者輸入的內容,但不想進行轉義時,可以使用safe
過濾器。<p>{{ user_input|safe }}</p>
如果
user_input
可能包含惡意指令碼,你需要確保它是可信的,或者在輸出之前進行適當的清理和驗證。 -
模板標籤: Django 提供了
safe
標籤,可以將整個塊標記為安全,不會進行轉義。{% autoescape off %} <p>{{ user_input|safe }}</p> {% endautoescape %}
這裡
autoescape off
指令關閉了模板的自動轉義功能,如果在塊內部使用safe
標籤,可以確保使用者輸入不會被轉義。 -
Content Security Policy (CSP) : Django 的
django.middleware.clickjacking.XSSMiddleware
和django.middleware.security.SecurityMiddleware
中包含了 Content Security Policy 的支援,可以限制頁面可以載入的內容來源,防止惡意指令碼的執行。 -
HTML5 模式: Django 的
X_FRAME_OPTIONS
設定可以控制頁面是否可以嵌入到其他頁面中,防止點選劫持(Clickjacking)攻擊,這是一種變相的 XSS 攻擊。 -
輸入驗證: 在接收使用者輸入時,始終進行適當的驗證和清理,確保資料的格式和內容符合預期,避免惡意指令碼的注入。
儘管 Django 提供了這些內建的保護機制,但開發人員仍然需要保持警惕,因為攻擊者可能會使用各種手段繞過這些防禦。在處理使用者輸入時,始終遵循“最小許可權原則”,只允許必要的資料和功能,並且在必要時使用第三方庫(如 django-csp
或 django-xss-filter
)進行額外的安全增強。
SQL隱碼攻擊
Django 使用 Object-Relational Mapping (ORM) 技術,可以有效幫助開發人員避免 SQL 注入攻擊。ORM 是一種在應用程式中使用高階程式語言(如 Python)來運算元據庫的方法,它可以將 SQL 語句的構造轉移到框架內部,從而減少直接編寫 SQL 語句的需求。
AD:漫畫首頁
Django 的 ORM 將引數化查詢作為預設行為,這意味著在構造 SQL 語句時,使用者提供的資料會被自動轉義,避免了直接將使用者輸入拼接到 SQL 語句中,這是 SQL 注入攻擊的主要入口。
以下是使用 Django ORM 時應該遵循的安全最佳實踐:
-
使用 ORM 而不是原生 SQL:儘可能地使用 Django ORM 來運算元據庫,而不是直接編寫原生 SQL 語句。ORM 會幫助你自動轉義使用者輸入,避免 SQL 注入攻擊。
-
使用引數化查詢:當需要使用原生 SQL 時,始終使用引數化查詢,避免將使用者輸入直接拼接到 SQL 語句中。例如,使用 Django 的
execute
方法:from django.db import connection with connection.cursor() as cursor: cursor.execute("SELECT * FROM myapp_model WHERE id = %s", [user_id]) result = cursor.fetchone()
這裡,
%s
是一個佔位符,[user_id]
是一個列表,其中包含使用者輸入的資料,ORM 會自動將其轉義,避免 SQL 注入攻擊。 -
使用預定義的查詢:使用 Django ORM 提供的查詢方法,如
get
、filter
、exclude
等,而不是直接使用原生的 SQL 查詢。這些查詢方法也會自動轉義使用者輸入,避免 SQL 注入攻擊。 -
輸入驗證:在接收使用者輸入時,始終進行適當的驗證和清理,確保資料的格式和內容符合預期,避免惡意輸入。
雖然 Django ORM 可以有效幫助開發人員避免 SQL 注入攻擊,但不能完全消除這種風險。因此,在處理使用者輸入時,始終應該遵循“最小許可權原則”,只允許必要的資料和功能,並在必要時使用第三方庫(如 django-sql-security
)進行額外的安全增強。
檔案上傳攻擊
Django 提供了一些內建的安全特性來幫助處理檔案上傳,以減少檔案上傳攻擊的風險。以下是一些關鍵的安全措施和最佳實踐:
-
檔案儲存和路徑安全:
- 避免使用使用者提供的檔名:不要直接使用使用者上傳的檔名來儲存檔案,因為這可能導致路徑遍歷攻擊。應該生成一個隨機的檔名,並確保檔案儲存在安全的目錄中。
- 限制檔案儲存位置:確保檔案儲存在應用程式的受控目錄中,避免將檔案儲存在可由Web伺服器直接訪問的位置,這樣可以防止直接訪問上傳的檔案。
-
檔案型別和大小限制:
- 檢查檔案型別:使用
mimetype
、content_type
或檔案的副檔名來驗證檔案型別,確保只接受預期的檔案型別。 - 限制檔案大小:在
settings.py
中設定FILE_UPLOAD_MAX_MEMORY_SIZE
和FILE_UPLOAD_MAX_NUMBER_PER_FIELD
來限制單個檔案上傳的大小和每個表單欄位可以上傳的檔案數量。
- 檢查檔案型別:使用
-
檔案內容驗證:
- 檢查檔案內容:對於某些檔案型別(如影像),可以使用庫(如
PIL
)來檢查檔案內容是否符合預期格式,以防止嵌入惡意程式碼。
- 檢查檔案內容:對於某些檔案型別(如影像),可以使用庫(如
-
使用 Django 的
FileField
和ImageField
:- 這些欄位型別提供了內建的驗證,可以檢查檔案的
mimetype
和大小。
- 這些欄位型別提供了內建的驗證,可以檢查檔案的
-
安全處理上傳的檔案:
- 不要執行不可信的檔案:永遠不要在伺服器上執行使用者上傳的檔案,這可能導致程式碼執行攻擊。
- 隔離上傳檔案:如果可能,將上傳的檔案儲存在隔離的環境中,以減少潛在的安全風險。
-
使用 Django 的中介軟體和檢視:
- Django 的中介軟體可以用來在檔案上傳到檢視之前進行額外的安全檢查。
- 使用 Django 的檢視裝飾器,如
@login_required
,來確保只有認證使用者才能上傳檔案。
-
定期更新和安全審計:
- 定期更新 Django 和所有依賴庫,以確保使用最新的安全修復。
- 進行安全審計,檢查檔案上傳功能是否存在潛在的安全漏洞。
透過遵循這些最佳實踐,可以大大降低檔案上傳攻擊的風險。然而,安全是一個持續的過程,需要不斷地評估和改進。
HTTPOnly cookie
Django 框架支援 HTTPOnly cookie,這是一種有助於提高網站安全性的措施。HTTPOnly cookie 是一種特殊的 cookie,它透過在設定 cookie 時新增 HttpOnly
標誌來實現。這個標誌告訴瀏覽器,該 cookie 不應該透過客戶端指令碼(如 JavaScript)訪問。
以下是 HTTPOnly cookie 的一些關鍵點:
-
防止 XSS 攻擊: HTTPOnly cookie 可以防止跨站指令碼(XSS)攻擊,因為攻擊者無法透過注入惡意指令碼來讀取使用者的 cookie。這有助於保護使用者的會話資訊不被竊取。
-
增強會話安全: 當使用者登入到一個網站時,伺服器通常會建立一個會話 cookie,用於在後續請求中識別使用者。如果這個 cookie 是 HTTPOnly 的,那麼即使網站存在 XSS 漏洞,攻擊者也無法透過 JavaScript 獲取這個 cookie。
-
Django 中的設定: Django 預設會為 session cookie 和 CSRF token cookie 啟用 HTTPOnly 標誌。你可以在 Django 的設定檔案
settings.py
中找到以下配置:SESSION_COOKIE_HTTPONLY = True CSRF_COOKIE_HTTPONLY = True
這些設定確保了 Django 生成的 session cookie 和 CSRF token cookie 都是 HTTPOnly 的。
-
手動設定 HTTPOnly cookie: 如果你需要在 Django 檢視中手動設定 cookie,並且希望它是 HTTPOnly 的,你可以這樣做:
response = HttpResponse() response.set_cookie('my_cookie', 'value', httponly=True)
在這個例子中,
my_cookie
將被設定為 HTTPOnly cookie。
雖然 HTTPOnly cookie 提供了額外的安全層,但它並不能完全防止所有型別的攻擊。例如,它不能防止中間人攻擊或透過其他方式(如網路嗅探)獲取 cookie。因此,除了使用 HTTPOnly cookie 之外,還應該採取其他安全措施,如使用 HTTPS、實施內容安全策略(CSP)等,以進一步提高網站的安全性。
密碼安全性
Django 提供了內建的安全密碼儲存功能,這是透過其內建的 django.contrib.auth
庫中的 User
模型和密碼雜湊處理機制實現的。當使用者註冊並設定密碼時,Django並不會直接儲存明文密碼,而是儲存密碼的雜湊值和一個隨機鹽值(salt)。
以下是 Django 安全密碼儲存的關鍵點:
-
雜湊演算法: Django 使用了 bcrypt 和 PBKDF2(取決於你的 Django 版本)這樣的安全雜湊演算法來加密密碼。這些演算法經過精心設計,即使攻擊者知道雜湊值,也無法輕易地透過暴力破解或彩虹表來恢復原始密碼。
-
鹽值: 每個使用者的密碼雜湊值都會與一個唯一的隨機鹽值結合,這樣即使相同的密碼,由於鹽值不同,生成的雜湊值也會不同。這進一步增加了破解的難度。
-
set_password()
方法: 當使用者設定密碼時,Django 提供了set_password()
方法,它會自動處理密碼的雜湊和鹽值生成。示例程式碼如下:user = User.objects.create_user(username='myuser', password='mypassword') user.set_password('mypassword') user.save()
-
驗證密碼: 當使用者嘗試登入時,Django 會計算他們提供的密碼與資料庫中儲存的雜湊值和鹽值的匹配。這透過
authenticate()
函式完成,而不是直接比較密碼。 -
check_password()
方法: 為了驗證密碼,可以使用check_password()
方法,如:if user.check_password('mynewpassword'): # 密碼正確 else: # 密碼錯誤
透過這種方式,Django 有效地保護了使用者的密碼,即使資料庫被洩露,攻擊者也無法直接獲取到使用者的密碼,從而提高了安全性。
安全會話
Django 使用加密和簽名的方式來保護會話資料,以確保會話的安全性。下面是 Django 中安全會話的實現方式:
- 加密會話資料: Django 預設會將會話資料加密後儲存在使用者的瀏覽器中。這樣即使使用者可以檢視瀏覽器的 cookie 資料,也無法直接讀取和理解其中的內容。Django 使用金鑰來加密和解密會話資料,確保資料的機密性。
- 簽名會話資料: 除了加密資料外,Django 還會對會話資料進行簽名。簽名是透過使用金鑰和雜湊演算法來生成一個簽名值,用於驗證資料的完整性和真實性。如果會話資料在傳輸過程中被篡改,簽名驗證將失敗,從而防止資料被篡改。
- SESSION_ENGINE 設定: 在 Django 的設定檔案中,可以透過
SESSION_ENGINE
設定來選擇會話引擎。預設情況下,Django 使用django.contrib.sessions.backends.db
作為會話引擎,將加密和簽名的會話資料儲存在資料庫中。也可以選擇其他會話引擎,如django.contrib.sessions.backends.cache
或django.contrib.sessions.backends.file
。 - SESSION_COOKIE_SECURE 設定: 可以透過設定
SESSION_COOKIE_SECURE = True
來確保會話 cookie 只能透過 HTTPS 連線傳輸,增加會話資料的安全性。 - SESSION_COOKIE_HTTPONLY 設定: 同樣可以透過設定
SESSION_COOKIE_HTTPONLY = True
來禁止 JavaScript 訪問會話 cookie,減少 XSS 攻擊的可能性。
透過加密和簽名會話資料,Django 確保了使用者的會話資訊在傳輸和儲存過程中的安全性,防止敏感資料洩露和篡改。這是保護使用者隱私和確保系統安全的重要措施之一。