SQL注入是應(yīng)用程序遭受的最常見的攻擊類型之一。鑒于其常見性及潛在的破壞性,需要在了解原理的基礎(chǔ)上探討如何保護(hù)應(yīng)用程序免受其害。
什么是SQL注入
?SQL注入(也稱為SQLi)是指攻擊者成功篡改Web應(yīng)用輸入,并在該應(yīng)用上執(zhí)行任意SQL查詢。此種攻擊通常會利用編程語言用來括住字符串的轉(zhuǎn)義字符。攻擊者想方設(shè)法用表單字段或URL參數(shù)向應(yīng)用注入額外的SQL代碼進(jìn)而獲得在目標(biāo)數(shù)據(jù)庫上執(zhí)行未經(jīng)授權(quán)的操作的能力。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖1:SQL注入原理
SQL注入的影響
實(shí)現(xiàn)SQL注入的攻擊者可以更改目標(biāo)數(shù)據(jù)庫中的數(shù)據(jù)。如果目標(biāo)應(yīng)用使用的數(shù)據(jù)庫連接字符串授予用戶寫入權(quán)限,SQL注入可能會造成重大破壞:攻擊者可以刪除大量數(shù)據(jù),甚至刪除表本身。
此外,即使攻擊者只能獲得對數(shù)據(jù)庫的讀取權(quán)限,也可能會導(dǎo)致敏感數(shù)據(jù)泄露,如財(cái)務(wù)信息或行業(yè)機(jī)密等業(yè)務(wù)敏感信息,以及客戶的私人信息等。隨著隱私法規(guī)越來越完善,數(shù)據(jù)泄露也是SQL注入最危險(xiǎn)的后果之一。
Java中的SQL注入
Java語言已經(jīng)存在了幾十年。盡管開發(fā)人員擁有包含穩(wěn)定的應(yīng)用框架和可靠的ORM的豐富生態(tài)系統(tǒng),仍不足以保護(hù)Java免于SQL注入攻擊。以Ruby為例。盡管Rails是一個穩(wěn)定的開發(fā)框架,但是SQL注入仍構(gòu)成了Ruby應(yīng)用70%的安全威脅。
以如下Java代碼片段為例:
String?sql?=?"select?"
??????+?"id,?title,?excerpt,?body"
??????+?"from?Posts?where?slug?=?'"
??????+?slug?
??????+?"'";
代碼通過關(guān)聯(lián)用戶以某種方式輸入的值(可能是URL參數(shù))來組裝SQL查詢。
這段代碼的問題在于通過用戶提供的值進(jìn)行關(guān)聯(lián)。假設(shè)這個Web應(yīng)用的基URL是
https://example.com/posts
如果我們將my-first-java-project添加到URL中,它變?yōu)?/p>
example.com/posts/my-first-java-projec
相應(yīng)的SQL代碼會變?yōu)?/p>
selectid, title, excerpt,bodyfromPostswhereslug ='my-first-java-project'
這段代碼看起來似乎沒什么問題。
進(jìn)一步地,假設(shè)一個不太善意的用戶將URL設(shè)置為
https://example.com/posts/whatever%27%20or%20%271%27=%271
其中實(shí)際傳遞的參數(shù)是
‘whatever’or‘1’=’1′
得出的SQL代碼是
selectid, title, excerpt,bodyfromPostswhereslug ='whatever'or'1'='1'
現(xiàn)在攻擊者已經(jīng)成功注入了未經(jīng)授權(quán)的代碼。SQL查詢的where子句有了一個判斷' 1 '是否等于' 1 '的附加條件。鑒于1=1是真命題,所有POST請求都可以被取回。
這看起來不是什么大問題;但如果對象是敏感的客戶信息,也即獲取敏感信息只需要一點(diǎn)點(diǎn)SQL注入,問題就不容忽視了。
防御Java?SQL注入的技術(shù)
盡管SQL注入攻擊很常見,而且具有潛在的破壞性,但它們并非無法防御。被利用的漏洞大多源于編碼錯誤,改進(jìn)方向有以下幾種:。
1.使用參數(shù)化查詢
針對Java中的SQL注入,可以從使用參數(shù)化查詢?nèi)胧?。下面的例子基于前一?jié)中提到的查詢語句進(jìn)行了改動
String sql = "selectid, title, excerpt,bodyfromPostswhereslug = ?";
用一個問號形式的占位符替換了關(guān)聯(lián)信息。下一步是創(chuàng)建一個預(yù)編譯語句,并給它綁定參數(shù)值
Connectionconnection = dataSource.getConnection();PreparedStatementp = c.prepareStatement(sql);p.setString(1,slug);
通過使用參數(shù)化查詢,我們可以以一種安全的方式組裝查詢語句與用戶提交的值。
2.允許列表輸入驗(yàn)證
這種方法是使用參數(shù)化查詢的補(bǔ)充。
白名單輸入驗(yàn)證是指將輸入限制為預(yù)先編譯的已知有效值列表,并對其余輸入進(jìn)行攔截。這包括使用正則表達(dá)式來驗(yàn)證某些類型的信息、驗(yàn)證數(shù)值參數(shù)是否符合預(yù)期范圍以及檢查參數(shù)是否符合預(yù)期數(shù)據(jù)類型。
建議對所有類型的用戶輸入進(jìn)行URL參數(shù)、表單字段、導(dǎo)入文件的內(nèi)容等驗(yàn)證。
3.以最小授權(quán)執(zhí)行查詢
SQL注入一旦成功,需確保應(yīng)用使用的連接字符串給予用戶最小授權(quán)。在應(yīng)用的特定部分,唯一需要的數(shù)據(jù)庫權(quán)限是讀取權(quán)限。這里推薦使用只有讀取權(quán)限的連接字符串;即便攻擊者能夠注入未經(jīng)授權(quán)的代碼,至少無法更改或刪除數(shù)據(jù)。
4.利用Java持久化
防御SQL注入的另一種方法是使用JPQL (Java持久性查詢語言)。JPA (Java Persistence API)有幾種實(shí)現(xiàn)方式,最流行的是Spring Data JPA和Hibernate。它們?yōu)閼?yīng)用提供了額外的數(shù)據(jù)層,有助于降低SQL注入成功的概率。
Java安全漏洞概述
SQL注入是Web應(yīng)用最常遭受攻擊類型之一;此外,還有許多安全威脅是Java開發(fā)人員應(yīng)該注意的,包括:
(1) 惡意Jar
(2) XSS注入
(3) Java?LDAP注入
(4) XPath注入
(5) SecurityManager漏洞
以下是一些增強(qiáng)Java應(yīng)用安全性的建議:
1、識別第三方漏洞。現(xiàn)代應(yīng)用通常對第三方庫和工具有很多依賴。使用SCA(軟件成分分析)工具對代碼進(jìn)行檢測,并形成軟件物料清單(SBOM),盤點(diǎn)代碼中引入的第三方組件及這些組件引入的漏洞風(fēng)險(xiǎn),并圍繞SBOM建立安全管理流程。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖2:SCA圖例
2、安全左移。在SDLC中盡早引入安全管理,使用自動化工具及相應(yīng)的管理流程來支持安全編碼實(shí)踐。
3、敏捷右移。應(yīng)用上線后進(jìn)入安全運(yùn)營階段,使用監(jiān)控和保護(hù)應(yīng)用安全的工具是關(guān)鍵,RASP能結(jié)合應(yīng)用的邏輯及上下文,以函數(shù)級的精度對訪問應(yīng)用系統(tǒng)的每一段代碼進(jìn)行檢測,實(shí)時監(jiān)控安全狀況、記錄并阻斷攻擊,而無需人工干預(yù)。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?圖3:RASP原理
SQL注入并不復(fù)雜,但其影響卻不容小覷。本文介紹了一些防御手段,以避免Java應(yīng)用成為SQL注入的犧牲品。安全理念、自動化工具及有效的安全管理流程共同構(gòu)成了保護(hù)應(yīng)用免于安全威脅的終極保障。