日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

這次我們將介紹怎么防止Python注入SQL攻擊。有上一篇的鋪墊,我們廢話不多說,開搞。。。

制作安全查詢參數

在上一篇中,我們看到了入侵者如何利用系統并通過使用 字符串獲得管理權限。問題是,我們允許直接執行從客戶端傳遞的值到數據庫,卻不執行任何類型的檢查或驗證,所以SQL注入就是依賴于這種類型的漏洞。

在數據庫查詢中使用用戶輸入時,可能存在SQL注入漏洞。防止PythonSQL注入的關鍵是確保該值是不是我們的意愿使用。在前面的示例中,我們打算username用作字符串。實際上,它被用作原始SQL語句。

為了防止入侵者將原始SQL注入字符串參數的位置,可以轉義引號:

>>> # BAD EXAMPLE. DON'T DO THIS!>>> username = username.replace("'", "''")

這只是一個例子。在試圖阻止Python SQL注入時,需要考慮許多特殊的字符和情況。還好,數據庫適配器提供了內置的工具,可以通過使用查詢參數來防止Python SQL注入。它們代替普通的字符串插值來組成一個帶有參數的查詢。

注意:不同的適配器、數據庫和編程語言以不同的名稱引用查詢參數。常見的名稱包括綁定變量、替換變量和替換變量。

 

現在我們對這個漏洞有了更好的理解,我們可以用查詢參數代替字符串插值來重寫函數了:

def is_admin(username: str) -> bool: with connection.cursor() as cursor: cursor.execute(""" SELECT admin FROM users WHERE username = %(username)s """, { 'username': username }) result = cursor.fetchone()
 if result is None: # User does not exist return False
 admin, = result return admin

 

在第9行,我們使用了一個命名參數username來指示用戶名應該放在哪里。注意,參數username不再被單引號包圍。

在第11行,我們將username的值作為第二個參數傳遞給了sor.execute()。在數據庫中執行查詢時,連接將使用username的類型和值。

 

測試這個函數,嘗試一些有效和無效的值,包括危險的字符串:

>>> is_admin('haki')False>>> is_admin('ran')True>>> is_admin('foo')False>>> is_admin("'; select true; --")False

該函數返回所有值都是預期的結果。更重要的是,無效的用戶名已經不再起作用了。可以通過檢查execute()生成的查詢來看原因:

>>> with connection.cursor() as cursor:... cursor.execute("""... SELECT... admin... FROM... users... WHERE... username = %(username)s... """, {... 'username': "'; select true; --"... })... print(cursor.query.decode('utf-8'))SELECT adminFROM usersWHERE username = '''; select true; --'

 

該連接將username的值視為字符串,并終止Python SQL注入的字符可能轉義該字符串的可能。

 

傳遞安全的查詢參數

數據庫適配器通常提供幾種傳遞查詢參數的方法。命名占位符通常是可讀性最好的,但是一些實現可能從使用其他選項中獲得。

 

讓我們快速查看一下使用查詢參數的一些正確和錯誤的方法。下面的代碼塊顯示了希望避免的查詢類型:

# BAD EXAMPLES. DON'T DO THIS!cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");

這些每一條語句都將用戶名從客戶機直接傳遞到數據庫,而不執行任何檢查或驗證。這種代碼適合引入Python SQL注入。

 

相比之下,這些類型的查詢執行起來應該是安全的:

cursor.execute("SELECT admin FROM users WHERE username = %s'", (username, ));cursor.execute("SELECT admin FROM users WHERE username = %(username)s", {'username': username});

在這些語句中,用戶名作為命名參數傳遞。數據庫將在執行查詢時使用用戶名的指定類型和值,從而避免Python SQL注入。

 

使用SQL組成

到目前為止,我們已經將參數用于諸如數字、字符串和日期之類的值。但是,如果有一個需要組合不同查詢,比如表名或列名,該怎么辦呢?

 

受前一個示例的啟發,讓我們實現一個函數,該函數接受表的名稱并返回該表中的行數:

# BAD EXAMPLE. DON'T DO THIS!def count_rows(table_name: str) -> int: with connection.cursor() as cursor: cursor.execute(""" SELECT count(*) FROM %(table_name)s """, { 'table_name': table_name, }) result = cursor.fetchone()
 rowcount, = result return rowcount

執行用戶表:

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 9, in count_rowspsycopg2.errors.SyntaxError: syntax error at or near "'users'"LINE 5: 'users'

該命令無法生成SQL。數據庫適配器將變量視為字符串或文字,但是表名不是普通的字符串。所以這就是SQL組合的用武之地。

 

現在已經知道使用字符串插值表達式來編寫SQL是不安全的。幸好,Psycopg提供了一個名為Psycopg的模塊。幫助我們安全地編寫sql查詢。讓我們使用psycopg.sql()重寫這個函數:

from psycopg2 import sql
def count_rows(table_name: str) -> int: with connection.cursor() as cursor: stmt = sql.SQL(""" SELECT count(*) FROM {table_name} """).format( table_name = sql.Identifier(table_name), ) cursor.execute(stmt) result = cursor.fetchone()
 rowcount, = result return rowcount

現在有兩個不同之處。首先,使用sql()來組合查詢。然后,使用sql.Identifier()來注釋參數值table_name。(標識符是列或表名。)

現在,嘗試執行用戶表上的函數:

>>> count_rows('users')2

接下來,讓我們看看當表不存在時會發生什么:

>> count_rows('foo')Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 11, in count_rowspsycopg2.errors.UndefinedTable: relation "foo" does not existLINE 5: "foo" 

該函數拋出UndefinedTable異常。在接下來的步驟中,我們將使用這個異常來表明函數不會受到Python SQL注入攻擊。

為了將它們放在一起,添加一個選項來將表中的行數計數到一定的限制, 這個特性對非常大的表很有用。要實現這一點,在查詢中添加一個LIMIT子句,以及LIMIT值的查詢參數:

from psycopg2 import sql
def count_rows(table_name: str, limit: int) -> int: with connection.cursor() as cursor: stmt = sql.SQL(""" SELECT COUNT(*) FROM ( SELECT 1 FROM {table_name} LIMIT {limit} ) AS limit_query """).format( table_name = sql.Identifier(table_name), limit = sql.Literal(limit), ) cursor.execute(stmt) result = cursor.fetchone()
 rowcount, = result return rowcount

在這個代碼塊中,使用sql.Literal()注釋了limit。與前面的示例一樣,psycopg在使用時將所有查詢參數綁定為文字。但是,在使用sql()時,需要使用sql.Identifier()或sql.Literal()顯式地注釋每個參數。

執行該功能,以確保運行正常:


>> count_rows('users', 1)1>>> count_rows('users', 10)2


運行正常,確保也是安全的:

>> count_rows("(select 1) as foo; update users set admin = true where name = 'haki'; --", 1)Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 18, in count_rowspsycopg2.errors.UndefinedTable: relation "(select 1) as foo; update users set admin = true where name = '" does not existLINE 8: "(select 1) as foo; update users set adm...

此返回顯示psycopg轉義了該值,并且數據庫將其視為表名。由于不存在具有此名稱的表,因此引發了UndefinedTable異常,攻擊失敗了


私信小編01 領取完整項目代碼

結論

我們已經成功地實現了一個組成動態SQL的函數,系統面臨Python SQL注入的風險也沒有了!

我們在查詢中既使用了字面值,又使用了標識符,沒有影響安全性。

分享到:
標簽:Python
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定