不會SQL隱碼攻擊,連漫畫都看不懂了

資料和雲發表於2022-12-05

先來看一副很有意思的漫畫:

不會SQL隱碼攻擊,連漫畫都看不懂了

相信大家對於學校們糟糕的網路環境和運維手段都早有體會,在此就不多做吐槽了。今天我們來聊一聊SQL隱碼攻擊相關的內容。

何謂SQL隱碼攻擊?


SQL隱碼攻擊是一種非常常見的資料庫攻擊手段,SQL隱碼攻擊漏洞也是網路世界中最普遍的漏洞之一。大家也許都聽過某某學長透過攻擊學校資料庫修改自己成績的事情,這些學長們一般用的就是SQL隱碼攻擊方法。

SQL隱碼攻擊其實就是惡意使用者透過在表單中填寫包含SQL關鍵字的資料來使資料庫執行非常規程式碼的過程。簡單來說,就是資料「越俎代庖」做了程式碼才能乾的事情。這個問題的來源是,SQL資料庫的操作是透過SQL語句來執行的,而無論是執行程式碼還是資料項都必須寫在SQL語句之中,這就導致如果我們在資料項中加入了某些SQL語句關鍵字(比如說SELECT、DROP等等),這些關鍵字就很可能在資料庫寫入或讀取資料時得到執行。

多言無益,我們拿真實的案例來說話。下面我們先使用SQLite建立一個學生檔案表。

SQL資料庫操作示例


import sqlite3

# 連線資料庫
conn = sqlite3.connect('test.db')

# 建立新的資料表
conn.executescript('''DROP TABLE IF EXISTS students;
       CREATE TABLE students
       (id INTEGER PRIMARY KEY AUTOINCREMENT,
       name TEXT NOT NULL);'''
)

# 插入學生資訊
students = ['Paul','Tom','Tracy','Lily']

for name in students:
    query = "INSERT INTO students (name) VALUES ('%s')" % (name)
    conn.executescript(query);

# 檢視已有的學生資訊
cursor = conn.execute("SELECT id, name from students")
print('IDName')
for row in cursor:
    print('{0}{1}'.format(row[0], row[1]))

conn.close()

點選執行按鈕將會列印目前表中的內容。上述程式中我們建立了一個test.db資料庫以及一個students資料表,並向表中寫入了四條學生資訊。

那麼SQL隱碼攻擊又是怎麼一回事呢?我們嘗試再插入一條惡意資料,資料內容就是漫畫中的"Robert');DROP TABLE students;--",看看會發生什麼情況。

SQL資料庫注入示例


conn = sqlite3.connect('test.db')

# 插入包含注入程式碼的資訊
name = "Robert');DROP TABLE students;--"
query = "INSERT INTO students (nameVALUES ('%s')" % (name)

conn.executescript(query)

# 檢視已有的學生資訊
cursor = conn.execute("
SELECT idname from students")
print('IDName')
for row in cursor:
    print('{0}{1}'.format(row[0], row[1]))

conn.close()

你將會發現,執行後,程式沒有輸出任何資料內容,而是返回一條錯誤資訊:表單students無法找到!

這是為什麼呢?問題就在於我們所插入的資料項中包含SQL關鍵字DROP TABLE,這兩個關鍵字的意義是從資料庫中清除一個表單。而關鍵字之前的Robert');使得SQL執行器認為上一命令已經結束,從而使得危險指令DROP TABLE得到執行。也就是說,這段包含DROP TABLE關鍵字的資料項使得原有的簡單的插入姓名資訊的SQL語句

"INSERT INTO students (nameVALUES ('Robert')"

變為了同時包含另外一條清除表單命令的語句

"INSERT INTO students (nameVALUES ('Robert');DROP TABLE students;--"

而SQL資料庫執行上述操作後,students表單被清除,因而表單無法找到,所有資料項丟失。

如何防止SQL隱碼攻擊問題


那麼,如何防止SQL隱碼攻擊問題呢?

大家也許都想到了,注入問題都是因為執行了資料項中的SQL關鍵字,那麼,只要檢查資料項中是否存在SQL關鍵字不就可以了麼?的確是這樣,很多資料庫管理系統都是採取了這種看似『方便快捷』的過濾手法,但是這並不是一種根本上的解決辦法,如果有個美國人真的就叫做『Drop Table』呢?你總不能逼人家改名字吧。

合理的防護辦法有很多。首先,儘量避免使用常見的資料庫名和資料庫結構。在上面的案例中,如果表單名字並不是students,則注入程式碼將會在執行過程中報錯,也就不會發生資料丟失的情況——SQL隱碼攻擊並不像大家想象得那麼簡單,它需要攻擊者本身對於資料庫的結構有足夠的瞭解才能成功,因而在構建資料庫時儘量使用較為複雜的結構和命名方式將會極大地減少被成功攻擊的機率。

使用正規表示式等字串過濾手段限制資料項的格式、字元數目等也是一種很好的防護措施。理論上,只要避免資料項中存在引號、分號等特殊字元就能很大程度上避免SQL隱碼攻擊的發生。

另外,就是使用各類程式文件所推薦的資料庫操作方式來執行資料項的查詢與寫入操作,比如在上述的案例中,如果我們稍加修改,首先使用execute()方法來保證每次執行僅能執行一條語句,然後將資料項以引數的方式與SQL執行語句分離開來,就可以完全避免SQL隱碼攻擊的問題,如下所示:

SQL資料庫反注入示例


conn = sqlite3.connect('test.db')

# 以安全方式插入包含注入程式碼的資訊
name = "Robert');DROP TABLE students;--"
query = "INSERT INTO students (nameVALUES (?)"

conn.execute(query, [name])

# 檢視已有的學生資訊
cursor = conn.execute("
SELECT idname from students")
print('IDName')
for row in cursor:
    print('{0}{1}'.format(row[0], row[1]))

conn.close()

而對於PHP而言,則可以透過mysql_real_escape_string等方法對SQL關鍵字進行轉義,必要時審查資料專案是否安全來防治SQL隱碼攻擊。

當然,做好資料庫的備份,同時對敏感內容進行加密永遠是最重要的。某些安全性問題可能永遠不會有完美的解決方案,只有我們做好最基本的防護措施,才能在發生問題的時候亡羊補牢,保證最小程度的損失。

連結:https://jizhi.im/blog/post/sql_injection_intro

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31556440/viewspace-2654636/,如需轉載,請註明出處,否則將追究法律責任。

相關文章