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 相关文章推荐
2021-4-5课程——SQL Server查询【3】
Apr 05 SQL Server
SQLServer2008提示评估期已过解决方案
Apr 12 SQL Server
sql查询结果列拼接成逗号分隔的字符串方法
May 25 SQL Server
在 SQL 语句中处理 NULL 值的方法
Jun 07 SQL Server
SQL 尚未定义空闲 CPU 条件 - OnIdle 作业计划将不起任何作用
Jun 30 SQL Server
SQLServer中exists和except用法介绍
Dec 04 SQL Server
SQL Server远程连接的设置步骤(图文)
Mar 23 SQL Server
SQL Server表分区降低运维和维护成本
Apr 08 SQL Server
SQL SERVER中的流程控制语句
May 25 SQL Server
SQL Server使用CROSS APPLY与OUTER APPLY实现连接查询
May 25 SQL Server
在SQL Server中使用 Try Catch 处理异常的示例详解
Jul 15 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
Content-type 的说明
2006/10/09 PHP
PHP header()函数使用详细(301、404等错误设置)
2013/04/17 PHP
基于php设计模式中单例模式的应用分析
2013/05/15 PHP
详解php中反射的应用
2016/03/15 PHP
php实现支持中文的文件下载功能示例
2017/08/30 PHP
php-fpm中max_children的配置
2019/03/15 PHP
jQuery Selector选择器小结
2010/05/06 Javascript
jQuery源码分析-01总体架构分析
2011/11/14 Javascript
JavaScript高级程序设计 阅读笔记(七) ECMAScript中的语句
2012/02/27 Javascript
Highcharts 非常实用的Javascript统计图demo示例
2013/07/03 Javascript
jquery用get实现ajax在ie里面刷新不进入后台解决方法
2013/08/12 Javascript
Jquery利用mouseenter和mouseleave实现鼠标经过弹出层且可以点击
2014/02/12 Javascript
js+css实现tab菜单切换效果的方法
2015/01/20 Javascript
jquery图片滚动放大代码分享(1)
2015/08/25 Javascript
基于BootStrap Metronic开发框架经验小结【二】列表分页处理和插件JSTree的使用
2016/05/12 Javascript
基于javascript实现数字英文验证码
2017/01/25 Javascript
js学习总结之DOM2兼容处理重复问题的解决方法
2017/07/27 Javascript
薪资那么高的Web前端必看书单
2017/10/13 Javascript
vue中使用iview自定义验证关键词输入框问题及解决方法
2018/03/26 Javascript
React组件内事件传参实现tab切换的示例代码
2018/07/04 Javascript
vue源码nextTick使用及原理解析
2019/08/13 Javascript
Element Card 卡片的具体使用
2020/07/26 Javascript
python操作MySQL 模拟简单银行转账操作
2017/09/27 Python
Python实现处理逆波兰表达式示例
2018/07/30 Python
pycharm 配置远程解释器的方法
2018/10/28 Python
python字典一键多值实例代码分享
2019/06/14 Python
Python MySQLdb 执行sql语句时的参数传递方式
2020/03/04 Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
2020/04/26 Python
浅析CSS3 用text-overflow解决文字排版问题
2020/10/28 HTML / CSS
HTML5-WebSocket实现聊天室示例
2016/12/15 HTML / CSS
英国翻新电子产品购物网站:Tech Trade
2017/12/25 全球购物
授权委托书样本及填写说明
2014/09/19 职场文书
领导班子作风建设剖析材料
2014/10/11 职场文书
收银员岗位职责范本
2015/04/07 职场文书
Nginx优化服务之网页压缩的实现方法
2021/03/31 Servers
Python图片检索之以图搜图
2021/05/31 Python