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——索引+基于单表的数据插入与简单查询【1】
Apr 05 SQL Server
SQL Server数据定义——模式与基本表操作
Apr 05 SQL Server
SQLServer2019 数据库的基本使用之图形化界面操作的实现
Apr 08 SQL Server
SQL Server中交叉联接的用法详解
Apr 22 SQL Server
SQL语句中JOIN的用法场景分析
Jul 25 SQL Server
SQL写法--行行比较
Aug 23 SQL Server
SQL SERVER存储过程用法详解
Feb 24 SQL Server
SQL Server实现分页方法介绍
Mar 16 SQL Server
SQLServer RANK() 排名函数的使用
Mar 23 SQL Server
SQL Server中搜索特定的对象
May 25 SQL Server
SQL Server中T-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
虫族 Zerg 魔法科技
2020/03/14 星际争霸
PHP中的命名空间相关概念浅析
2015/01/22 PHP
Gird事件机制初级读本
2007/03/10 Javascript
Sample script that deletes a SQL Server database
2007/06/16 Javascript
JS中判断null、undefined与NaN的方法
2014/03/24 Javascript
jquery中push()的用法(数组添加元素)
2014/11/25 Javascript
jQuery插件制作之全局函数用法实例
2015/06/01 Javascript
盘点javascript 正则表达式中 中括号的【坑】
2016/03/16 Javascript
详解JavaScript中|单竖杠运算符的使用方法
2016/05/23 Javascript
bootstrap和jQuery.Gantt的css冲突 如何解决
2016/05/29 Javascript
浅谈jQuery双事件多重加载的问题
2016/10/05 Javascript
简单实现JS倒计时效果
2016/12/23 Javascript
HTML中使背景图片自适应浏览器大小实例详解
2017/04/06 Javascript
NodeJS实现不可逆加密与密码密文保存的方法
2018/03/16 NodeJs
微信小程序网络封装(简单高效)
2018/08/06 Javascript
Vue作用域插槽slot-scope实例代码
2018/09/05 Javascript
react脚手架如何配置less和ant按需加载的方法步骤
2018/11/28 Javascript
[53:10]2018DOTA2亚洲邀请赛 4.6 淘汰赛 VP vs VG 第一场
2018/04/11 DOTA
python插入排序算法实例分析
2015/07/03 Python
Python编程使用*解包和itertools.product()求笛卡尔积的方法
2017/12/18 Python
django如何实现视图重定向
2019/07/24 Python
Django 404、500页面全局配置知识点详解
2020/03/10 Python
详解python对象之间的交互
2020/09/29 Python
主持人演讲稿范文
2013/12/28 职场文书
公民代理授权委托书
2014/09/24 职场文书
学校领导班子四风问题整改意见
2014/10/02 职场文书
运动会广播稿200字
2014/10/18 职场文书
2015中秋节慰问信范文
2015/03/23 职场文书
党小组意见范文
2015/06/08 职场文书
校长新学期致辞
2015/07/30 职场文书
感恩父母主题班会
2015/08/12 职场文书
2016国培研修心得体会
2016/01/08 职场文书
个人工作总结(管理人员)范文
2019/08/13 职场文书
python 制作一个gui界面的翻译工具
2021/05/14 Python
Python实现简繁体转换
2021/06/07 Python
OpenCV-Python模板匹配人眼的实例
2021/06/08 Python