隨著網路應用程式日益複雜,安全性議題也越發重要。本文深入探討 Python 網路安全,涵蓋套件管理、YAML、XML 和 HTTP 安全防護等導向。我們將探討如何使用 Pipenv 確保套件依賴的安全性,並示範如何利用 PyYAML 和 defusedxml 安全地解析 YAML 和 XML 檔案,避免潛在的程式碼注入風險。此外,本文也將探討 HTTP 相關的攻擊,例如 DoS 攻擊、Host header 攻擊和開放重定向攻擊,並提供相應的防禦策略與最佳實務,以提升 Python 網路應用程式的安全性。

shell 引數

在某些情況下,可能需要使用 shell 來執行命令。subprocess 模組提供了一個 shell 引數,可以用來啟用 shell 模式。然而,啟用 shell 模式會增加安全風險,因為它允許攻擊者提交惡意命令。

執行命令的安全性

執行命令的安全性取決於多個因素,包括命令本身、命令的引數以及執行命令的環境。為了確保安全性,應該盡可能避免使用 shell 模式,並且應該驗證所有的輸入資料。

13 不要相信輸入

本章涵蓋以下主題:

  • 使用 Pipenv 驗證 Python 依賴項
  • 使用 PyYAML 安全地解析 YAML
  • 使用 defusedxml 安全地解析 XML
  • 防止 DoS 攻擊、Host header 攻擊、開放重定向和 SQL 注入

在本章中,Mallory 對 Alice、Bob 和 Charlie 進行了一系列攻擊。這些攻擊和其對策並不複雜,每個攻擊都遵循一個模式:Mallory 使用惡意輸入來濫用系統或使用者。這些攻擊可以以多種形式出現,包括套件依賴項、YAML、XML、HTTP 和 SQL。這些攻擊的目標包括資料損壞、許可權提升和未經授權的資料存取。輸入驗證是預防這些攻擊的解藥。

13.1 使用 Pipenv 進行套件管理

在本節中,我將向您展示如何使用 Pipenv 防止注入攻擊。雜湊和資料完整性等主題將再次出現。像任何 Python 套件管理器一樣,Pipenv 從套件倉函式庫(如 PyPI)檢索和安裝第三方套件。然而,程式設計師們不幸地忽略了這一點:當您使用 Pipenv 安裝套件時,您實際上是在信任套件的作者。

包管理安全:抵禦惡意軟體攻擊

在軟體開發中,包管理是一個非常重要的環節。開發者經常需要從包倉函式庫中下載和安裝各種依賴包,以便實作特定的功能。然而,這個過程也存在著安全風險。假設有一個惡意的攻擊者 Mallory,他可以攻擊包倉函式庫,修改依賴包中的程式碼,然後將惡意程式碼下載到開發者的本地環境中。

包管理安全威脅

包管理安全威脅是一種常見的安全問題。攻擊者可以透過以下方式實作惡意攻擊:

  1. 包倉函式庫攻擊:攻擊者可以攻擊包倉函式庫,修改依賴包中的程式碼,然後將惡意程式碼下載到開發者的本地環境中。
  2. 依賴包攻擊:攻擊者可以修改依賴包中的程式碼,然後將惡意程式碼下載到開發者的本地環境中。

Pipenv 的安全機制

Pipenv 是一個 Python 的包管理工具,它提供了一些安全機制來防止惡意攻擊。其中包括:

  1. 包完整性驗證:Pipenv 可以驗證包的完整性,確保包沒有被修改過。
  2. 雜湊值驗證:Pipenv 可以驗證包的雜湊值,確保包的內容沒有被修改過。

Pipenv 的雜湊值驗證機制

Pipenv 的雜湊值驗證機制是透過以下步驟實作的:

  1. 第一次下載包:當 Pipenv第一次下載一個包時,它會計算包的雜湊值,並將雜湊值儲存到 Pipfile.lock 檔案中。
  2. 後續下載包:當 Pipenv 後續下載同一個包時,它會計算包的雜湊值,並將雜湊值與 Pipfile.lock 檔案中的雜湊值進行比較。如果雜湊值匹配,則 Pipenv 可以確保包沒有被修改過。

示例

以下是 Pipenv 的雜湊值驗證機制的示例:

$ pipenv install
Installing dependencies from Pipfile.lock
An error occurred while installing requests==2.24.0
  --hash=sha256:b3559a131db72c33ee969480840fff4bb6dd1117c8...
  --hash=sha256:fe75cc94a9443b9246fc7049224f756046acb93f87...
...
[pipenv.exceptions.InstallError]: ['ERROR: THESE PACKAGES DO NOT
  MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated
  the package versions, please update the hashes. Otherwise,
  examine the package contents carefully; someone may have
  tampered with them.']

在這個示例中,Pipenv 下載了 requests 包,並計算了包的雜湊值。然後,它將雜湊值與 Pipfile.lock 檔案中的雜湊值進行比較。如果雜湊值不匹配,Pipenv 會報錯,並提示使用者檢查包的內容。

圖表翻譯:

  graph LR
    A[包倉函式庫] -->|攻擊|> B[惡意攻擊者]
    B -->|修改依賴包|> C[依賴包]
    C -->|下載|> D[開發者本地環境]
    D -->|驗證|> E[Pipenv]
    E -->|雜湊值驗證|> F[包完整性驗證]
    F -->|透過|> G[包安裝]
    F -->|不透過|> H[報錯]

這個圖表展示了 Pipenv 的包管理安全機制,包括包倉函式庫攻擊、依賴包攻擊、包完整性驗證和雜湊值驗證等步驟。

YAML 遠端程式碼執行攻擊

在第 7 章中,您看到 Mallory 進行了一次遠端程式碼執行攻擊。她首先將惡意程式碼嵌入到序列化的 Python 物件中,然後將其偽裝成根據 Cookie 的 HTTP 會話狀態並傳送到伺服器。伺服器接收到這個請求後,不經意地執行了惡意程式碼。這次,我們將探討如何使用 YAML 格式來進行類似的攻擊。

安全性注意事項

在撰寫本文時,非安全的反序列化是目前網路安全領域中的一個重大問題。YAML、JSON、CSV 和 XML 都是常見的用於表示資料的格式,每種程式語言都有工具可以解析、序列化和反序列化這些格式。Python 程式設計師經常使用 PyYAML 來解析 YAML。

安裝 PyYAML

要使用 PyYAML,首先需要安裝它。您可以在虛擬環境中使用 pipenv 來安裝 PyYAML:

$ pipenv install pyyaml

解析 YAML 檔案

接下來,讓我們在 Python 中解析一個 YAML 檔案。以下是使用 PyYAML 解析 YAML 檔案的範例:

import yaml

# 定義 YAML 檔案內容
document = """
title: 全面掌握 Python 安全
characters:
  - Alice
  - Bob
  - Charlie
  - Eve
  - Mallory
"""

# 使用 BaseLoader 解析 YAML 檔案
book = yaml.load(document, Loader=yaml.BaseLoader)

# 存取 YAML 檔案中的資料
print(book['title'])  # 輸出:全面掌握 Python 安全
print(book['characters'])  # 輸出:['Alice', 'Bob', 'Charlie', 'Eve', 'Mallory']

最小許可權原則

在第 1 章中,您學習了最小許可權原則(PLP)。PLP 指出,使用者或系統應該只被授予執行其職責所需的最小許可權。在這裡,我們將展示如何將 PLP 應用於解析 YAML 檔案。

PyYAML 的 Loader

PyYAML 支援四種不同的 Loader,從最不強大到最強大:

  1. BaseLoader:支援基本的 Python 物件,如字串和列表。
  2. SafeLoader:支援基本的 Python 物件和標準 YAML 標籤。
  3. Loader:支援更多功能,但也增加了風險。
  4. FullLoader:支援所有 YAML 功能,但也存在最大的風險。

您可以使用 Loader 引數來指定 PyYAML 使用哪種 Loader 來解析 YAML 檔案。例如,以下範例使用 BaseLoader 來解析 YAML 檔案:

book = yaml.load(document, Loader=yaml.BaseLoader)

圖表翻譯:

  flowchart TD
    A[PyYAML] --> B[Loader]
    B --> C[BaseLoader]
    C --> D[SafeLoader]
    D --> E[Loader]
    E --> F[FullLoader]
    F --> G[風險增加]

在這個圖表中,我們展示了 PyYAML 的四種 Loader,從最不強大到最強大。每種 Loader 都支援更多功能,但也增加了風險。因此,選擇合適的 Loader 來解析 YAML 檔案非常重要。

XML 實體擴充套件攻擊

XML 實體擴充套件是一種小眾的 XML 特性,允許使用者定義和命名任意資料在 XML 檔案中。這種特性可以被用來實作實體參照,從而在 XML 檔案中嵌入實體。XML 解析器的工作是將實體參照擴充套件為實體。

以下是 Python 程式碼示例,展示了 XML 實體擴充套件的工作原理:

from xml.etree.ElementTree import fromstring

# 定義一個小型的 XML 檔案,包含一個實體宣告和兩個實體參照
xml_doc = '''
<root>
    <name>&alice;</name>
    <age>25</age>
    <name>&alice;</name>
</root>
'''

# 定義實體宣告
entity_decl = '''
<!ENTITY alice "Alice">
'''

# 將實體宣告和 XML 檔案合併
xml_doc = entity_decl + xml_doc

# 解析 XML 檔案
root = fromstring(xml_doc)

# 印出 XML 檔案中的資料
print(root.find('name').text)  # 輸出:Alice
print(root.find('age').text)    # 輸出:25
print(root.find('.//name[2]').text)  # 輸出:Alice

在這個例子中,我們定義了一個小型的 XML 檔案,包含一個實體宣告 &alice;,它參照了一個名為 alice 的實體。XML 檔案中包含兩個實體參照 &alice;,它們會被解析器擴充套件為實體 alice

實體擴充套件攻擊

實體擴充套件攻擊是指攻擊者利用 XML 實體擴充套件特性,將大量資料嵌入 XML 檔案中,從而導致系統耗盡記憶體。這種攻擊方式可以用來實作拒絕服務(DoS)攻擊。

以下是 Python 程式碼示例,展示了實體擴充套件攻擊的工作原理:

from xml.etree.ElementTree import fromstring

# 定義一個小型的 XML 檔案,包含一個實體宣告和多個實體參照
xml_doc = '''
<root>
    <name>&alice;</name>
    <age>25</age>
    <name>&alice;</name>
    <name>&alice;</name>
    ...
</root>
'''

# 定義實體宣告
entity_decl = '''
<!ENTITY alice "Alice">
'''

# 將實體宣告和 XML 檔案合併
xml_doc = entity_decl + xml_doc

# 解析 XML 檔案
root = fromstring(xml_doc)

# 印出 XML 檔案中的資料
print(root.find('name').text)  # 輸出:Alice
print(root.find('age').text)    # 輸出:25
print(root.find('.//name[2]').text)  # 輸出:Alice

在這個例子中,我們定義了一個小型的 XML 檔案,包含一個實體宣告 &alice;,它參照了一個名為 alice 的實體。XML 檔案中包含多個實體參照 &alice;,它們會被解析器擴充套件為實體 alice。這會導致系統耗盡記憶體。

圖表翻譯:

  graph LR
    A[XML 檔案] --> B[實體宣告]
    B --> C[實體參照]
    C --> D[解析器]
    D --> E[記憶體耗盡]
    E --> F[拒絕服務攻擊]

這個圖表展示了實體擴充套件攻擊的工作原理。XML 檔案包含實體宣告和實體參照,解析器會將實體參照擴充套件為實體,從而導致記憶體耗盡和拒絕服務攻擊。

XML 實體擴充套件攻擊

XML 實體擴充套件是一種功能,允許開發人員定義實體並在 XML 檔案中參照它們。然而,這種功能也可以被用來對系統進行攻擊,尤其是在實體定義過大或被過多次參照的情況下。

XML 實體擴充套件示例

以下是一個簡單的 XML 檔案,示範了實體擴充套件的功能:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY a "Alice">
]>
<root>&a;&a;</root>

在這個例子中,實體 a 被定義為 "Alice",然後在 root 元素中被參照兩次。解析這個 XML 檔案後,實體會被擴充套件,結果為 "AliceAlice"

二次爆炸攻擊

二次爆炸攻擊是一種利用 XML 實體擴充套件功能對系統進行攻擊的方法。攻擊者可以建立一個 XML 檔案,其中包含一個大型實體和多個對該實體的參照。當系統解析這個 XML 檔案時,實體會被擴充套件,導致系統記憶體使用量急劇增加。

以下是一個示例:

<!DOCTYPE bomb [
  <!ENTITY e "a loooooooooooooooooooooooooong entity ...">
]>
<bomb>&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;</bomb>

在這個例子中,實體 e 被定義為一個長字串,然後在 bomb 元素中被參照 10 次。如果實體 e 的大小為 1 MB,則這個 XML 檔案會被擴充套件為 10 MB。

防禦措施

為了防禦二次爆炸攻擊,系統可以實施以下措施:

  1. 限制 XML 檔案的大小:系統可以設定一個最大允許的 XML 檔案大小,超過這個大小的檔案將被拒絕。
  2. 限制實體的大小:系統可以設定一個最大允許的實體大小,超過這個大小的實體將被拒絕。
  3. 限制實體的參照次數:系統可以設定一個最大允許的實體參照次數,超過這個次數的參照將被拒絕。
  4. 使用安全的 XML 解析器:系統可以使用安全的 XML 解析器,例如那些具有實體擴充套件限制的解析器。

XML攻擊:億笑攻擊和防禦

XML(Extensible Markup Language)是一種用於描述和交換資料的標記語言。然而,XML也可能被用於惡意目的,例如億笑攻擊(Billion Laughs Attack)。這種攻擊是透過建立一個包含大量巢狀實體的XML檔案,從而導致XML解析器耗盡記憶體資源。

億笑攻擊

億笑攻擊是一種特殊的XML攻擊,透過建立一個包含多級巢狀實體的XML檔案,從而導致XML解析器耗盡記憶體資源。以下是一個例子:

<!DOCTYPE bomb [
  <!ENTITY a "lol">
  <!ENTITY b "&a;&a;&a;&a;&a;&a;&a;&a;&a;&a;">
  <!ENTITY c "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;">
  <!ENTITY d "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;">
]>
<bomb>&d;</bomb>

這個XML檔案包含四個巢狀實體:abcd。每個實體都包含多個參照其他實體的例項。當XML解析器嘗試解析這個檔案時,它會不斷地展開這些實體,從而導致記憶體資源耗盡。

防禦億笑攻擊

要防禦億笑攻擊,可以使用一個叫做defusedxml的Python函式庫。這個函式庫提供了一個安全的XML解析器,可以防止億笑攻擊。以下是一個例子:

from defusedxml.ElementTree import parse

try:
    parse('/path/to/billion_laughs.xml')
except EntitiesForbidden:
    print("億笑攻擊被防禦")

在這個例子中,我們使用defusedxml函式庫的parse函式來解析XML檔案。如果檔案包含巢狀實體,parse函式會引發一個EntitiesForbidden異常。

Python XML API 和 defusedxml 替代品

以下是Python的XML API和defusedxml的替代品:

Native Python APIdefusedxml API
xml.dom.minidom.parsedefusedxml.minidom.parse
xml.dom.pulldom.parsedefusedxml.pulldom.parse
xml.sax.parsedefusedxml.sax.parse
xml.etree.ElementTree.parsedefusedxml.ElementTree.parse

使用defusedxml函式庫可以有效地防禦億笑攻擊和其他類似的XML攻擊。

服務拒絕攻擊(DoS)簡介

服務拒絕攻擊(Denial of Service,DoS)是一種設計用來使系統不堪負荷的攻擊,通常是透過大量消耗系統資源來實作的。這些資源包括儲存空間、網路頻寬、CPU等。DoS攻擊的目的是透過使系統過載來拒絕使用者存取服務。

DoS攻擊的型別

DoS攻擊有很多種類,包括但不限於以下幾種:

  • 網路流量攻擊:透過向系統傳送大量網路流量來使系統不堪負荷。
  • 儲存空間攻擊:透過向系統傳送大量資料來使儲存空間不足。
  • CPU攻擊:透過向系統傳送大量計算任務來使CPU過載。

DoS攻擊的防禦

DoS攻擊的防禦需要從多個層面入手,包括:

  • 網路層面:使用防火牆、入侵檢測系統等網路安全裝置來過濾和阻止惡意流量。
  • 應用層面:使用應用層面的安全措施,例如設定請求引數限制、請求體大小限制等。

Django中的DoS攻擊防禦設定

Django提供了一些設定來幫助防禦DoS攻擊,包括:

  • DATA_UPLOAD_MAX_NUMBER_FIELDS:設定最大允許的請求引數數量。
  • DATA_UPLOAD_MAX_MEMORY_SIZE:設定最大允許的請求體大小(以位元組為單位)。

以下是Django中相關設定的示例:

# settings.py
DATA_UPLOAD_MAX_NUMBER_FIELDS = 1000
DATA_UPLOAD_MAX_MEMORY_SIZE = 1024 * 1024  # 1MB

防禦DoS攻擊的最佳實踐

  • 監控系統:實時監控系統的效能和安全狀態。
  • 設定安全措施:設定合適的安全措施,例如防火牆、入侵檢測系統等。
  • 更新和維護:定期更新和維護系統和應用程式,以確保安全性和效能。

圖表翻譯:

  flowchart TD
    A[DoS攻擊] --> B[網路流量攻擊]
    A --> C[儲存空間攻擊]
    A --> D[CPU攻擊]
    B --> E[防火牆]
    C --> F[儲存空間限制]
    D --> G[CPU限制]

在這個圖表中,我們可以看到DoS攻擊的不同型別和對應的防禦措施。透過設定合適的安全措施和監控系統,我們可以有效地防禦DoS攻擊。

網路安全:抵禦 HTTP 型別的 DoS 攻擊

在網路安全中,抵禦 HTTP 型別的 DoS 攻擊是一個重要的議題。DoS 攻擊(Denial of Service)是指惡意的攻擊者嘗試使網站或服務無法正常運作,通常是透過大量的請求或資料傳輸來達成。以下將介紹一些 Gunicorn 引數,來幫助抵禦這型別的攻擊。

Gunicorn 引數

Gunicorn 提供了多個引數來設定 HTTP 請求的限制,以下是幾個重要的引數:

  • limit-request-line:設定 HTTP 請求行的大小限制,包括 HTTP 方法、協定版本和 URL。預設值為 4094,最大值為 8190。設定為 0 可以停用這個限制。
  • limit-request-fields:設定 HTTP 請求中允許的最大 header 數量。預設值為 100,最大值為 32768。
  • limit-request-field_size:設定 HTTP header 的最大允許大小。預設值為 8190,設定為 0 可以允許 header 的大小不受限制。

Host Header 攻擊

Host Header 攻擊是一種利用 HTTP 的 Host Header 欄位來進行的攻擊。惡意的攻擊者可以透過設定 Host Header 的值來使得網站將請求轉發到錯誤的位置,從而可能導致安全漏洞。

防禦措施

為了防禦這型別的攻擊,需要設定正確的 Gunicorn 引數,並確保網站的安全設定。以下是一些防禦措施:

  • 設定 limit-request-linelimit-request-fieldslimit-request-field_size 引數,來限制 HTTP 請求的大小和 header 數量。
  • 確保網站的安全設定,例如設定正確的 Host Header 欄位,來防禦 Host Header 攻擊。
  • 使用安全的網站框架和函式庫,來防禦常見的安全漏洞。
內容解密:

以上內容介紹了 Gunicorn 引數和 Host Header 攻擊的防禦措施。透過設定正確的 Gunicorn 引數和網站安全設定,可以有效地防禦 HTTP 型別的 DoS 攻擊和 Host Header 攻擊。這些設定可以限制 HTTP 請求的大小和 header 數量,從而防禦惡意的攻擊。

圖表翻譯:

以下是 Gunicorn 引數和 Host Header 攻擊的流程圖:

  flowchart TD
    A[HTTP 請求] --> B[Gunicorn 引數]
    B --> C[限制請求大小和 header 數量]
    C --> D[防禦 DoS 攻擊]
    D --> E[防禦 Host Header 攻擊]
    E --> F[設定正確的 Host Header 欄位]
    F --> G[確保網站安全設定]

這個流程圖顯示了 Gunicorn 引數和 Host Header 攻擊的防禦措施的流程。透過設定正確的 Gunicorn 引數和網站安全設定,可以有效地防禦 HTTP 型別的 DoS 攻擊和 Host Header 攻擊。

網站安全:Host Header 驗證和 Open Redirect 攻擊

Host Header 驗證

在網站開發中,Host Header 是用於指定請求的主機名稱。然而,直接存取 Host Header 可能會導致安全風險。為了避免這種風險,Django 提供了一個 get_host 方法來驗證和擷取 Host Header。

以下是直接存取 Host Header 的壞實踐:

bad_practice = request.META['HTTP_HOST']

這種做法會繞過輸入驗證,可能導致安全風險。

以下是使用 get_host 方法的好實踐:

good_practice = request.get_host()

這種做法會驗證 Host Header,確保主機名稱是有效的。

ALLOWED_HOSTS 設定

Django 提供了一個 ALLOWED_HOSTS 設定,允許您指定哪些主機名稱可以存取您的網站。這個設定是用於防止 Host Header 攻擊。

以下是 ALLOWED_HOSTS 設定的範例:

ALLOWED_HOSTS = ['example.com', 'www.example.com']

您可以動態地從公鑰證書中擷取主機名稱,然後將其新增到 ALLOWED_HOSTS 設定中。以下是使用 cryptography 套件的範例:

from cryptography.hazmat.backends import default_backend
from cryptography.x509.oid import NameOID

with open(CERTIFICATE_PATH, 'rb') as f:
    cert = default_backend().load_pem_x509_certificate(f.read())

ALLOWED_HOSTS = [a.value for a in cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)]

Open Redirect 攻擊

Open Redirect 攻擊是一種網站安全風險,攻擊者可以將使用者重定向到惡意網站。以下是 Open Redirect 攻擊的範例:

假設 Mallory 想要偷取 Bob 的錢,她可以建立一個網站,模仿 Alice 的線上銀行網站。然後,她可以將 Bob 重定向到她的網站,讓 Bob 輸入他的登入憑證。Mallory 的網站可以使用 Bob 的憑證來存取他的帳戶。

為了防止 Open Redirect 攻擊,網站開發者必須小心地處理重定向。以下是使用 Django 的 redirect 函式的範例:

from django.shortcuts import redirect

def my_view(request):
    # ...
    return redirect('https://example.com')

在這個範例中,redirect 函式會將使用者重定向到 https://example.com。然而,如果攻擊者可以控制重定向的 URL,則可能會導致 Open Redirect 攻擊。

為了防止這種風險,網站開發者可以使用 is_safe_url 函式來驗證重定向的 URL。以下是使用 is_safe_url 函式的範例:

from django.utils.http import is_safe_url

def my_view(request):
    # ...
    url = 'https://example.com'
    if is_safe_url(url):
        return redirect(url)
    else:
        # ...

在這個範例中,is_safe_url 函式會驗證重定向的 URL 是否安全。如果 URL 不安全,則會傳回錯誤。

網站安全:抵禦開放重定向攻擊

開放重定向攻擊是一種常見的網路安全威脅,攻擊者可以利用這種漏洞將使用者重定向到惡意網站,以竊取使用者的敏感資訊。這種攻擊通常是透過在網站的URL中加入惡意的重定向引數實作的。

深入剖析網路安全議題,從底層的套件管理到高階的 HTTP 請求,本篇探討了確保應用程式安全的關鍵環節。分析顯示,無論是使用 Pipenv 管理套件、解析 YAML 和 XML 檔案,或是處理 HTTP 請求和重定向,輸入驗證都是抵禦惡意攻擊的第一道防線。忽略輸入驗證的後果,輕則導致程式當機,重則可能造成資料洩露和系統癱瘓。技術堆疊的各層級協同運作中體現,縱深防禦策略的重要性不容忽視。對於重視長期穩定性的企業,採取多層級的防禦策略,包括使用安全的解析器、設定資源限制、驗證所有使用者輸入,並定期更新安全策略,將帶來最佳平衡。玄貓認為,安全意識的提升和最佳實務的落實,才是構建安全可靠應用程式的基本。隨著攻擊手段日趨複雜,持續學習和更新安全知識,才能在不斷變化的威脅態勢中保持領先。