Django 安全性與防禦性程式設計:如何保護 Django Web 應用

Amd794發表於2024-05-13

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

image

跨站請求偽造(CSRF)

跨站請求偽造(CSRF)是一種常見的網路攻擊,它利用使用者的身份和許可權,欺騙伺服器執行非預期的操作。Django 提供了一種內建的 CSRF 保護機制,可以幫助保護應用免受 CSRF 攻擊。

Django 的 CSRF 保護機制是透過 CSRF 令牌(CSRF Token)實現的,它是一個加密字串,包含了一些關於使用者會話和請求的資訊。在每個 POST、PUT、PATCH 和 DELETE 請求中,都需要在表單或 AJAX 請求中包含這個 CSRF 令牌,以便伺服器可以驗證請求的合法性。
AD:首頁 | 一個覆蓋廣泛主題工具的高效線上平臺
在 Django 中,可以透過以下幾種方式獲取 CSRF 令牌:

  1. 在 HTML 模板中,使用 {% csrf_token %} 標籤,在表單中插入 CSRF 令牌。

    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">提交</button>
    </form>
    
  2. 在 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'));
            }
        }
    });
    
  3. 在 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)。

  1. 內建過濾器: Django 的模板引擎(如 Django 的 {{ }} 模板標籤)提供了 safe 過濾器,用於標記字串為安全的,不會進行 HTML 實體轉義。當需要在模板中顯示使用者輸入的內容,但不想進行轉義時,可以使用 safe 過濾器。

    <p>{{ user_input|safe }}</p>
    

    如果 user_input 可能包含惡意指令碼,你需要確保它是可信的,或者在輸出之前進行適當的清理和驗證。

  2. 模板標籤: Django 提供了 safe 標籤,可以將整個塊標記為安全,不會進行轉義。

    {% autoescape off %}
    <p>{{ user_input|safe }}</p>
    {% endautoescape %}
    

    這裡 autoescape off 指令關閉了模板的自動轉義功能,如果在塊內部使用 safe 標籤,可以確保使用者輸入不會被轉義。

  3. Content Security Policy (CSP) : Django 的 django.middleware.clickjacking.XSSMiddlewaredjango.middleware.security.SecurityMiddleware 中包含了 Content Security Policy 的支援,可以限制頁面可以載入的內容來源,防止惡意指令碼的執行。

  4. HTML5 模式: Django 的 X_FRAME_OPTIONS 設定可以控制頁面是否可以嵌入到其他頁面中,防止點選劫持(Clickjacking)攻擊,這是一種變相的 XSS 攻擊。

  5. 輸入驗證: 在接收使用者輸入時,始終進行適當的驗證和清理,確保資料的格式和內容符合預期,避免惡意指令碼的注入。

儘管 Django 提供了這些內建的保護機制,但開發人員仍然需要保持警惕,因為攻擊者可能會使用各種手段繞過這些防禦。在處理使用者輸入時,始終遵循“最小許可權原則”,只允許必要的資料和功能,並且在必要時使用第三方庫(如 django-cspdjango-xss-filter)進行額外的安全增強。

SQL隱碼攻擊

Django 使用 Object-Relational Mapping (ORM) 技術,可以有效幫助開發人員避免 SQL 注入攻擊。ORM 是一種在應用程式中使用高階程式語言(如 Python)來運算元據庫的方法,它可以將 SQL 語句的構造轉移到框架內部,從而減少直接編寫 SQL 語句的需求。

AD:漫畫首頁
Django 的 ORM 將引數化查詢作為預設行為,這意味著在構造 SQL 語句時,使用者提供的資料會被自動轉義,避免了直接將使用者輸入拼接到 SQL 語句中,這是 SQL 注入攻擊的主要入口。

以下是使用 Django ORM 時應該遵循的安全最佳實踐:

  1. 使用 ORM 而不是原生 SQL:儘可能地使用 Django ORM 來運算元據庫,而不是直接編寫原生 SQL 語句。ORM 會幫助你自動轉義使用者輸入,避免 SQL 注入攻擊。

  2. 使用引數化查詢:當需要使用原生 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 注入攻擊。

  3. 使用預定義的查詢:使用 Django ORM 提供的查詢方法,如 getfilterexclude 等,而不是直接使用原生的 SQL 查詢。這些查詢方法也會自動轉義使用者輸入,避免 SQL 注入攻擊。

  4. 輸入驗證:在接收使用者輸入時,始終進行適當的驗證和清理,確保資料的格式和內容符合預期,避免惡意輸入。

雖然 Django ORM 可以有效幫助開發人員避免 SQL 注入攻擊,但不能完全消除這種風險。因此,在處理使用者輸入時,始終應該遵循“最小許可權原則”,只允許必要的資料和功能,並在必要時使用第三方庫(如 django-sql-security)進行額外的安全增強。

檔案上傳攻擊

Django 提供了一些內建的安全特性來幫助處理檔案上傳,以減少檔案上傳攻擊的風險。以下是一些關鍵的安全措施和最佳實踐:

  1. 檔案儲存和路徑安全

    • 避免使用使用者提供的檔名:不要直接使用使用者上傳的檔名來儲存檔案,因為這可能導致路徑遍歷攻擊。應該生成一個隨機的檔名,並確保檔案儲存在安全的目錄中。
    • 限制檔案儲存位置:確保檔案儲存在應用程式的受控目錄中,避免將檔案儲存在可由Web伺服器直接訪問的位置,這樣可以防止直接訪問上傳的檔案。
  2. 檔案型別和大小限制

    • 檢查檔案型別:使用 mimetypecontent_type 或檔案的副檔名來驗證檔案型別,確保只接受預期的檔案型別。
    • 限制檔案大小:在 settings.py 中設定 FILE_UPLOAD_MAX_MEMORY_SIZEFILE_UPLOAD_MAX_NUMBER_PER_FIELD 來限制單個檔案上傳的大小和每個表單欄位可以上傳的檔案數量。
  3. 檔案內容驗證

    • 檢查檔案內容:對於某些檔案型別(如影像),可以使用庫(如 PIL)來檢查檔案內容是否符合預期格式,以防止嵌入惡意程式碼。
  4. 使用 Django 的 FileFieldImageField

    • 這些欄位型別提供了內建的驗證,可以檢查檔案的 mimetype 和大小。
  5. 安全處理上傳的檔案

    • 不要執行不可信的檔案:永遠不要在伺服器上執行使用者上傳的檔案,這可能導致程式碼執行攻擊。
    • 隔離上傳檔案:如果可能,將上傳的檔案儲存在隔離的環境中,以減少潛在的安全風險。
  6. 使用 Django 的中介軟體和檢視

    • Django 的中介軟體可以用來在檔案上傳到檢視之前進行額外的安全檢查。
    • 使用 Django 的檢視裝飾器,如 @login_required,來確保只有認證使用者才能上傳檔案。
  7. 定期更新和安全審計

    • 定期更新 Django 和所有依賴庫,以確保使用最新的安全修復。
    • 進行安全審計,檢查檔案上傳功能是否存在潛在的安全漏洞。

透過遵循這些最佳實踐,可以大大降低檔案上傳攻擊的風險。然而,安全是一個持續的過程,需要不斷地評估和改進。

Django 框架支援 HTTPOnly cookie,這是一種有助於提高網站安全性的措施。HTTPOnly cookie 是一種特殊的 cookie,它透過在設定 cookie 時新增 HttpOnly 標誌來實現。這個標誌告訴瀏覽器,該 cookie 不應該透過客戶端指令碼(如 JavaScript)訪問。

以下是 HTTPOnly cookie 的一些關鍵點:

  1. 防止 XSS 攻擊: HTTPOnly cookie 可以防止跨站指令碼(XSS)攻擊,因為攻擊者無法透過注入惡意指令碼來讀取使用者的 cookie。這有助於保護使用者的會話資訊不被竊取。

  2. 增強會話安全: 當使用者登入到一個網站時,伺服器通常會建立一個會話 cookie,用於在後續請求中識別使用者。如果這個 cookie 是 HTTPOnly 的,那麼即使網站存在 XSS 漏洞,攻擊者也無法透過 JavaScript 獲取這個 cookie。

  3. 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 的。

  4. 手動設定 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 安全密碼儲存的關鍵點:

  1. 雜湊演算法: Django 使用了 bcrypt 和 PBKDF2(取決於你的 Django 版本)這樣的安全雜湊演算法來加密密碼。這些演算法經過精心設計,即使攻擊者知道雜湊值,也無法輕易地透過暴力破解或彩虹表來恢復原始密碼。

  2. 鹽值: 每個使用者的密碼雜湊值都會與一個唯一的隨機鹽值結合,這樣即使相同的密碼,由於鹽值不同,生成的雜湊值也會不同。這進一步增加了破解的難度。

  3. set_password() 方法: 當使用者設定密碼時,Django 提供了 set_password() 方法,它會自動處理密碼的雜湊和鹽值生成。示例程式碼如下:

    user = User.objects.create_user(username='myuser', password='mypassword')
    user.set_password('mypassword')
    user.save()
    
  4. 驗證密碼: 當使用者嘗試登入時,Django 會計算他們提供的密碼與資料庫中儲存的雜湊值和鹽值的匹配。這透過 authenticate() 函式完成,而不是直接比較密碼。

  5. check_password() 方法: 為了驗證密碼,可以使用 check_password() 方法,如:

    if user.check_password('mynewpassword'):
        # 密碼正確
    else:
        # 密碼錯誤
    

透過這種方式,Django 有效地保護了使用者的密碼,即使資料庫被洩露,攻擊者也無法直接獲取到使用者的密碼,從而提高了安全性。

安全會話

Django 使用加密和簽名的方式來保護會話資料,以確保會話的安全性。下面是 Django 中安全會話的實現方式:

  1. 加密會話資料: Django 預設會將會話資料加密後儲存在使用者的瀏覽器中。這樣即使使用者可以檢視瀏覽器的 cookie 資料,也無法直接讀取和理解其中的內容。Django 使用金鑰來加密和解密會話資料,確保資料的機密性。
  2. 簽名會話資料: 除了加密資料外,Django 還會對會話資料進行簽名。簽名是透過使用金鑰和雜湊演算法來生成一個簽名值,用於驗證資料的完整性和真實性。如果會話資料在傳輸過程中被篡改,簽名驗證將失敗,從而防止資料被篡改。
  3. SESSION_ENGINE 設定: 在 Django 的設定檔案中,可以透過 SESSION_ENGINE 設定來選擇會話引擎。預設情況下,Django 使用 django.contrib.sessions.backends.db 作為會話引擎,將加密和簽名的會話資料儲存在資料庫中。也可以選擇其他會話引擎,如 django.contrib.sessions.backends.cachedjango.contrib.sessions.backends.file
  4. SESSION_COOKIE_SECURE 設定: 可以透過設定 SESSION_COOKIE_SECURE = True 來確保會話 cookie 只能透過 HTTPS 連線傳輸,增加會話資料的安全性。
  5. SESSION_COOKIE_HTTPONLY 設定: 同樣可以透過設定 SESSION_COOKIE_HTTPONLY = True 來禁止 JavaScript 訪問會話 cookie,減少 XSS 攻擊的可能性。

透過加密和簽名會話資料,Django 確保了使用者的會話資訊在傳輸和儲存過程中的安全性,防止敏感資料洩露和篡改。這是保護使用者隱私和確保系統安全的重要措施之一。

相關文章