V&N面試得到的收獲
前言
前兩天面了V&N,面試中遇見了很多問題,給我面試的師傅也熱心的給出了解答。
先找一下自己得不足叭:
- 開發不夠深入,對java得理解還是不夠熟悉和了解。
- 爆肝了一段時間的ctf題,但是缺少對文章及其他重要的東西得研究,P3師傅也說不能只面向題目
- 知識沒有很好的聯系起來,理解不夠透徹
- 面試時好緊張啊,想問題也不夠全面,看到許多的大師傅,心里發慌。要好好研究技術了。
關於sql注入&預編譯
引用師傅給我的問題
當時還好打開了自己的項目看了一眼,仔細看看自己寫過的項目,發現拼表操作確實是這樣的
(簡單操作得Mapper.xml都是自己寫,比較繁雜的都百度,沒有好好研究吃了大虧),
原來確實有一些參數,還是必須使用${}得。
下面對原理進行解讀,大佬輕噴,敬請批證。參考了網上各路大佬的解釋。
什么是預編譯
sql 預編譯指的是數據庫驅動在發送 sql 語句和參數給 DBMS 之前對 sql 語句進行編譯,這樣 DBMS 執行 sql 時,就不需要重新編譯。
可以理解為,參數化處理得方式把我們傳入的值全部團成一團,丟進去。
${}和#{}得區別
${}"是做簡單的字符串替換,即將傳入的值直接拼接到SQL語句中,且不會自動加單引號。
"#{}"是將傳入的值先用?作為占位符替換,之后在兩邊加上單引號,以字符串方式處理
${ } 的變量的替換階段是在動態 SQL 解析階段,而 #{ }的變量的替換是在 DBMS
例如:
select * from userInfo where userName = #{name};
會被解析為
select * from userInfo where userName = ?;
${}和#{}用法提示(很重要)
-
能使用 #{ } 的地方就用 #{ },這樣得好處有兩點:
-
性能考慮,相同的預編譯 sql 可以重復利用。
-
預編譯這會很好的處理 sql 注入問題
-
-
表名作為變量時,必須使用 ${ }
原理解釋:
因為,表名是字符串,使用 sql 占位符替換字符串時會帶上單引號
''
,會導致 sql 語法錯誤原來如此……(●ˇ∀ˇ●)。
那么我們舉個例子叭,例如:
mapper.xml中寫了
select * from #{tableName} where name = #{name};
預編譯之后的sql 變為:
select * from ? where name = ?;
假設傳入的參數為
tableName = "userInfo" , name = "h3zh1",
那么在占位符進行變量替換后,sql 語句變為
select * from 'userInfo' where name='h3zh1';
很明顯'userInfo'是語法錯誤得,單引號是不可以用在表名上的,但是Tab鍵上面的``確實可以奧,下圖可以作為參考。
-
order by必須用${}
order by也是同理!
order by后一般是接字段名,而字段名是不能帶引號的。
繼續舉例~
select * from 'userInfo' order by id; 如果我們應用了#{}預編譯來參數化。 那么id就成了'id'。 select * from 'userInfo' order by 'id'; 語法錯誤,當然就game over了
多說點:
like關鍵字也是可以預編譯的,不過需要一些簡單的處理。
不只order by,凡是字符串但又不能加引號的位置都不能參數化;包括sql關鍵字、庫名表名字段名、函數名等等。
!此處強調 order by注入應該是個要關注的地方了,因為他即是不可以預編譯的點,也是很常用的點.
必須使用${}時,好的防御手段
這一段參考大佬的博客。
不能參數化,不管怎么拼接,最終都是和使用“+”號拼接字符串的功效一樣:拼成了sql語句缺不能防御sql注入的效果。
好在的一點是,不管是sql關鍵字,還是庫名、表名、字段名、函數名對於后台開發者來說他的集合都是有限的,更准確點應該說也就那么幾個。
這時我們應可以使用白名單的這種針對有限集合最常用的處理辦法進行處理,如果傳來的參數不在白名單列表中,直接返回錯誤即可。
我參考大佬寫的if句,仿寫了一個比較簡單代碼示例 (param in whiteList)簡單表示:如果參數在白名單中則為true , 否則為false。
public void juge(String param){
if( param in whiteList){
System.out.println("你得參數是白名單可以通過檢驗");
} else {
System.out.println("你得參數不能通過白名單的校驗");
}
}
最后補充幾個師傅教我的知識
問題:
如果我mybatis的jdbc寫了 select * from xxx where id= #{id}
並且jdbc開啟了useServerPrepStmts=true
如果我查的是mysql 然后我用wireshark抓包 一次查詢我能抓到幾條發往mysql端口的報文?
答案:
答案是兩條 第一條預編譯報文 第二條查詢報文 如果算關閉連接報文的話 三條
<!-- Intger類型的預編譯 -->
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_book
where id = #{id,jdbcType=INTEGER}
</select>
<!-- String類型 -->
<select id="selectByName" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_book
where name = #{name,jdbcType=INTEGER}
</select>
<!-- !!!!!!!!!!!!強調一下like關鍵字也是可以預編譯的 -->
<select id="selectByFuzz" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_book
where name like concat('%',#{name,jdbcType=INTEGER},'%') limit 1
</select>
截圖:
預編譯:
執行:
預編譯:
執行:
作為一個高齡又菜的離譜得ctfer壓力真得很大啊,很僥幸的過了面試,希望。
希望可以融入大家伙和師傅們一起學習。
參考:
https://www.cnblogs.com/lsdb/p/12084038.html
https://www.freebuf.com/articles/web/216336.html
https://www.iteye.com/blog/zhangxugg-163-com-1835721