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数据库单表查询 4.3实验课
Apr 05 SQL Server
SQLServer中JSON文档型数据的查询问题解决
Jun 27 SQL Server
SQL中的三种去重方法小结
Nov 01 SQL Server
SQLServer中exists和except用法介绍
Dec 04 SQL Server
详解在SQLPlus中实现上下键翻查历史命令的功能
Mar 18 SQL Server
SQL Server数据库查询出现阻塞之性能调优
Apr 10 SQL Server
MSSQL基本语法操作
Apr 11 SQL Server
使用MybatisPlus打印sql语句
Apr 22 SQL Server
SQL Server中搜索特定的对象
May 25 SQL Server
SQL Server使用CROSS APPLY与OUTER APPLY实现连接查询
May 25 SQL Server
SQL Server中的逻辑函数介绍
May 25 SQL Server
SQL使用复合索引实现数据库查询的优化
May 25 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
vBulletin HACK----显示话题大小和打开新窗口于论坛索引页
2006/10/09 PHP
linux下 C语言对 php 扩展
2008/12/14 PHP
php中++i 与 i++ 的区别
2012/08/08 PHP
解决PHP mysql_query执行超时(Fatal error: Maximum execution time …)
2013/07/03 PHP
php截取html字符串及自动补全html标签的方法
2015/01/15 PHP
ThinkPHP和UCenter接口冲突的解决方法
2016/07/25 PHP
php中array_slice和array_splice函数解析
2016/10/18 PHP
Js之软键盘实现(js源码)
2007/01/30 Javascript
json简单介绍
2008/06/10 Javascript
firefox firebug中文入门教程 脚本之家新年特别版
2010/01/02 Javascript
javascript textContent与innerText的异同分析
2010/10/22 Javascript
JavaScript实现自己的DOM选择器原理及代码
2013/03/04 Javascript
js判断选择时间不能小于当前时间的示例代码
2013/09/24 Javascript
js中prototype用法详细介绍
2013/11/14 Javascript
js 实现的可折叠留言板(附源码下载)
2014/07/01 Javascript
js打造数组转json函数
2015/01/14 Javascript
详解addEventListener的三个参数之useCapture
2015/03/16 Javascript
总结jQuery插件开发中的一些要点
2016/05/16 Javascript
微信小程序 常见问题总结(4058,40013)及解决办法
2017/01/11 Javascript
vue-cli入门之项目结构分析
2017/04/20 Javascript
在vscode中统一vue编码风格的方法
2018/02/22 Javascript
解决vue build打包之后首页白屏的问题
2018/03/06 Javascript
微信小程序动态生成二维码的实现代码
2018/07/25 Javascript
JavaScript数组排序小程序实现解析
2020/01/13 Javascript
vue 监听 Treeselect 选择项的改变操作
2020/08/31 Javascript
基于python的Paxos算法实现
2019/07/03 Python
使用CSS3制作倾斜导航条和毛玻璃效果
2017/09/12 HTML / CSS
Debenhams百货英国官方网站:Debenhams UK
2016/07/12 全球购物
伊利莎白雅顿官网:Elizabeth Arden
2016/10/10 全球购物
法国体育用品商店:GO Sport
2019/10/23 全球购物
企划主管岗位职责
2013/12/12 职场文书
大学生的四年学习自我评价
2013/12/13 职场文书
大学生职业生涯设计书
2014/01/02 职场文书
电大会计学自我鉴定
2014/02/06 职场文书
汽车促销活动方案
2014/03/31 职场文书
2014年体育教学工作总结
2014/12/09 职场文书