一次mybatis中ognl引發的bug排查
現象
專案組一妹子程式設計師求助,說mybatis有bug,有一個值明明設定的是A.prop1=XXX,但是存到資料庫裡面卻會自動變成A.prop1=true,嘗試了各種調整也找不原因,都快急瘋了!我以前確實沒有研究過mybatis原始碼,本著專(ba)研(mei)問(zhuang)題(b)的精神,決定通過debug對mybais一探究竟。
定位
debugDao的入口類便是org.apache.ibatis.binding.MapperProxy ,可以看到它實現了InvocationHandler,很明顯mybatis使用了jdk的動態代理,參見檢視我以前關於動態代理類的培訓ppt如下,那麼它一定有地方使用了newProxyInstance生成代理類,果然在MapperProxyFactory中就可以找到對應的方法,只不過這次動態代理是對純介面進行代理,而不是對實現類代理(當然滿足動態代理中被代理類需要實現介面的要求了!)
解決
mybatis中會更具mapper檔案生成一個SqlNodeTree,然後根據入參的資料有選擇的生成最終的SQL,例如mapper檔案中支援<if test>語法,if test的條件動態決定update或者where的sql語句是否存在,例如傳過來的A物件中沒有prop1屬性,那麼if test A.prop1!=null判斷一下,如果沒有的話,最終的sql就不包含update prop1=?,達到靈活和提高效能的目的。這個ifSqlNode就是mapper if test語法的具體Node,但是這個node應該只是判斷是否存在這個條件啊!怎麼會改值的? 進入到具體的判斷方法內部,發現了原來這邊判斷if test是使用的Ognl表示式引擎啊,Ognl是一個功能非常強大的JAVA表示式引擎,但是由於過於強大了,導致使用它的Struts2漏洞漫天飛,你Http請求中傳參'Runtime.getRuntime("shutdown")',它真的就給你執行關機了你敢信!!。
/* */ public boolean evaluateBoolean(String expression, Object parameterObject)
/* */ {
/* 29 */ Object value = OgnlCache.getValue(expression, parameterObject);
/* 30 */ if (value instanceof Boolean) return ((Boolean)value).booleanValue();
/* 31 */ if (value instanceof Number) return (!(new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO)));
/* 32 */ return (value != null);
/* */ }
敏銳的我對著expression看了一遍又一遍,突然,我眼花了麼?
A.prop1=!null !
A.prop1=!null !!
A.prop1=!null !!!
你妹啊! "!="寫成了"=!"!ognl又一次立功了,在需要它判斷的時候,它忠實的執行了賦值(怪OGNL不怪妹子是幾個意思?),修改了mapper檔案中的錯誤,終於恢復了正常!
擴充套件
上網搜尋下看看有沒有犯同樣錯誤的同學,沒想到還真有人在mybatis中這樣玩OGNL的,根據if test判斷的結果給table名賦值,無疑是提供了一種嶄(hun)新(luan)的思路!! 給你們這些挖坑的跪了!
<if test="@[email protected](_parameter)
or dynamicTableName == null
or dynamicTableName == ''">
select from ${dynamicTableName }