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 过滤索引提高查询语句的性能分析
Jul 15 SQL Server
sql通过日期判断年龄函数的示例代码
Jul 16 SQL Server
万能密码的SQL注入漏洞其PHP环境搭建及防御手段
Sep 04 SQL Server
SQL Server查询某个字段在哪些表中存在
Mar 03 SQL Server
详解在SQLPlus中实现上下键翻查历史命令的功能
Mar 18 SQL Server
MSSQL基本语法操作
Apr 11 SQL Server
使用MybatisPlus打印sql语句
Apr 22 SQL Server
使用 MybatisPlus 连接 SqlServer 数据库解决 OFFSET 分页问题
Apr 22 SQL Server
SQL Server 中的事务介绍
May 20 SQL Server
SQL Server中搜索特定的对象
May 25 SQL Server
SQL Server中T-SQL标识符介绍与无排序生成序号的方法
May 25 SQL Server
SQL Server2019安装的详细步骤实战记录(亲测可用)
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
第十一节--重载
2006/11/16 PHP
做了CDN获取用户真实IP的函数代码(PHP与Asp设置方式)
2013/04/13 PHP
php文件服务实现虚拟挂载其他目录示例
2014/04/17 PHP
PHP实现图片压缩的两则实例
2014/07/19 PHP
Thinkphp调用Image类生成缩略图的方法
2015/03/07 PHP
搭建基于Docker的PHP开发环境的详细教程
2015/07/01 PHP
项目实践之javascript技巧
2007/12/06 Javascript
{}与function(){}选用空对象{}来存放keyValue
2012/05/23 Javascript
jquery1.83 之前所有与异步列队相关的模块详细介绍
2012/11/13 Javascript
原生js结合html5制作简易的双色子游戏
2015/03/30 Javascript
JavaScript使用Prototype实现面向对象的方法
2015/04/14 Javascript
浅析JS原型继承与类的继承
2016/04/07 Javascript
easyui datagrid 大数据加载效率慢,优化解决方法(推荐)
2016/11/09 Javascript
详解在Vue中使用TypeScript的一些思考(实践)
2018/07/06 Javascript
简单说说angular.json文件的使用
2018/10/29 Javascript
vue 的 solt 子组件过滤过程解析
2019/09/07 Javascript
用原生JS实现爱奇艺首页导航栏代码实例
2019/09/19 Javascript
JavaScript实现点击自制菜单效果
2021/02/02 Javascript
python 字典(dict)遍历的四种方法性能测试报告
2014/06/25 Python
在Python程序中操作文件之flush()方法的使用教程
2015/05/24 Python
Python入门_学会创建并调用函数的方法
2017/05/16 Python
python笔记:mysql、redis操作方法
2017/06/28 Python
Python使用cx_Freeze库生成msi格式安装文件的方法
2018/07/10 Python
对python的bytes类型数据split分割切片方法
2018/12/04 Python
Python单元和文档测试实例详解
2019/04/11 Python
Python enumerate内置库用法解析
2020/02/24 Python
基于tensorflow for循环 while循环案例
2020/06/30 Python
python pillow库的基础使用教程
2021/01/13 Python
HTML5微信播放全屏问题的解决方法
2017/03/09 HTML / CSS
欧洲著名的珠宝和手表网上商城:uhrcenter
2017/04/10 全球购物
急诊科护士自我鉴定
2013/10/14 职场文书
普师专业个人自荐信范文
2013/11/26 职场文书
2014年学校党建工作汇报材料
2014/11/02 职场文书
成本低的5个创业项目:投资小、赚钱快
2019/08/20 职场文书
python神经网络编程之手写数字识别
2021/05/08 Python
Python中常见的反爬机制及其破解方法总结
2021/06/10 Python