成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何避免出現SQL注入漏洞

安全 應用安全 網站安全
本文將針對開發過程中依舊經常出現的SQL編碼缺陷,講解其背后原理及形成原因。

[[424246]]

一、前言

本文將針對開發過程中依舊經常出現的SQL編碼缺陷,講解其背后原理及形成原因。并以幾個常見漏洞存在形式,提醒技術同學注意相關問題。最后會根據原理,提供解決或緩解方案。

二、SQL注入漏洞的原理、形成原因

SQL注入漏洞,根本上講,是由于錯把外部輸入當作SQL代碼去執行。目前最佳的解決方案就是預編譯的方式。

SQL語句在執行過程中,需要經過以下三大基本步驟:

  • 代碼語義分析
  • 制定執行計劃
  • 獲得返回結果

而一個SQL語句是由代碼和數據兩部分,如:

  1. SELECT id, name, phone FROM userTable WHERE name = 'xiaoming';   

SELECT id, name, phone FROM userTable WHERE name = 是代碼,'xiaoming'是數據。

而預編譯,以Mybatis為例,就是預先分析帶有占位符的語義:

如SELECT id, name, phone FROM userTable WHERE id = #{name};

然后再將數據'xiaoming',傳入到占位符。這樣一來,錯開來代碼語義分析階段,也就不會被誤認為是代碼的一部分了。

在最早期,開發者顯式使用JDBC來自己創建Connection,執行SQL語句。這種情況下,如果將外部可控數據拼接到SQL語句,且沒有做充分過濾的話,就會產生漏洞。這種情況在正常的業務開發過程中已經很少了,按照公司規定,無特殊情況下,必須使用ORM框架來執行SQL。

但目前部分項目中,仍會使用JDBC來編寫一些工具腳本,如DataMerge.java 、DatabaseClean.java,借用JDBC的靈活性,通過這些腳本來執行數據庫批量操作。

此類代碼不應該出現在線上版本中,以免因各種情況,被外部調用。

三、直接使用Mybatis

1. 易錯點

目前大部分的平臺代碼是基于Mybatis來處理持久層和數據庫之間的交互的,Mybatis傳入數據有兩種占位符{}和#{}。{}和#{}。{}可以理解為語義分析前的字符串拼接,講傳入的參數,原封不動地傳入。

比如說

  1. SELECT id, name, phone FROM userTable WHERE name = '${name}';   

傳入name=xiaoming后,相當于

  1. SELECT id, name, phone FROM userTable WHERE name = 'xiaoming';   

實際應用中

  1. SELECT id, name, phone FROM userTable WHERE ${col} = 'xiaoming';   

傳入col = "name",相當于

  1. SELECT id, name, phone FROM userTable WHERE name = 'xiaoming';   

就像預編譯原理介紹里講的一樣,使用#{} 占位符就不存在注入問題了。但有些業務場景是不可以直接使用#{}的。

(1) 比如order by語法中

如果編寫SELECT id, name, phone FROM userTable ORDER BY #{}; ,執行時是會報錯的。因為order by后的內容,是一個列名,屬于代碼語義的一部分。如果在語義分析部分沒有確定下來,就相當于執行SELECT id, name, phone FROM userTable ORDER BY 。肯定會有語法錯誤。

(2) 再比如like場景下

 

  1. SELECT id, name, phone FROM userTable WHERE name like '%#{name}%';   

#{}不會被解析,從而導致報錯。

in 語法和 between語法都是如此,那么如何解決這類問題呢?

2. 正確寫法

(1) order by(group by)語句中使用${}

使用條件判斷

  1. <select id="getUserAndOrder" resultType="Emp" parameterType="Emp"> 
  2.     select * from users where id < #{id} 
  3.     <choose> 
  4.         <when test="order == \"name\""> 
  5.             order by name 
  6.         </when> 
  7.         <when test="order != \"age\""> 
  8.             order by age 
  9.         </when> 
  10.         <otherwise> 
  11.             order by id 
  12.         </otherwise> 
  13.     </choose> 
  14. </select> 

使用全局過濾機制,限制order by后的變量內容只能是數字、字母、下劃線。

如使用正則過濾:

  1. keywordkeyword = keyword.replaceAll("[^a-zA-Z0-9_\s+]", ""); 

這里需要注意,過濾需要使用白名單,不能使用黑名單,黑名單無法解決注入問題。

(2) LIKE語句

由于需要like中的關鍵詞需要包裹在兩個%符號中,因此可以使用CONCAT函數進行拼接。

  1. <select id="selectStudentByFuzzyQuery" resultMap="studentMap"> 
  2.     SELECT * 
  3.     FROM student 
  4.     WHERE student.stu_name 
  5.             LIKE CONCAT('%',#{stuName},'%') 
  6. </select> 

注意不要用 CONCAT('%','${stuName}','%') ,這樣仍然存在漏洞。也就是說,使用$符號是不對的,使用#符號才安全。

(3) IN語句

類似于like語句,直接使用#{}會報錯,常見的錯誤寫法為:

  1. tenant_id in (${tenantIds}) 

正確的寫法為:

  1. select * from news where id in 
  2. <foreach collection="ids" item="item" open="("separator="," close=")">#{item}</foreach> 

四、Mybatis-generator使用安全

繁重的CRUD代碼壓力下,開發者慢慢開始通過Mybatis-generator、idea-mybatis-generator插件、通用Mapper、Mybatis-generator-plus來自動生成Mapper、POJO、Dao等文件。

這些工具可以自動的生成CRUD所需要的文件,但如果使用不當,就會自動產生SQL注入漏洞。我們以最常用的org.mybatis.generator為例,來講解可能會出現的問題。

1. 動態語句支持

Mybatis-generator提供來一些函數,幫助用戶把SQL的各個條件連接起來,比如多個參數的like語法,多個參數的比較語法。為了保證使用的簡潔性,需要使用將一些語義代碼拼接到SQL語句中。而如果開發者使用不當,將外部輸入也傳入了{}占位符。就會產生漏洞。

2. targetRuntime參數配置

在配置generator時,配置文件generator-rds.xml中有一個targetRuntime屬性,默認為MyBatis3。在這種情況下,會啟動Mybatis的動態語句支持,啟動enableSelectByExample、enableDeleteByExample、enableCountByExample 以及 enableUpdateByExample功能。

以enableSelectByExample為例,會在xml映射文件中代入以下動態模塊:

  1. <sql id="Example_Where_Clause" > 
  2.   <where > 
  3.     <foreach collection="oredCriteria" item="criteria" separator="or" > 
  4.       <if test="criteria.valid" > 
  5.         <trim prefix="(" suffix=")" prefixOverrides="and" > 
  6.           <foreach collection="criteria.criteria" item="criterion" > 
  7.             <choose > 
  8.               <when test="criterion.noValue" > 
  9.                 and ${criterion.condition} 
  10.               </when> 
  11.               <when test="criterion.singleValue" > 
  12.                 and ${criterion.condition} #{criterion.value} 
  13.               </when> 
  14.               <when test="criterion.betweenValue" > 
  15.                 and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} 
  16.               </when> 
  17.               <when test="criterion.listValue" > 
  18.                 and ${criterion.condition} 
  19.                 <foreach collection="criterion.value" item="listItem" open="(" close=")" separator="," > 
  20.                   #{listItem} 
  21.                 </foreach> 
  22.               </when> 
  23.             </choose> 
  24.           </foreach> 
  25.         </trim> 
  26.       </if> 
  27.     </foreach> 
  28.   </where> 
  29. </sql> 

開發者include該模塊就可以添加where條件,但如果使用不當,就會導致SQL注入漏洞:

  1. <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.doctor.mybatisdemo.domain.userExample" > 
  2.     select 
  3.     <if test="distinct" > 
  4.       distinct 
  5.     </if> 
  6.     <include refid="Base_Column_List" /> 
  7.     from user 
  8.     <if test="_parameter != null" > 
  9.       <include refid="Example_Where_Clause" /> 
  10.     </if> 
  11.     <if test="orderByClause != null" > 
  12.       order by ${orderByClause} 
  13.     </if> 
  14.   </select> 

并使用自定義的參數添加函數:

  1. public Criteria addKeywordTo(String keyword) { 
  2.   StringBuilder sb = new StringBuilder(); 
  3.   sb.append("(display_name like '%" + keyword + "%' or "); 
  4.   sb.append("org like '" + keyword + "%' or "); 
  5.   sb.append("status like '%" + keyword + "%' or "); 
  6.   sb.append("id like '" + keyword + "%') "); 
  7.   addCriterion(sb.toString()); 
  8.   return (Criteria) this; 

目的是為了實現同時對display_name、org、status、id的like操作。其中addCriterion是Mybatis-generator自帶的函數:

  1. protected void addCriterion(String condition) { 
  2.     if (condition == null) { 
  3.         throw new RuntimeException("Value for condition cannot be null"); 
  4.     } 
  5.     criteria.add(new Criterion(condition)); 

這里的誤區在于,addCriterion本身提供了多個條件的支持,但開發者認為需要自己把多個條件拼接起來,一同傳入addCriterion方法。如同案例中的代碼一樣,最終傳入addCriterion的只有一個參數。從而執行Example_Where_Clause語句中的:

  1. <when test="criterion.noValue" > 
  2.     and ${criterion.condition} 
  3. </when> 

也就是說,開發者把自己拼接的SQL語句,直接代入了${criterion.condition}中,從而導致了漏洞的產生。

而按照Mybatis-generator的文檔,正確的寫法應該是:

  1. public void addKeywordTo(String keyword, UserExample userExample) { 
  2.   userExample.or().andDisplayNameLike("%" + keyword + "%"); 
  3.   userExample.or().andOrgLike(keyword + "%"); 
  4.   userExample.or().andStatusLike("%" + keyword + "%"); 
  5.   userExample.or().andIdLike("%" + keyword + "%"); 

or方法負責創建Criteria,這時觸發的邏輯就是

  1. <when test="criterion.singleValue" > 
  2.   and ${criterion.condition} #{criterion.value} 
  3. </when> 

${criterion.condition}被替換為了沒有單引號的like,like作為語義代碼,在語義分析前拼接到了SQL語句中,而"%" + keyword + "%"會作為數據添加到預編譯#{criterion.value}中去,從而避免了注入。

類似的,也提供了In語法的安全使用方法:

  1. List<Integer> field5Values = new ArrayList<Integer>(); 
  2. field5Values.add(8); 
  3. field5Values.add(11); 
  4. field5Values.add(14); 
  5. field5Values.add(22); 
  6.  
  7. example.or() 
  8.   .andField5In(field5Values); 

Beetween的安全使用方法:

  1. example.or() 
  2.   .andField6Between(3, 7); 

Mybatis-generator默認生成的order by語句也是使用${}直接進行拼接的:

  1. <if test="orderByClause != null" > 
  2.       order by ${orderByClause} 
  3.     </if> 

如果沒有對傳入的參數進行額外的過濾的話,就會導致注入問題。

3. order by

除了自己寫的SQL語句以外,Mybatis-generator默認生成的order by語句也是使用${}直接進行拼接的:

  1. <if test="orderByClause != null" > 
  2.       order by ${orderByClause} 
  3.     </if> 

如果沒有對傳入的參數進行額外的過濾的話,就會導致注入問題。

PS: 實際掃雷過程中發現很多語句自動生成了order by語法,但上層調用時,并沒有傳入該可選參數。這種情況應當刪除多余的order by語法。

4. 其它插件

插件與插件之間的安全缺陷還不太一樣,下面簡單列舉了常用的幾種插件。

(1) idea-mybatis-generator

這是IDEA的插件,可以在開發過程中,從IDE的層面,自動生成CRUD中需要的文件。使用該插件時,也有一些默認安全隱患需要注意。

1)自定義order by處理

like\in\between可以參照官方文檔使用,無安全隱患。

但該插件沒有內置的order by處理,需要自行編寫,編寫時,參考Case2

2)默認的IF條件前需要判斷是否為空

插件默認生成的語法大致如下:

  1. <if test="ID != null"> 
  2. ID = #{ID} and 

當ID參數為null時,if標簽下的邏輯不會添加到SQL語句中,可能會導致DOS、權限繞過等漏洞。因此,參數傳入查詢語句前,需要確認不為空。

(2) com.baomidou.mybatis-plus

  • apply方法傳參時,應當使用{}
  • 自帶的last方法,其原理是直接拼接到SQL語句的末尾,存在注入漏洞。

五、其它ORM框架

1. Hibernate

ORM全稱為對象關系映射(Object Relational Mapping),簡單地說,就是將數據庫中的表映射為Java對象, 這種只有屬性,沒有業務邏輯的對象也叫做POJO(Plain Ordinary Java Object)對象。

Hibernate是第一個被廣泛使用的ORM框架,它通過XML管理數據庫連接,提供全表映射模型,封裝程度很高。在配置映射文件和數據庫鏈接文件后,Hibernate就可以通過Session對象進行數據庫操作,開發者無需接觸SQL語句,只需要寫HQL語句即可。

Hibernate經常與Struts、Spring搭配使用,也就是Java世界的經典SSH框架。

HQL相較于SQL,多了很多語法限制:

  • 不能查詢未做映射的表,只有當模型之間的關系明確后,才可以使用UNION語法。
  • 表名,列名大小寫敏感。
  • 沒有*、#、-- 。
  • 沒有延時函數。

所以HQL注入利用要比SQL注入苦難得多。從代碼審計的角度和普通SQL注入是一致的:

拼接會導致注入漏洞:

  1. List<Student> studentList = session.createQuery("FROM Student s WHERE s.stuId = " + stuId).list(); 

可以使用占位符和具名參數來防止SQL語句,其本質都是預編譯。

  1. List<Student> studentList = session.createQuery("FROM Student s WHERE s.stuId = :stuId").setParameter("stuId",stuId).list(); 
  1. List<Student> studentList = session.createQuery("FROM Student s WHERE s.stuId = ?").setParameter(stuId).list(); 

Hibernate在使用過程中有很多不足:

  • 全表映射不靈活,更新時需要發送所有字段,影響程序運行效率。
  • 對復雜查詢的支持很差。
  • 對存儲過程的支持很差。
  • HQL性能較差,無法根據SQL進行優化。

在審計Hibernate相關注入時,可以通過全局搜索createQuery來快速定位SQL操作的位置。

2. JPA

JPA全稱為Java Persistence API,是Java EE提供的一種數據持久化的規范,允許開發者通過XML或注解的方式,將某個對象,持久化到數據庫中。

主要包括三方面內容:

(1) ORM映射元數據,通過XML或注解,描述對象和數據表之間的對應關系。框架便可以自動將對象中的數據保存到數據庫中。

常見的注解有:@Entity、@Table、@Column、@Transient

(2) 數據操作API,內置接口,方便對某個數據表執行CRUD操作,節省開發者編寫SQL的時間。

常見的方法有:entityManager.merge(T t);

(3) JPQL, 提供一種面向對象而不是面向數據庫的查詢語言,將程序和數據庫、SQL解耦合。

JPA是一套規范,Hibernate實現了這一JPA規范。

在Spring框架中,提供了簡易版的JPA實現——spirng data jpa。按照約定好的方法命名規則寫dao層接口,就可以在不寫接口實現的情況下,實現對數據庫的訪問和操作。同時提供了很多除了CRUD之外的功能,如分頁、排序、復雜查詢等等。使用起來更簡單,但底層仍然在使用Hibernate的JPA實現。

和HQL注入一樣,如果使用拼接的方式,將用戶可控的數據代入了查詢語句中,就會導致SQL注入。

安全的查詢應該使用預編譯技術。

Spring Data JPA的預編譯寫法為:

  1. String getUser = "SELECT username FROM users WHERE id = ?"
  2. Query query = em.createNativeQuery(getUser); 
  3. query.setParameter(1, id); 
  4. String username = query.getResultList(); 

小貼士:其實Hibernate的出現日期比JPA規范要早,Hibernate逐漸成熟之后,JavaEE的開發團隊,邀請Hibernate核心開發人員一起制定了JPA規范。之后Spring Data JPA按照規范做了進一步優化。除此之外,JPA規范的實現有很多產品,比如Eclipse的TopLink(OracleLink)。

六、 總結

經過上面的介紹,尤其是圍繞Mybatis易錯點的討論,我們可以得到以下結論:

  • 持久層組件種類繁多。
  • 開發者對工具使用的錯誤理解,是漏洞出現的主要原因。
  • 由于自動生成插件的動態特性,自動化發現SQL漏洞不能簡單地使用${}來尋找。必須要根據全局的持久層組件特性,來做詳細的匹配規則。

【本文為51CTO專欄作者“阿里巴巴官方技術”原創稿件,轉載請聯系原作者】

戳這里,看該作者更多好文

 

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2020-02-19 10:45:04

開發技能代碼

2009-11-02 13:47:09

2009-10-25 13:32:09

2009-02-12 10:14:16

2010-10-22 15:18:18

SQL注入漏洞

2011-12-26 11:22:48

2010-07-14 09:52:50

SQL Server服

2012-12-19 10:36:06

2009-06-03 15:23:18

Struts教程亂碼

2022-04-06 09:28:04

漏洞SQL注入

2009-10-23 13:08:23

2010-09-08 13:42:06

2012-04-12 15:06:44

2023-12-01 16:21:42

2018-03-29 10:16:04

2014-12-04 15:01:13

2017-05-02 09:02:14

2022-02-07 19:17:56

SQL系統MySQL

2014-10-11 11:44:02

2024-12-04 13:33:43

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品久久久久久久久久久久久久久久久 | 在线观看国产视频 | 国产精品久久片 | 91在线看片| 国产亚洲一区二区三区 | 免费一区二区 | 91精品国产高清一区二区三区 | 亚洲a在线视频 | www.国产一区 | 久久久久久久久国产 | 亚洲视频中文字幕 | av中文字幕在线观看 | 免费一级片| 国产精品久久久久久久久久免费看 | 成人高清视频在线观看 | 日韩av一区在线观看 | 欧美日日 | 日日操视频 | 性色av香蕉一区二区 | 精品日韩一区二区 | 日韩天堂av | 亚洲成人精品免费 | 麻豆av网站 | 欧美激情一区二区三级高清视频 | 特黄毛片| 亚洲一区 | 成人亚洲在线 | 亚洲 中文 欧美 日韩 在线观看 | 国产精品一区二区不卡 | 欧美精品片 | 中文字幕av亚洲精品一部二部 | 亚洲精品福利视频 | 欧美成人一区二免费视频软件 | 日韩福利在线 | 91aiai| 夜夜夜操| 欧美成人aaa级毛片在线视频 | 九九精品在线 | 日韩在线观看中文字幕 | 国产美女永久免费无遮挡 | 国产91久久精品一区二区 |