SQL注入
攻擊原理
在編寫SQL語句時,如果直接將用戶傳入的數據作為參數使用字符串拼接的方式插入到SQL查詢中,那么攻擊者可以通過注入其他語句來執行攻擊操作,這些攻擊包括可以通過SQL語句做的任何事:獲取敏感數據、修改數據、刪除數據庫表等
攻擊示例
假設我們的程序是一個學生信息查詢程序,其中的某個視圖函數接收用戶輸入的密碼,返回根據密碼查詢對應的數據。我們的數據庫有一個db對象表示,SQL語句通過execute()方法執行:
@app.route('/students')
def bobby_table():
password = request.args.get('password')
cur = db.execute("SELECT * FROM students WHERE password = '%s';" %password)#直接使用用戶輸入的數據執行sql
results = cur.fetchall()
return results
在實際應用中,敏感數據需要通過表單提交的POST請求接收,這里為了便於演示,通過查詢參數接收。
我們通過查詢字符串獲取用戶插入的查詢參數,並且不經過任何處理就使用字符串格式化的方法拼接到SQL語句中。在這種情況下,如果攻擊者輸入的參數值為” ‘or 1 =1”,即http://example.com/students?password=’or 1=1 --,那么視圖函數中被執行的SQL語句就變成:
select * from students where password=’’ or 1=1 --;’
這時會把students表中所有記錄全部查詢並返回,也就意味着所有的記錄都被攻擊者竊取了。更可怕的是,如果攻擊者將password參數的值設為”’; drop table users; --”,那么查詢語句就會變成:
select * from students where password=’ ’; drop table users; --’
這個語句會把students表中所有的記錄全部刪掉。
在SQL中,“;”用來結束一行語句;“--”用來注視后邊的語句,類似python的”#”。
主要防范方法
驗證輸入類型
比如某個視圖函數接收整形id來查詢,那么就在URL規則中限制URL變量為整形。
參數化查詢
在構造SQL語句時避免使用拼接字符串或字符串格式化(使用百分號或format()方法)的方式來構建SQL語句。而要使用各類接口庫提供的參數化查詢方法,以mysql為例:
db.execute(‘select * from students where password=@password’),此時,即使password的值是“’ or 1=1 --”話,那么SQL在執行的時候會把password這個變量單獨進行解析:
@password=’’ or 1=1 –‘,並不會像拼接字符串那樣使這個SQL執行其他的動作,所以就不會被注入了
轉義特使字符
比如引號、分號和橫線等。使用參數化查詢時,各種接口庫會為我們做轉義工作。