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 相关文章推荐
SQLServer2019 数据库环境搭建与使用的实现
Apr 08 SQL Server
mybatis调用sqlserver存储过程返回结果集的方法
May 08 SQL Server
如何有效防止sql注入的方法
May 25 SQL Server
SQL Server中使用判断语句(IF ELSE/CASE WHEN )案例
Jul 07 SQL Server
SQL中的三种去重方法小结
Nov 01 SQL Server
SQL SERVER存储过程用法详解
Feb 24 SQL Server
详解在SQLPlus中实现上下键翻查历史命令的功能
Mar 18 SQL Server
MSSQL基本语法操作
Apr 11 SQL Server
SQLServer权限之只开启创建表权限
Apr 12 SQL Server
SQL Server Agent 服务无法启动
Apr 20 SQL Server
SQL Server一个字符串拆分多行显示或者多行数据合并成一个字符串
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
php.ini中的php-5.2.0配置指令详解
2008/03/27 PHP
php mssql 日期出现中文字符的解决方法
2009/03/10 PHP
zf框架db类的分页示例分享
2014/03/14 PHP
PHP 无限级分类
2017/05/04 PHP
php实现websocket实时消息推送
2018/03/30 PHP
JavaScript入门教程(11) js事件处理
2009/01/31 Javascript
javascript 日期常用的方法
2009/11/11 Javascript
JavaScript 的继承
2011/10/01 Javascript
jQuery scroll事件实现监控滚动条分页示例
2014/04/04 Javascript
用原生js做个简单的滑动效果的回到顶部
2014/10/15 Javascript
jQuery源码分析之jQuery.fn.each与jQuery.each用法
2015/01/23 Javascript
js实现一个链接打开两个链接地址的方法
2015/05/12 Javascript
JavaScript中style.left与offsetLeft的使用及区别详解
2016/06/08 Javascript
JavaScript探测CSS动画是否已经完成的方法
2016/08/30 Javascript
详解js的事件处理函数和动态创建html标记方法
2016/12/16 Javascript
详解无限滚动插件vue-infinite-scroll源码解析
2019/05/12 Javascript
Vue搭建后台系统需要注意的问题
2019/11/08 Javascript
python模块restful使用方法实例
2013/12/10 Python
Python数据结构之Array用法实例
2014/10/09 Python
举例讲解Python的Tornado框架实现数据可视化的教程
2015/05/02 Python
python实现在sqlite动态创建表的方法
2015/05/08 Python
python实现支付宝当面付(扫码支付)功能
2018/05/30 Python
python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧(自定义信号与槽)详解与实例
2020/03/06 Python
python获取命令行参数实例方法讲解
2020/11/02 Python
CSS伪类与CSS伪元素的区别及由来具体说明
2012/12/07 HTML / CSS
GUESS盖尔斯法国官网:美国时尚品牌
2016/09/23 全球购物
AVON雅芳官网:世界上最大的美容化妆品公司之一
2016/11/02 全球购物
VICHY薇姿英国官网:全球专业敏感肌护肤领先品牌
2017/07/04 全球购物
Zavvi荷兰:英国大型音像制品和图书游戏零售商
2018/03/22 全球购物
EJB与JAVA BEAN的区别
2016/08/29 面试题
企业安全生产责任书
2014/04/14 职场文书
学生检讨书范文
2014/10/30 职场文书
人与自然的观后感
2015/06/18 职场文书
党员观看《筑梦中国》心得体会
2016/01/18 职场文书
Python函数中的不定长参数相关知识总结
2021/06/24 Python
muduo TcpServer模块源码分析
2022/04/26 Redis