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-3课程——SQL Server查询【2】
Apr 05 SQL Server
SQL Server数据定义——模式与基本表操作
Apr 05 SQL Server
SQL Server中交叉联接的用法详解
Apr 22 SQL Server
SQL Server代理:理解SQL代理错误日志处理方法
Jun 30 SQL Server
通过T-SQL语句创建游标与实现数据库加解密功能
Mar 16 SQL Server
SQL Server表分区降低运维和维护成本
Apr 08 SQL Server
Sql Server 行数据的某列值想作为字段列显示的方法
Apr 20 SQL Server
SQL Server中使用表变量和临时表
May 20 SQL Server
SQL Server携程核心系统无感迁移到MySQL实战
Jun 01 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
smarty 缓存控制前的页面静态化原理
2013/03/15 PHP
php漏洞之跨网站请求伪造与防止伪造方法
2013/08/15 PHP
Linux下php5.4启动脚本
2014/08/03 PHP
浅谈php错误提示及查错方法
2015/07/14 PHP
PHP实现的多文件上传类及用法示例
2016/05/06 PHP
javascript add event remove event
2008/04/07 Javascript
JavaScript开发规范要求(规范化代码)
2010/08/16 Javascript
JavaScript原型继承之基础机制分析
2011/08/26 Javascript
jquery插件制作 图片走廊 gallery
2012/08/17 Javascript
JS去掉第一个字符和最后一个字符的实现代码
2014/02/20 Javascript
jquery实现动态画圆
2014/12/04 Javascript
JS实现三级折叠菜单特效,其它级可自动收缩
2015/08/06 Javascript
jQuery实现带滑动条的菜单效果代码
2015/08/26 Javascript
Javascript实现鼠标框选操作  不是点击选取
2016/04/14 Javascript
基于JS如何实现类似QQ好友头像hover时显示资料卡的效果(推荐)
2016/06/09 Javascript
JavaScript对象数组如何按指定属性和排序方向进行排序
2016/06/15 Javascript
Linux使用Node.js建立访问静态网页的服务实例详解
2017/03/21 Javascript
详解Vue.js组件可复用性的混合(mixin)方式和自定义指令
2017/09/06 Javascript
JavaScript实现的原生态兼容IE6可调可控滚动文字功能详解
2017/09/19 Javascript
js最简单的双向绑定实例讲解
2018/01/02 Javascript
vue 录制视频并压缩视频文件的方法
2018/07/27 Javascript
angularJs select绑定的model取不到值的解决方法
2018/10/08 Javascript
详解VUE调用本地json的使用方法
2019/05/15 Javascript
完美解决安装完tensorflow后pip无法使用的问题
2018/06/11 Python
python GUI库图形界面开发之PyQt5信号与槽机制、自定义信号基础介绍
2020/02/25 Python
解决Python中报错TypeError: must be str, not bytes问题
2020/04/07 Python
详解CSS3阴影 box-shadow的使用和技巧总结
2016/12/03 HTML / CSS
Html5页面在微信端的分享的实现方法
2018/08/30 HTML / CSS
Paper Cape官网:美国婴儿和儿童服装品牌
2019/11/02 全球购物
2014年机关植树节活动方案
2014/02/27 职场文书
分公司负责人任命书
2014/06/04 职场文书
地理科学专业自荐信
2014/09/01 职场文书
售后客服个人自我评价
2014/09/14 职场文书
文明倡议书
2015/01/19 职场文书
幼儿园心得体会范文
2016/01/21 职场文书
MySQL 重命名表的操作方法及注意事项
2021/05/21 MySQL