SQL Server #{}可以防止SQL注入


Posted in SQL Server onMay 11, 2022

#{} 和 ${} 的区别

#{} 匹配的是一个占位符,相当于 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 注入的风险。

到此这篇关于浅谈为什么#{}可以防止SQL注入的文章就介绍到这了!


Tags in this post...

SQL Server 相关文章推荐
SQL Server数据定义——模式与基本表操作
Apr 05 SQL Server
Windows环境下实现批量执行Sql文件
Oct 05 SQL Server
SQL Server2019数据库备份与还原脚本,数据库可批量备份
Nov 20 SQL Server
SQLServer中exists和except用法介绍
Dec 04 SQL Server
SQL SERVER触发器详解
Feb 24 SQL Server
sql时间段切分实现每隔x分钟出一份高速门架车流量
Feb 28 SQL Server
详解在SQLPlus中实现上下键翻查历史命令的功能
Mar 18 SQL Server
SQL CASE 表达式的具体使用
Mar 21 SQL Server
SQL Server数据库查询出现阻塞之性能调优
Apr 10 SQL Server
SQL Server中T-SQL标识符介绍与无排序生成序号的方法
May 25 SQL Server
SQL使用复合索引实现数据库查询的优化
May 25 SQL Server
一次SQL查询优化原理分析(900W+数据从17s到300ms)
Jun 10 SQL Server
SQL Server 忘记密码以及重新添加新账号
使用 MybatisPlus 连接 SqlServer 数据库解决 OFFSET 分页问题
Apr 22 #SQL Server
使用MybatisPlus打印sql语句
Apr 22 #SQL Server
Sql Server 行数据的某列值想作为字段列显示的方法
SQL Server Agent 服务无法启动
Apr 20 #SQL Server
SQLServer权限之只开启创建表权限
如何使用SQL Server语句创建表
Apr 12 #SQL Server
You might like
php 移除数组重复元素的一点说明
2008/11/27 PHP
理解php Hash函数,增强密码安全
2011/02/25 PHP
DOM下的节点属性和操作小结
2009/05/14 Javascript
JavaScript自定义事件介绍
2013/08/29 Javascript
Extjs4实现两个GridPanel之间数据拖拽功能具体方法
2013/11/21 Javascript
JQuery获取与设置HTML元素的内容或文本的实现代码
2014/06/20 Javascript
Jquery中find与each方法用法实例
2015/02/04 Javascript
JavaScript监听文本框回车事件并过滤文本框空格的方法
2015/04/16 Javascript
基于JavaScript实现全屏透明遮罩div层锁屏效果
2016/01/26 Javascript
JavaScript编写检测用户所使用的浏览器的代码示例
2016/05/05 Javascript
JS常用函数和常用技巧小结
2016/10/15 Javascript
javascript 中的继承实例详解
2017/05/05 Javascript
input框中自动展示当前日期yyyy/mm/dd的实现方法
2017/07/06 Javascript
浅谈Emergence.js 检测元素可见性的 js 插件
2017/11/18 Javascript
详解微信小程序网络请求接口封装实例
2019/05/02 Javascript
微信小程序 Storage更新详解
2019/07/16 Javascript
vue中通过使用$attrs实现组件之间的数据传递功能
2019/09/01 Javascript
vue实现移动端H5数字键盘组件使用详解
2020/08/25 Javascript
[02:28]DOTA2亚洲邀请赛附加赛 RECAP赛事回顾
2015/01/29 DOTA
python基础教程之分支、循环简单用法
2016/06/16 Python
Python使用回溯法子集树模板解决迷宫问题示例
2017/09/01 Python
python的构建工具setup.py的方法使用示例
2017/10/23 Python
Python从零开始创建区块链
2018/03/06 Python
Python操作mongodb数据库的方法详解
2018/12/08 Python
python数据处理 根据颜色对图片进行分类的方法
2018/12/08 Python
在Python中实现函数重载的示例代码
2019/12/12 Python
bonprix匈牙利:女士、男士和儿童服装
2019/07/19 全球购物
全球采购的街头服饰和帽子:Urban Excess
2020/10/28 全球购物
中式婚礼主持词
2014/03/13 职场文书
实习生工作证明范本
2014/09/14 职场文书
诚信承诺书
2015/01/19 职场文书
2015年七一建党节活动方案
2015/05/05 职场文书
员工福利申请报告
2015/05/15 职场文书
观后感的写法
2015/06/19 职场文书
运动会班级口号霸气押韵
2015/12/24 职场文书
2019最新婚庆对联集锦!
2019/07/10 职场文书