一、SQL注入
1、什么是SQL注入?
SQL注入是比較常見的網絡攻擊方式之一,主要攻擊對象是數據庫,針對程序員編寫時的疏忽,通過SQL語句,實現無賬號登錄,篡改數據庫。。
SQL注入簡單來說就是通過在表單中填寫包含SQL關鍵字的數據來使數據庫執行非常規代碼的過程。
SQL數據庫的操作是通過SQL語句來執行的,這就導致如果我們在代碼中加入了某些SQL語句關鍵字(比如說DELETE、DROP等),這些關鍵字就很可能在數據庫寫入或讀取數據時得到執行。
2、SQL注入攻擊的總體思路
- 尋找到SQL注入的位置;
- 判斷服務器類型和后台數據庫類型;
- 針對不同的服務器和數據庫特點進行SQL注入攻擊。
3、SQL注入案例來看一個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()
SQL注入代碼
# 連接數據庫
conn = sqlite3.connect(‘test.db’)
# 插入包含注入代碼的信息 name = “Robert’);
DROP TABLE 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()
上述代碼執行其后果可想。
二、如何防止SQL注入
但凡有SQL注入漏洞的程序,都是因為程序要接受來自客戶端用戶輸入的變量或URL傳遞的參數,並且這個變量或參數是組成SQL語句的一部分,對於用戶輸入的內容或傳遞的參數,我們應該要時刻保持警惕,這是安全領域里的「外部數據不可信任」的原則。縱觀Web安全領域的各種攻擊方式,大多數都是因為開發者違反了這個原則而導致的,所以自然能想到的,就是從變量的檢測、過濾、驗證下手,確保變量是開發者所預想的。
1、檢查變量數據類型和格式
如果SQL語句是類似where id={$id}這種形式,數據庫里所有的id都是數字,那么就應該在SQL被執行前,檢查確保變量id是int類型;如果是其他的類型比如日期、時間等也是一個道理。只要是有固定格式的變量,在SQL語句執行前,應該嚴格按照固定格式去檢查,確保變量是我們預想的格式,這樣很大程度上可以避免SQL注入攻擊。
2、過濾特殊符號
對於無法確定固定格式的變量,一定要進行特殊符號過濾或轉義處理。
3、綁定變量,使用預編譯語句
實際上,綁定變量使用預編譯語句是預防SQL注入的最佳方式,使用預編譯的SQL語句語義不會發生改變,在SQL語句中,變量用問號?表示,黑客即使本事再大,也無法改變SQL語句的結構。
三、為什么SQL預編譯能有效防御SQL注入
1、預編譯語句是什么?
一條sql在db接收到最終執行完畢返回可以分為下面三個過程:
- 詞法和語義解析;
- 優化sql語句,制定執行計划;
- 執行並返回結果。
很多情況,同一類型的sql語句可能會反復執行,如果每次都需要經過上面的詞法語義解析、語句優化、制定執行計划等,不但影響執行效率也不安全。
所謂預編譯語句就是將這類語句中的值用占位符替代,可以視為將sql語句模板化或者說參數化,一般稱這類語句叫Prepared Statements。
預編譯語句的優勢在於歸納為:一次編譯、多次運行,省去了解析優化等過程;此外預編譯語句能防止sql注入。
2、為什么Statement會被sql注入?
Statement之所以會被sql注入是因為SQL語句結構發生了變化。
比如:
"select * from tablename where username='"+uesrname+ "'and password='"+password+"'"
在用戶輸入’or true or’之后sql語句結構改變。
- select * from tablename where username=”or true or” and password=”
這樣本來是判斷用戶名和密碼都匹配時才會計數,但是經過改變后變成了或的邏輯關系,不管用戶名和密碼是否匹配該式的返回值永遠為true;
3、為什么Preparement可以防止SQL注入?
原理是采用了預編譯的方法,先將SQL語句中可被客戶端控制的參數集進行編譯,生成對應的臨時變量集,再使用對應的設置方法,為臨時變量集里面的元素進行賦值,賦值函數setString(),會對傳入的參數進行強制類型檢查和安全檢查,所以就避免了SQL注入的產生。
Preparement樣式為:
- select * from tablename where username=? and password=?
該SQL語句會在得到用戶的輸入之前先用數據庫進行預編譯,這樣的話不管用戶輸入什么用戶名和密碼的判斷始終都是並的邏輯關系,防止了SQL注入。
