0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Python中的十個安全陷阱(二)

python爬蟲知識分享 ? 來源:python爬蟲知識分享 ? 作者:python爬蟲知識分享 ? 2022-03-24 16:47 ? 次閱讀

6. 不完整的正則表達式匹配

正則表達式(regex)是大多數(shù) Web 程序不可或缺的一部分。我們經(jīng)常能看到它被自定義的 Web 應(yīng)用防火墻(WAF,Web Application Firewalls)用來作輸入驗證,例如檢測惡意字符串。在 Python 中,re.match 和 re.search 之間有著細微的區(qū)別,我們將在下面的代碼片段中演示。

def is_sql_injection(request):
    pattern = re.compile(r".*(union)|(select).*")
    name_to_test = request.GET['name']
    if re.search(pattern, name_to_test):
        return True
    return False

在第 2 行中,我們定義了一個匹配 union 或者 select 的模式,以檢測可能的 SQL 注入。這是一個糟糕的寫法,因為你可以輕易地繞過這些黑名單,但我們已經(jīng)在線上的程序中見過它。在第 4 行中,函數(shù) re.match 使用前面定義好的模式,檢查第 3 行中的用戶輸入內(nèi)容是否包含這些惡意的值。

然而,與 re.search 函數(shù)不同的是,re.match 函數(shù)不匹配新行。例如,如果攻擊者提交了值 aaaaaa \n union select,這個輸入就匹配不上正則表達式。因此,檢查可以被繞過,失去保護作用。

總而言之,我們不建議使用正則表達式黑名單進行任何安全檢查。

7. Unicode 清洗器繞過

Unicode 支持用多種形式來表示字符,并將這些字符映射到碼點。在 Unicode 標準中,不同的 Unicode 字符有四種歸一化方案。程序可以使用這些歸一化方法,以獨立于人類語言的標準方式來存儲數(shù)據(jù),例如用戶名。

然而,攻擊者可以利用這些歸一化,這已經(jīng)導(dǎo)致了 Python 的 urllib 出現(xiàn)漏洞(CVE-2019-9636)。下面的代碼片段演示了一個基于 NFKC 歸一化的跨站點腳本漏洞(XSS,Cross-Site Scripting)。

import unicodedata
from django.shortcuts import render
from django.utils.html import escape

def render_input(request):
    user_input = escape(request.GET['p'])
    normalized_user_input = unicodedata.normalize("NFKC", user_input)
    context = {'my_input': normalized_user_input}
    return render(request, 'test.html', context)

在第 6 行中,用戶輸入的內(nèi)容被 Django 的 escape 函數(shù)處理了,以防止 XSS 漏洞。在第 7 行中,經(jīng)過清洗的輸入被 NFKC 算法歸一化,以便在第 8-9 行中通過 test.html 模板正確地渲染。

templates/test.html


{{ my_input | safe}}

在模板 test.html 中,第 4 行的變量 my_input 被標記為安全的,因為開發(fā)人員預(yù)期有特殊字符,并且認為該變量已經(jīng)被 escape 函數(shù)清洗了。通過標記關(guān)鍵字 safe, Django 不會再次對變量進行清洗。

但是,由于第 7 行(view.py)的歸一化,字符“%EF%B9%A4”會被轉(zhuǎn)換為“<”,“%EF%B9%A5”被轉(zhuǎn)換為“>”。這導(dǎo)致攻擊者可以注入任意的 HTML 標記,進而觸發(fā) XSS 漏洞。為了防止這個漏洞,就應(yīng)該在把用戶輸入做完歸一化之后,再進行清洗。

8. Unicode 編碼碰撞

前文說過,Unicode 字符會被映射成碼點。然而,有許多不同的人類語言,Unicode 試圖將它們統(tǒng)一起來。這就意味著不同的字符很有可能擁有相同的“l(fā)ayout”。例如,小寫的土耳其語 ?(沒有點)的字符是英語中大寫的 I。在拉丁字母中,字符 i 也是用大寫的 I 表示。在 Unicode 標準中,這兩個不同的字符都以大寫形式映射到同一個碼點。

這種行為是可以被利用的,實際上已經(jīng)在 Django 中導(dǎo)致了一個嚴重的漏洞(CVE-2019-19844)。下面的代碼是一個重置密碼的示例。

from django.core.mail import send_mail
from django.http import HttpResponse
from vuln.models import User

def reset_pw(request):
    email = request.GET['email']
    result = User.objects.filter(email__exact=email.upper()).first()
    if not result:
        return HttpResponse("User not found!")
    send_mail('Reset Password','Your new pw: 123456.', 'from@example.com', [email], fail_silently=False)
    return HttpResponse("Password reset email send!")

第 6 行代碼獲取了用戶輸入的 email,第 7-9 行代碼檢查這個 email 值,查找是否存在具有該 email 的用戶。如果用戶存在,則第 10 行代碼依據(jù)第 6 行中輸入的 email 地址,給用戶發(fā)送郵件。需要指出的是,第 7-9 行中對郵件地址的檢查是不區(qū)分大小寫的,使用了 upper 函數(shù)。

至于攻擊,我們假設(shè)數(shù)據(jù)庫中存在一個郵箱地址為 foo@mix.com 的用戶。那么,攻擊者可以簡單地傳入 foo@m?x.com 作為第 6 行中的 email,其中 i 被替換為土耳其語 ?。第 7 行代碼將郵箱轉(zhuǎn)換成大寫,結(jié)果是 FOO@MIX.COM。這意味著找到了一個用戶,因此會發(fā)送一封重置密碼的郵件。

然而,郵件被發(fā)送到第 6 行未轉(zhuǎn)換的郵件地址,也就是包含了土耳其語的 ?。換句話說,其他用戶的密碼被發(fā)送到了攻擊者控制的郵件地址。為了防止這個漏洞,可以將第 10 行替換成使用數(shù)據(jù)庫中的用戶郵箱。即使發(fā)生編碼沖突,攻擊者在這種情況下也得不到任何好處。

9. IP 地址歸一化

在 Python < 3.8 中,IP 地址會被 ipaddress 庫歸一化,因此前綴的零會被刪除。這種行為乍一看可能是無害的,但它已經(jīng)在 Django 中導(dǎo)致了一個高嚴重性的漏洞(CVE-2021-33571)。攻擊者可以利用歸一化繞過校驗程序,發(fā)起服務(wù)端請求偽造攻擊(SSRF,Server-Side Request Forgery)。

下面的代碼展示了如何繞過這樣的校驗器。

import requests
import ipaddress

def send_request(request):
    ip = request.GET['ip']
    try:
        if ip in ["127.0.0.1", "0.0.0.0"]:
            return HttpResponse("Not allowed!")
        ip = str(ipaddress.IPv4Address(ip))
    except ipaddress.AddressValueError:
        return HttpResponse("Error at validation!")
    requests.get('https://' + ip)
    return HttpResponse("Request send!")

第 5 行代碼獲取用戶傳入的一個 IP 地址,第 7 行代碼使用一個黑名單來檢查該 IP 是否為本地地址,以防止可能的 SSRF 漏洞。這份黑名單并不完整,僅作為示例。

第 9 行代碼檢查該 IP 是否為 IPv4 地址,同時將 IP 歸一化。在完成驗證后,第 12 行代碼會對該 IP 發(fā)起實際的請求。

但是,攻擊者可以傳入 127.0.001 這樣的 IP 地址,在第 7 行的黑名單列表中找不到。然后,第 9 行代碼使用 ipaddress.IPv4Address 將 IP 歸一化為 127.0.0.1。因此,攻擊者就能夠繞過 SSRF 校驗器,并向本地網(wǎng)絡(luò)地址發(fā)送請求。

10. URL 查詢參數(shù)解析

在 Python < 3.7 中,urllib.parse.parse_qsl 函數(shù)允許使用“;”和“&”字符作為 URL 的查詢變量的分隔符。有趣的是“;”字符不能被其它語言識別為分隔符。

在下面的例子中,我們將展示為什么這種行為會導(dǎo)致漏洞。假設(shè)我們正在運行一個基礎(chǔ)設(shè)施,其中前端是一個 PHP 程序,后端則是一個 Python 程序。

攻擊者向 PHP 前端發(fā)送以下的 GET 請求:

GET https://victim.com/?a=1;b=2

PHP 前端只識別出一個查詢參數(shù)“a”,其內(nèi)容為“1;b=2”。PHP 不把“;”字符作為查詢參數(shù)的分隔符?,F(xiàn)在,前端會將攻擊者的請求直接轉(zhuǎn)發(fā)給內(nèi)部的 Python 程序:

GET https://internal.backend/?a=1;b=2

如果使用了 urllib.parse.parse_qsl,Python 程序會處理成兩個查詢參數(shù),即“a=1”和“b=2”。這種查詢參數(shù)解析的差異可能會導(dǎo)致致命的安全漏洞,比如 Django 中的 Web 緩存投毒漏洞(CVE-2021-23336)。

總結(jié)

我們介紹了 10 個 Python 安全陷阱,我們認為開發(fā)者不太了解它們。每個細微的陷阱都很容易被忽視,并在過去導(dǎo)致了線上程序的安全漏洞。

正如前文所述,安全陷阱可能出現(xiàn)在各種操作中,從處理文件、目錄、壓縮文件、URL、IP 到簡單的字符串。一種常見的情況是庫函數(shù)的使用,這些函數(shù)可能有意想不到的行為。這提醒我們一定要升級到最新版本,并仔細閱讀文檔。在 SonarSource 中,我們正在研究這些缺陷,以便將來不斷改進我們的代碼分析器。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 程序
    +關(guān)注

    關(guān)注

    116

    文章

    3766

    瀏覽量

    80765
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4727

    瀏覽量

    68248
  • python
    +關(guān)注

    關(guān)注

    55

    文章

    4774

    瀏覽量

    84386
收藏 人收藏

    評論

    相關(guān)推薦

    具有十個解碼輸出的進位計數(shù)器/除法器數(shù)據(jù)表

    電子發(fā)燒友網(wǎng)站提供《具有十個解碼輸出的進位計數(shù)器/除法器數(shù)據(jù)表.pdf》資料免費下載
    發(fā)表于 05-17 10:47 ?0次下載
    具有<b class='flag-5'>十個</b>解碼輸出的<b class='flag-5'>十</b>進位計數(shù)器/除法器數(shù)據(jù)表

    ADIS16460正常的工作溫度是?25°C 到 +85°C,為什么測試的時候有幾十個是-10°C ?

    ADIS16460正常的工作溫度是?25°C 到 +85°C,為什么測試的時候有幾十個是-10°C ?
    發(fā)表于 12-28 08:18

    split在python的用法

    split在python的用法 split()是Python中一非常常用的字符串函數(shù),它能夠根據(jù)指定的分隔符將一字符串分割成多個子字符
    的頭像 發(fā)表于 12-25 15:12 ?1951次閱讀

    你知道進制轉(zhuǎn)進制如何進行轉(zhuǎn)換嗎?

    。讓我們開始吧。 首先,什么是進制數(shù)?現(xiàn)代數(shù)學(xué)中使用的數(shù)字系統(tǒng)是進制系統(tǒng)。它是一基于10的系統(tǒng),其中包含十個數(shù)字:0、1、2、3、4、5、6、7、8和9。在這個系統(tǒng)
    的頭像 發(fā)表于 12-20 17:05 ?1400次閱讀

    華為星閃BS25 SDKGADC設(shè)置的陷阱

    用華為星閃 BS25 SDK 做產(chǎn)品中發(fā)現(xiàn) GADC的 一陷阱。
    的頭像 發(fā)表于 12-01 14:01 ?1561次閱讀
    華為星閃BS25 SDK<b class='flag-5'>中</b>GADC設(shè)置的<b class='flag-5'>陷阱</b>

    如何避開無源元件的陷阱

    電子發(fā)燒友網(wǎng)站提供《如何避開無源元件的陷阱.pdf》資料免費下載
    發(fā)表于 11-28 10:19 ?0次下載
    如何避開無源元件的<b class='flag-5'>陷阱</b>

    Python2與Python3的差異

    Python2,print語句是一關(guān)鍵字,而在Python3,print函數(shù)被引入,必須使用括號。此外,
    的頭像 發(fā)表于 11-23 16:48 ?880次閱讀

    pythontext的用法

    Python的text是一常見的數(shù)據(jù)類型,它用于存儲和處理文本數(shù)據(jù)。在Python,文本常常被表示為字符串(string),字符串是一
    的頭像 發(fā)表于 11-23 15:46 ?3273次閱讀

    pythoninput怎么輸入3

    Python ,可以使用 input() 函數(shù)來獲取用戶的輸入。如果你想要輸入多個值,可以根據(jù)具體的需求使用以下方法。 方法一:使用多個 input 函數(shù) 最簡單的方法是使用多個 input
    的頭像 發(fā)表于 11-23 15:31 ?9473次閱讀

    pythonnumber代表什么

    Python,number(數(shù)字)是一種內(nèi)置的數(shù)據(jù)類型,用于表示數(shù)值。Python提供了幾種不同的number類型,分別是整數(shù)(int)、浮點數(shù)(float)、復(fù)數(shù)(complex)和布爾值
    的頭像 發(fā)表于 11-22 09:50 ?1840次閱讀

    Python如何表達復(fù)數(shù)

    Python,可以使用complex類型來表示和操作復(fù)數(shù)。復(fù)數(shù)是由一實部和一虛部組成的數(shù),可以使用下列格式來表示:real + imag*j,其中real是實部,imag是虛部
    的頭像 發(fā)表于 11-21 16:50 ?2080次閱讀

    python輸出list的每一元素

    使用for循環(huán)來遍歷列表,并輸出其中的每一元素。接下來,我將詳盡、詳實、細致地介紹一下Python輸出列表的每一元素的方法,希望能對你有所幫助。 首先,我們需要明確一下什么是列表。列表是一種有序的可變序列,其中可以包含任意類
    的頭像 發(fā)表于 11-21 16:16 ?1439次閱讀

    python如何定義維空數(shù)組

    Python,可以通過使用列表嵌套的方式來定義維空數(shù)組。具體步驟如下: Step 1: 創(chuàng)建一空的維列表 要創(chuàng)建一
    的頭像 發(fā)表于 11-21 15:12 ?1659次閱讀

    python怎么創(chuàng)建維數(shù)組

    如何創(chuàng)建維數(shù)組在Python是一常見的問題。在Python,我們可以使用嵌套的列表(li
    的頭像 發(fā)表于 11-21 15:10 ?3587次閱讀

    接口調(diào)用并發(fā)執(zhí)行十個任務(wù)總結(jié)

    接口調(diào)用時,接收到一列表,十個元素,需要并發(fā)執(zhí)行十個任務(wù),每個任務(wù)都要返回執(zhí)行的結(jié)果和異常,然后對返回的結(jié)果裝填到一切片列表里,統(tǒng)一
    的頭像 發(fā)表于 11-15 10:37 ?412次閱讀