Go 避免SQL注入

2022-05-13 17:39 更新

什么是SQL注入

SQL注入攻擊(SQL Injection),簡(jiǎn)稱(chēng)注入攻擊,是Web開(kāi)發(fā)中最常見(jiàn)的一種安全漏洞??梢杂盟鼇?lái)從數(shù)據(jù)庫(kù)獲取敏感信息,或者利用數(shù)據(jù)庫(kù)的特性執(zhí)行添加用戶(hù),導(dǎo)出文件等一系列惡意操作,甚至有可能獲取數(shù)據(jù)庫(kù)乃至系統(tǒng)用戶(hù)最高權(quán)限。

而造成SQL注入的原因是因?yàn)槌绦驔](méi)有有效過(guò)濾用戶(hù)的輸入,使攻擊者成功的向服務(wù)器提交惡意的SQL查詢(xún)代碼,程序在接收后錯(cuò)誤的將攻擊者的輸入作為查詢(xún)語(yǔ)句的一部分執(zhí)行,導(dǎo)致原始的查詢(xún)邏輯被改變,額外的執(zhí)行了攻擊者精心構(gòu)造的惡意代碼。

SQL注入實(shí)例

很多Web開(kāi)發(fā)者沒(méi)有意識(shí)到SQL查詢(xún)是可以被篡改的,從而把SQL查詢(xún)當(dāng)作可信任的命令。殊不知,SQL查詢(xún)是可以繞開(kāi)訪問(wèn)控制,從而繞過(guò)身份驗(yàn)證和權(quán)限檢查的。更有甚者,有可能通過(guò)SQL查詢(xún)?nèi)ミ\(yùn)行主機(jī)系統(tǒng)級(jí)的命令。

下面將通過(guò)一些真實(shí)的例子來(lái)詳細(xì)講解SQL注入的方式。

考慮以下簡(jiǎn)單的登錄表單:

<form action="/login" method="POST">
<p>Username: <input type="text" name="username" /></p>
<p>Password: <input type="password" name="password" /></p>
<p><input type="submit" value="登陸" /></p>
</form>

我們的處理里面的SQL可能是這樣的:

username:=r.Form.Get("username")
password:=r.Form.Get("password")
sql:="SELECT * FROM user WHERE username='"+username+"' AND password='"+password+"'"

如果用戶(hù)的輸入的用戶(hù)名如下,密碼任意

myuser' or 'foo' = 'foo' --

那么我們的SQL變成了如下所示:

SELECT * FROM user WHERE username='myuser' or 'foo'=='foo' --'' AND password='xxx'

在SQL里面--是注釋標(biāo)記,所以查詢(xún)語(yǔ)句會(huì)在此中斷。這就讓攻擊者在不知道任何合法用戶(hù)名和密碼的情況下成功登錄了。

對(duì)于MSSQL還有更加危險(xiǎn)的一種SQL注入,就是控制系統(tǒng),下面這個(gè)可怕的例子將演示如何在某些版本的MSSQL數(shù)據(jù)庫(kù)上執(zhí)行系統(tǒng)命令。

sql:="SELECT * FROM products WHERE name LIKE '%"+prod+"%'"
Db.Exec(sql)

如果攻擊提交a%' exec master..xp_cmdshell 'net user test testpass /ADD' --作為變量 prod的值,那么sql將會(huì)變成

sql:="SELECT * FROM products WHERE name LIKE '%a%' exec master..xp_cmdshell 'net user test testpass /ADD'--%'"

MSSQL服務(wù)器會(huì)執(zhí)行這條SQL語(yǔ)句,包括它后面那個(gè)用于向系統(tǒng)添加新用戶(hù)的命令。如果這個(gè)程序是以sa運(yùn)行而 MSSQLSERVER服務(wù)又有足夠的權(quán)限的話(huà),攻擊者就可以獲得一個(gè)系統(tǒng)帳號(hào)來(lái)訪問(wèn)主機(jī)了。

雖然以上的例子是針對(duì)某一特定的數(shù)據(jù)庫(kù)系統(tǒng)的,但是這并不代表不能對(duì)其它數(shù)據(jù)庫(kù)系統(tǒng)實(shí)施類(lèi)似的攻擊。針對(duì)這種安全漏洞,只要使用不同方法,各種數(shù)據(jù)庫(kù)都有可能遭殃。

如何預(yù)防SQL注入

也許你會(huì)說(shuō)攻擊者要知道數(shù)據(jù)庫(kù)結(jié)構(gòu)的信息才能實(shí)施SQL注入攻擊。確實(shí)如此,但沒(méi)人能保證攻擊者一定拿不到這些信息,一旦他們拿到了,數(shù)據(jù)庫(kù)就存在泄露的危險(xiǎn)。如果你在用開(kāi)放源代碼的軟件包來(lái)訪問(wèn)數(shù)據(jù)庫(kù),比如論壇程序,攻擊者就很容易得到相關(guān)的代碼。如果這些代碼設(shè)計(jì)不良的話(huà),風(fēng)險(xiǎn)就更大了。目前Discuz、phpwind、phpcms等這些流行的開(kāi)源程序都有被SQL注入攻擊的先例。

這些攻擊總是發(fā)生在安全性不高的代碼上。所以,永遠(yuǎn)不要信任外界輸入的數(shù)據(jù),特別是來(lái)自于用戶(hù)的數(shù)據(jù),包括選擇框、表單隱藏域和 cookie。就如上面的第一個(gè)例子那樣,就算是正常的查詢(xún)也有可能造成災(zāi)難。

SQL注入攻擊的危害這么大,那么該如何來(lái)防治呢?下面這些建議或許對(duì)防治SQL注入有一定的幫助。

  1. 嚴(yán)格限制Web應(yīng)用的數(shù)據(jù)庫(kù)的操作權(quán)限,給此用戶(hù)提供僅僅能夠滿(mǎn)足其工作的最低權(quán)限,從而最大限度的減少注入攻擊對(duì)數(shù)據(jù)庫(kù)的危害。
  2. 檢查輸入的數(shù)據(jù)是否具有所期望的數(shù)據(jù)格式,嚴(yán)格限制變量的類(lèi)型,例如使用regexp包進(jìn)行一些匹配處理,或者使用strconv包對(duì)字符串轉(zhuǎn)化成其他基本類(lèi)型的數(shù)據(jù)進(jìn)行判斷。
  3. 對(duì)進(jìn)入數(shù)據(jù)庫(kù)的特殊字符('"\尖括號(hào)&*;等)進(jìn)行轉(zhuǎn)義處理,或編碼轉(zhuǎn)換。Go 的text/template包里面的HTMLEscapeString函數(shù)可以對(duì)字符串進(jìn)行轉(zhuǎn)義處理。
  4. 所有的查詢(xún)語(yǔ)句建議使用數(shù)據(jù)庫(kù)提供的參數(shù)化查詢(xún)接口,參數(shù)化的語(yǔ)句使用參數(shù)而不是將用戶(hù)輸入變量嵌入到SQL語(yǔ)句中,即不要直接拼接SQL語(yǔ)句。例如使用database/sql里面的查詢(xún)函數(shù)PrepareQuery,或者Exec(query string, args ...interface{})。
  5. 在應(yīng)用發(fā)布之前建議使用專(zhuān)業(yè)的SQL注入檢測(cè)工具進(jìn)行檢測(cè),以及時(shí)修補(bǔ)被發(fā)現(xiàn)的SQL注入漏洞。網(wǎng)上有很多這方面的開(kāi)源工具,例如sqlmap、SQLninja等。
  6. 避免網(wǎng)站打印出SQL錯(cuò)誤信息,比如類(lèi)型錯(cuò)誤、字段不匹配等,把代碼里的SQL語(yǔ)句暴露出來(lái),以防止攻擊者利用這些錯(cuò)誤信息進(jìn)行SQL注入。

總結(jié)

通過(guò)上面的示例我們可以知道,SQL注入是危害相當(dāng)大的安全漏洞。所以對(duì)于我們平常編寫(xiě)的Web應(yīng)用,應(yīng)該對(duì)于每一個(gè)小細(xì)節(jié)都要非常重視,細(xì)節(jié)決定命運(yùn),生活如此,編寫(xiě)Web應(yīng)用也是這樣。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)