1. 程式人生 > 其它 >什麼是SQL注入?如何防止SQL注入?

什麼是SQL注入?如何防止SQL注入?

一、SQL注入

1、什麼是SQL注入?

SQL注入是比較常見的網路攻擊方式之一,主要攻擊物件是資料庫,針對程式設計師編寫時的疏忽,通過SQL語句,實現無賬號登入,篡改資料庫。

SQL注入簡單來說就是通過在表單中填寫包含SQL關鍵字的資料來使資料庫執行非常規程式碼的過程。

SQL資料庫的操作是通過SQL語句來執行的,這就導致如果我們在程式碼中加入了某些SQL語句關鍵字(比如說DELETE、DROP等),這些關鍵字就很可能在資料庫寫入或讀取資料時得到執行。

2、SQL注入攻擊的總體思路 

  1. 尋找到SQL注入的位置;
  2. 判斷伺服器型別和後臺資料庫型別;
  3. 針對不同的伺服器和資料庫特點進行SQL注入攻擊。

3、SQL注入案例來看一個SQL注入的案例。正常程式碼

import sqlite3

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

# 建立新的資料表
conn.executescript(”’DROPTABLEIFEXISTSstudents;

CREATETABLEstudents(idINTEGERPRIMARYKEYAUTOINCREMENT, name TEXT NOT NULL);”’

#插入學生資訊
students = [‘Paul’,’Tom’,’Tracy’,’Lily’] fornameinstudents:
query=“INSERTINTOstudents(name)VALUES(‘%s’)”%(name)

conn.executescript(query);

#檢視已有的學生資訊
cursor=conn.execute(“SELECTid,namefromstudents”)
print(‘IDName’)forrowincursor: print(‘{0}{1}’.format(row[0], row[1]))
conn.close()

SQL注入程式碼

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

#插入包含注入程式碼的資訊name=“Robert’);
DROPTABLEstudents;–”
query = “INSERT INTO students (name) VALUES (‘%s’)” % (name)


conn.executescript(query)

#檢視已有的學生資訊
cursor=conn.execute(“SELECTid,namefromstudents”)
print(‘IDName’)forrowincursor:
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接收到最終執行完畢返回可以分為下面三個過程:

  1. 詞法和語義解析;
  2. 優化sql語句,制定執行計劃;
  3. 執行並返回結果。

很多情況,同一型別的sql語句可能會反覆執行,如果每次都需要經過上面的詞法語義解析、語句優化、制定執行計劃等,不但影響執行效率也不安全。

所謂預編譯語句就是將這類語句中的值用佔位符替代,可以視為將sql語句模板化或者說引數化,一般稱這類語句叫Prepared Statements

預編譯語句的優勢在於歸納為:一次編譯、多次執行,省去了解析優化等過程;此外預編譯語句能防止sql注入。

2、為什麼Statement會被sql注入?

Statement之所以會被sql注入是因為SQL語句結構發生了變化。
比如:

"select * from tablename where username='"+uesrname+  "'andpassword='"+password+"'"

在使用者輸入’or true or’之後sql語句結構改變。

  • select * fromtablenamewhereusername=”ortrueor”andpassword=”

這樣本來是判斷使用者名稱和密碼都匹配時才會計數,但是經過改變後變成了或的邏輯關係,不管使用者名稱和密碼是否匹配該式的返回值永遠為true;

3、為什麼Preparement可以防止SQL注入?

原理是採用了預編譯的方法,先將SQL語句中可被客戶端控制的引數集進行編譯,生成對應的臨時變數集,再使用對應的設定方法,為臨時變數集裡面的元素進行賦值,賦值函式setString(),會對傳入的引數進行強制型別檢查和安全檢查,所以就避免了SQL注入的產生。

Preparement樣式為:

  • select * from tablename where username=? and password=?

該SQL語句會在得到使用者的輸入之前先用資料庫進行預編譯,這樣的話不管使用者輸入什麼使用者名稱和密碼的判斷始終都是並的邏輯關係,防止了SQL注入。

聚焦技術與人文,分享乾貨,共同成長
更多內容請關注“資料與人”