網站首頁 編程語言 正文
#{} 和 ${} 的區別
#{}
匹配的是一個占位符,相當于 JDBC 中的一個?,會對一些敏感字符進行過濾,編譯過后會對傳遞的值加上雙引號,因此可以防止 SQL 注入問題。
${}
匹配的是真實傳遞的值,傳遞過后,會與 SQL 語句進行字符串拼接。${} 會與其他 SQL 進行字符串拼接,無法防止 SQL 注入問題。
<mapper namespace="com.gitee.shiayanga.mybatis.wildcard.dao.UserDao"> <select id="findByUsername" resultType="com.gitee.shiayanga.mybatis.wildcard.entity.User" parameterType="string"> select * from user where username like #{userName} </select> <select id="findByUsername2" resultType="com.gitee.shiayanga.mybatis.wildcard.entity.User" parameterType="string"> select * from user where username like '%${userName}%' </select> </mapper>
==> Preparing: select * from user where username like ? ==> Parameters: '%小%' or 1=1 --(String) <== Total: 0 ==> Preparing: select * from user where username like '%aaa' or 1=1 -- %' ==> Parameters: <== Total: 4
#{} 底層是如何防止 SQL 注入的?
- #{} 底層采用的是 PreparedStatement,因此不會產生 SQL 注入問題
- #{} 不會產生字符串拼接,而 ${} 會產生字符串拼接
為什么能防止SQL注入?
以MySQL為例,#{} 使用的是 com.mysql.cj.ClientPreparedQueryBindings#setString 方法,在這里會對一些特殊字符進行處理:
public void setString(int parameterIndex, String x) { if (x == null) { setNull(parameterIndex); } else { int stringLength = x.length(); if (this.session.getServerSession().isNoBackslashEscapesSet()) { // Scan for any nasty chars boolean needsHexEscape = isEscapeNeededForString(x, stringLength); if (!needsHexEscape) { StringBuilder quotedString = new StringBuilder(x.length() + 2); quotedString.append('\''); quotedString.append(x); quotedString.append('\''); byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(quotedString.toString()) : StringUtils.getBytes(quotedString.toString(), this.charEncoding); setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR); } else { byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(x) : StringUtils.getBytes(x, this.charEncoding); setBytes(parameterIndex, parameterAsBytes); } return; } String parameterAsString = x; boolean needsQuoted = true; if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) { needsQuoted = false; // saves an allocation later StringBuilder buf = new StringBuilder((int) (x.length() * 1.1)); buf.append('\''); // // Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure... // for (int i = 0; i < stringLength; ++i) { char c = x.charAt(i); switch (c) { case 0: /* Must be escaped for 'mysql' */ buf.append('\\'); buf.append('0'); break; case '\n': /* Must be escaped for logs */ buf.append('\\'); buf.append('n'); break; case '\r': buf.append('\\'); buf.append('r'); break; case '\\': buf.append('\\'); buf.append('\\'); break; case '\'': buf.append('\''); buf.append('\''); break; case '"': /* Better safe than sorry */ if (this.session.getServerSession().useAnsiQuotedIdentifiers()) { buf.append('\\'); } buf.append('"'); break; case '\032': /* This gives problems on Win32 */ buf.append('\\'); buf.append('Z'); break; case '\u00a5': case '\u20a9': // escape characters interpreted as backslash by mysql if (this.charsetEncoder != null) { CharBuffer cbuf = CharBuffer.allocate(1); ByteBuffer bbuf = ByteBuffer.allocate(1); cbuf.put(c); cbuf.position(0); this.charsetEncoder.encode(cbuf, bbuf, true); if (bbuf.get(0) == '\\') { buf.append('\\'); } } buf.append(c); break; default: buf.append(c); } } buf.append('\''); parameterAsString = buf.toString(); } byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(parameterAsString) : (needsQuoted ? StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charEncoding) : StringUtils.getBytes(parameterAsString, this.charEncoding)); setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR); } }
所以 '%小%' or 1=1 --
?經過處理之后就變成了 '''%小%'' or 1=1 --'
而 ${} 只是簡單的拼接字符串,不做其他處理。
這樣,它們就變成了:
-- %aaa' or 1=1 -- select * from user where username like '%aaa' or 1=1 -- %' -- '%小%' or 1=1 -- select * from user where username like '''%小%'' or 1=1 --'
所以就避免了 SQL 注入的風險。
原文鏈接:https://juejin.cn/post/7095247748126998535
- 上一篇:c++詳細講解構造函數的拷貝流程_C 語言
- 下一篇:c++超細致講解引用_C 語言
相關推薦
- 2022-04-17 Spring Security前后端分離實現
- 2022-10-14 Redis常見分布鎖的原理和實現_Redis
- 2023-05-26 keras.layers.Conv2D()函數參數用法及說明_python
- 2022-11-17 Python?pyecharts模塊安裝與入門教程_python
- 2022-04-30 C/C++編程語言中的指針(pointer)你了解嗎_C 語言
- 2023-10-14 c/c++--字節對齊(byte alignment)
- 2022-09-18 iOS開發技能weak和strong修飾符的規范使用詳解_IOS
- 2023-04-14 使用?React?hooks?實現類所有生命周期_React
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支