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中mod()函数取余数的用法
May 29 SQL Server
利用 SQL Server 过滤索引提高查询语句的性能分析
Jul 15 SQL Server
SQL语句中JOIN的用法场景分析
Jul 25 SQL Server
Windows环境下实现批量执行Sql文件
Oct 05 SQL Server
SQL SERVER实现连接与合并查询
Feb 24 SQL Server
SQL Server使用导出向导功能
Apr 08 SQL Server
SQL Server Agent 服务无法启动
Apr 20 SQL Server
SQL Server中的游标介绍
May 20 SQL Server
SQL Server中使用表变量和临时表
May 20 SQL Server
SQL Server使用PIVOT与unPIVOT实现行列转换
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
一个可以删除字符串中HTML标记的PHP函数
2006/10/09 PHP
探讨Smarty中如何获取数组的长度以及smarty调用php函数的详解
2013/06/20 PHP
PHP中session变量的销毁
2014/02/27 PHP
Yii实现复选框批量操作实例代码
2017/03/15 PHP
PHP实现的链式队列结构示例
2017/09/15 PHP
asp.net和asp下ACCESS的参数化查询
2008/06/11 Javascript
javascript开发技术大全-第3章 js数据类型
2011/07/03 Javascript
js特殊字符转义介绍
2013/11/05 Javascript
Node.js中的事件驱动编程详解
2014/08/16 Javascript
使用nodejs开发cli项目实例
2015/06/03 NodeJs
jQuery mobile 移动web(6)
2015/12/20 Javascript
JS日程管理插件FullCalendar中文说明文档
2017/02/06 Javascript
React.js中常用的ES6写法总结(推荐)
2017/05/09 Javascript
react-router4 嵌套路由的使用方法
2017/07/24 Javascript
JS实现网页抢购功能(触发,终止脚本)
2017/11/27 Javascript
jQuery 操作 HTML 元素和属性的方法
2018/11/12 jQuery
js实现3D照片墙效果
2019/10/28 Javascript
利用Python中的pandas库对cdn日志进行分析详解
2017/03/07 Python
Python中elasticsearch插入和更新数据的实现方法
2018/04/01 Python
python环境路径配置以及命令行运行脚本
2019/04/02 Python
Django 对IP访问频率进行限制的例子
2019/08/30 Python
结合CSS3的布局新特征谈谈常见布局方法
2016/01/22 HTML / CSS
HTML5实现Notification API桌面通知功能
2016/03/02 HTML / CSS
MUGLER官方网站:蒂埃里·穆勒香水
2019/11/26 全球购物
Why do we need Unit test
2013/01/03 面试题
酒吧副总经理岗位职责
2013/12/10 职场文书
精彩自我鉴定
2014/01/16 职场文书
大家检讨书5000字
2014/02/03 职场文书
电力公司个人求职信范文
2014/02/04 职场文书
网络工程师职业规划
2014/02/10 职场文书
食品质量与安全专业毕业生求职信
2014/08/11 职场文书
三关爱志愿服务活动方案
2014/08/17 职场文书
2014大学校园光棍节活动策划书
2014/09/29 职场文书
演讲比赛主持词
2015/06/29 职场文书
2019年年中职场激励人心语录30条
2019/08/07 职场文书
详解ZABBIX监控ESXI主机的问题
2022/06/21 Servers