安全漏洞大揭秘:手把手教你輕松防止SQL注入
SQL(結構化查詢語言)注入是眾所周知的軟件弱點和安全漏洞,如果不是的話,也是最出名的漏洞之一。盡管享有盛名,但如何防止SQL注入仍然是主要漏洞之一,并且攻擊持續增長。
查找SQL注入
根據OWASP Top 10,注入漏洞(其中SQL注入是其中一種)是Web應用程序安全性的頭號問題。SQL注入在CWE Top 25中排名第六。其他類型的安全漏洞示例包括:
- 指令注入(CWE-77)
- 操作系統命令注入(CWE-78)
- 冬眠注射(CWE-564)
- 表達語言注入(CWE-917)
所有這些漏洞都有一個共同的屬性。利用來自系統外部的數據,用戶或文件輸入或任何潛在危險功能來利用它們。
幸運的是,SQL注入可以通過工具靜態和動態地檢測到。但是,您永遠無法確定是否全部抓住了它們。防止SQL注入也是減少這些漏洞的頻率和影響的關鍵。結合了漏洞檢測和預防功能的成熟DevSecOps流程很可能會捕獲并阻止這些類型的漏洞進入已發布的產品。
什么是SQL?
SQL是一種特定于域的語言,旨在管理關系數據庫。關系數據庫將數據顯示為行和列中的表的集合。每行都有一個提供與其他表的關系的鍵。這是表“user”的示例:

與CWE Top 25中常見的漏洞枚舉有關的內存錯誤
SQL是用于管理,查詢和處理關系數據庫中數據的首選語言。它定義數據庫創建中的表和關系。對于大多數日常使用,開發人員將SQL用于“CRUD”—創建、讀取、更新和刪除數據。
為什么SQL可利用?
通用編程語言不包括對SQL的支持。通過數據庫供應商提供的API訪問數據庫命令。在許多情況下,SQL命令以字符串形式發送,API會解釋該字符串并將其應用于數據庫。以下是一些簡單的SQL查詢:
典型的SQL查詢采用以下形式:
- Select (something) from (somewhere) (optional condition)
以上表為例,從姓氏為“Smith”的行中檢索電子郵件,使用以下SQL語句:
- Select email from user where lastname = ‘Smith’
輸出如下:
- Smith1234@mail.com
- John.smith@mail.netSmith1234@mail.com
使用Web表單(見下文)從用戶那里獲取輸入是Web應用程序中的一種常見用例。 用戶在“名稱”字段中輸入的數據,例如,用于根據收到的輸入來形成SQL查詢。 考慮以下簡單的Web表單:

該軟件處理表單并將值分配給變量,如下所示:
- String formName = request.getParameter(Name);
輸入為“名稱”的字符串用于使用該用戶輸入來組合查詢:
- String myQuery = “select message from user where email = ‘” + formName +”’;”
使用此構造的查詢:
- Select message from user where email= ‘Smith1234@mail.com’;
其輸出(以上表為例)如下:
- Hello
- How are you
希望很容易看到這一切都會出錯。假定直接在字符串中使用用戶輸入,那么了解SQL語法的人可以輕松地操縱它來生成SQL查詢。考慮以下示例:
使用上面相同的表格,有人在電子郵件字段中輸入“Smith1234@mail.com”或“1” =“1”。
相同的代碼將組裝以下SQL查詢字符串:
- Select message from user where email = ‘Smith1234@mail.com’ or ‘1’=’1’;
添加看似無害的內容(例如“or 1=1”)會更改查詢的邏輯,并可能通過返回表中稱為“用戶”的所有行來泄漏數據。在這種情況下,向您顯示表中每個用戶的消息。嚴重的隱私問題,在某些司法管轄區或環境中也可能存在法律問題,例如GDPR,HIPAA或CCPA。
上面的查詢以以下意外輸出結束:
- Hello
- Password 1234
- How are youDon’t tell anyone
- Wassup
SQL注入的工作方式
SQL注入(和其他類型的注入漏洞)的基本要點是在SQL查詢字符串中使用來自應用程序外部的未經檢查的數據,例如用戶輸入文本。CWE 89的描述:“SQL命令中使用的特殊元素的不適當中和(SQL注入)”更精確地定義了以下內容:
“在用戶可控制的輸入中沒有充分刪除或引用SQL語法的情況下,生成的SQL查詢可能導致這些輸入被解釋為SQL而不是普通用戶數據。這可用于更改查詢邏輯以繞過安全性檢查,或用于插入修改后端數據庫的其他語句,可能包括執行系統命令。”
CWE數據庫中的相同條目(CWE 89)提供了此攻擊的另一個簡單示例。假設應用程序代表用戶“wiley”進行查詢,并且用戶以包含SQL指令的方式構造輸入,例如:
- name'; DELETE FROM items; --
如果此應用程序不對此輸入進行任何有效性檢查,則會構造如下查詢:
- SELECT * FROM items WHERE owner = 'wiley' AND itemname = 'name';
- DELETE FROM items;
- --'
如果此攻擊成功,它將刪除表項中的所有數據,從而對數據庫造成破壞。任何有效的SQL命令都可能以這種方式執行。這是寫/修改攻擊的示例,其目的是破壞數據庫或插入不需要的信息。前面的示例(“or 1=1”)是讀取攻擊,其目的是數據泄漏。
數據庫服務器的許多實現都接受分號作為命令分隔符,這使得這種SQL注入非常危險。尾部的“–”表示文本的其余部分為注釋,從而迫使SQL解釋器忽略尾部的引號,否則將導致語法錯誤。欺騙組合查詢字符串的方法有多種。有時以開發人員無法想象的方式。
防止SQL注入的緩解措施
開發人員應實施幾種緩解措施。首先,安全立場應考慮所有來自不受信任的應用程序外部的數據。以下是典型的緩解策略:
- 將準備好的語句與參數化查詢一起使用。
- 使用存儲過程。
- 白名單輸入驗證。
- 轉義所有提供的輸入。
這些在OWASP速查表的SQL注入中有更詳細的描述。
測試SQL注入
一種典型的安全性方法是,在集成軟件運行時,作為常規質量檢查操作的一部分,執行各種類型的安全性測試。不幸的是,功能測試不會嘗試將漏洞利用插入用戶輸入字段中,因為大多數測試人員并不認為自己是壞演員。
除了傳統上他們沒有時間或方向的事實之外。手動測試注入類型漏洞也很困難,因為它需要嘗試許多不同的輸入組合。這是開始進行模糊測試或模糊測試的地方。它會創建隨機,意外和無效的數據作為被測應用程序的輸入。模糊測試是滲透測試的一部分,因為目標是通過公開的界面公開安全漏洞。
滲透測試
滲透性測試(以及擴展性的模糊測試)是有益的,因為它可以發現貫穿整個過程的安全性問題并揭示重要的安全性問題。但是,像所有動態測試一樣,它完全取決于測試,代碼和API覆蓋的數量,以完全測試所有可能的排列和組合。滲透測試取決于功能測試的完整性,通常在用戶界面級別進行。因此,請務必通過API測試和SAST支持滲透測試,以確保您的工作透徹。
API測試
API測試通過消除對脆弱且耗時的UI測試的依賴,有助于向左移動功能和安全性測試。API層是許多應用程序功能所駐留的地方,并且測試在此級別進行更改時更具彈性,并且更易于自動化和維護。
API級別的滲透測試
使用諸如Parasoft SOAtest之類的工具可以進行API級別的滲透測試以暴露SQL注入,在這些工具中,可以從現有功能測試中創建自動模糊測試,從而行使應用程序的業務邏輯。Parasoft SOAtest與著名的滲透測試工具Burp Suite集成在一起。
使用Parasoft SOAtest執行功能測試方案時,將捕獲測試中定義的API調用以及請求和響應流量。每次測試中的Burp Suite分析工具都會將流量數據傳遞到Burp Suite應用程序的一個單獨的運行實例,該實例將根據其在流量數據中觀察到的API參數,使用自己的啟發式方法對API進行滲透測試。
然后,Burp Suite分析工具將獲取Burp Suite發現的任何錯誤,并將其報告為SOAtest中與訪問API的測試相關的錯誤。Parasoft SOAtest結果將報告到Parasoft的報告和分析儀表板中。有關其他報告功能。
要了解有關此集成的更多信息,請參閱我們以前的滲透測試文章。有關Portswigger關于使用Burp進行SQL注入的更多信息,請查看其文章。以下是與Burp集成的工作方式的表示:

將這種類型的滲透測試集成到您的CI/CD流程中是防御SQL注入和其他類型漏洞的重要組成部分。
滲透和模糊測試無疑是DevSecOps中的重要過程,并且至關重要。但是,這提出了問題。
- 測試檢測到安全漏洞時會發生什么?
- 當軟件團隊發現其大部分用戶輸入處理不安全時,會發生什么?
- 它當然需要修復,但是要付出什么代價?
在開發的后期發現嚴重的安全問題會導致嚴重的成本和延遲。預防和檢測是將安全操作進一步轉移到更便宜且更容易修復的地方的關鍵。
將SQL注入的檢測和消除向左移動
在軟件開發中采用DevSecOps方法意味著將安全性集成到DevOps管道的各個方面。正如團隊盡早在SDLC中推進代碼分析和單元測試等質量流程一樣,安全性也是如此。
如果團隊更廣泛地采用這種方法,則SQL注入可能已成為過去。攻擊的增加意味著它尚未發生。無論如何,讓我們概述一種可以盡早防止SQL注入的方法。
與修補(和道歉!)已發布的應用程序相比,查找和修復潛在的SQL注入(以及其他注入漏洞)可節省大量時間。單個重大事件可能使公司損失20萬美元或更多。小型企業發生許多事件。一次攻擊會造成嚴重的財務壓力,更不用說有關違規披露和保護個人身份信息的潛在監管問題了。
下面概述的“檢測和阻止”方法基于將減輕SQL注入的風險轉移到開發的最早階段,并通過通過靜態代碼分析進行檢測來增強此功能。
如何檢測SQL注入
檢測SQL注入依賴于靜態分析來在源代碼中找到這些類型的漏洞。檢測發生在開發人員的桌面和構建系統中。它可以包括現有的、舊的和第三方代碼。
連續檢測安全問題可確保發現以下所有問題:
- 開發人員錯過了IDE。
- 存在于比您的新的檢測預防方法還早的代碼中。
推薦的方法是信任但驗證模型。安全性分析在IDE級別進行,開發人員在該級別上根據收到的報告做出實時決策。接下來,在構建級別進行驗證。理想情況下,構建級別的目標不是找到漏洞。這是為了驗證系統是否干凈。
例如,以Parasoft的演示應用程序Parabank為例。com.parasoft.parabank.dao.jdbc.internal中的StockDataInserter.java文件中可能存在SQL注入:
- …
- final String sql = sb.toString();
- rows = (nextId - lastId) / JdbcSequenceDao.OFFSET;totalRows += rows;getJdbcTemplate().update(sql);…
Parasoft JTest在生成時生成的報告如下:

詳細信息如下:
- Call to a dangerous method
- StockDataInserter.java (96): getJdbcTemplate().update(sql); *** Tainted data: SQL
追溯到先前發現源污染數據(來自應用程序外部的未經檢查、未經驗證的輸入)的位置:
- Tainting point
- StockDataInserter.java (47): return getJdbcTemplate().query(SQL, new
- ResultSetExtractor<List<String>>() { *** Tainted data:
- getJdbcTemplate().query(SQL, new ResultSetExtractor<List<Str...return
- symbols; } })
在與SQL注入的持續斗爭中,開發人員需要認真對待這些警告。在SQL查詢中任何使用未經驗證的數據都是嚴重的風險。即使當前的形式可能不是一個特定的警告問題,以后的重構也可能會暴露這些漏洞。檢查查詢字符串中使用的所有數據!
實際上,開發人員應驗證來自應用程序外部的任何數據,以確保它們符合預期的格式和內容。轉向“始終驗證”的理念以及依靠安全編碼而不是安全測試的過程會大大提高應用程序的安全性。開始加強代碼,以防止SQL注入首先被封裝。
何時以及如何防止SQL注入
防止SQL注入的理想時間和地點是開發人員在其IDE中編寫代碼時。采納安全編碼標準(例如C和C++的SEI CERT C和Java和.NET的OWASP Top 10或CWE Top 25)的團隊都具有警告未驗證SQL查詢輸入的準則。
在新創建的代碼上運行靜態分析既快速又簡單,并且很容易集成到CI/CD流程中。在現階段調查所有安全警告和不安全的編碼做法是一個好習慣,以防止將此代碼寫入到內部版本中。

檢測不良編碼實踐的同等重要的部分是報告的實用性。重要的是要能夠理解靜態分析違規的根本原因,以便快速、有效地解決它們。這就是Parasoft的C/C++test,dotTEST和Jtest等商業工具的發源地。
Parasoft的自動測試工具可對警告進行完整跟蹤,在IDE中進行說明,并連續收集構建信息和其他信息。這些收集的數據以及測試結果和指標可提供對團隊編碼標準合規性的全面了解。它還顯示了總體質量和安全狀態。
這些報告包括風險模型,這些模型是OWASP,CERT和CWE提供的信息的一部分。這樣,開發人員可以更好地了解該工具報告的潛在漏洞的影響以及應優先考慮哪些漏洞。在IDE級別生成的所有數據都與上面概述的下游活動相關。
總結
臭名昭著的SQL注入漏洞繼續困擾著Web應用程序。盡管知道它是如何工作和可以被利用的,但它仍然很普遍。有關最新示例,請參見IoT Hall of Shame。
我們提出一種預防和檢測方法來補充主動安全測試。這種方法可防止在寫入代碼之前盡早在SDLC中進行SQL注入。防止在IDE上進行SQL注入并在CI/CD管道中檢測到它們是將其路由到軟件之外的關鍵。最后,在使用滲透測試技術的測試過程中查找并修復這些錯誤。
與SQL注入(以及其他受污染數據的利用)的斗爭仍在繼續。精明的團隊可以在他們現有的工作流程中以正確的流程、工具和自動化扭轉局面。