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基本使用和简单的CRUD操作
Apr 05 SQL Server
【HBU】数据库第四周 单表查询
Apr 05 SQL Server
SQLServer 日期函数大全(小结)
Apr 08 SQL Server
mybatis调用sqlserver存储过程返回结果集的方法
May 08 SQL Server
在 SQL 语句中处理 NULL 值的方法
Jun 07 SQL Server
sql字段解析器的实现示例
Jun 23 SQL Server
SQL Server表分区删除详情
Oct 16 SQL Server
SQL Server数据库基本概念、组成、常用对象与约束
Mar 20 SQL Server
SQLServer权限之只开启创建表权限
Apr 12 SQL Server
SQL Server中的逻辑函数介绍
May 25 SQL Server
SQL解决未能删除约束问题drop constraint
May 30 SQL Server
SQL Server携程核心系统无感迁移到MySQL实战
Jun 01 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
漫威DC御用漫画家去世 他的表情包曾走红网络
2020/04/09 欧美动漫
PHP实现取得HTTP请求的原文
2014/08/18 PHP
php中限制ip段访问、禁止ip提交表单的代码分享
2014/08/22 PHP
PHP Header失效的原因分析及解决方法
2016/11/16 PHP
php实现数组中出现次数超过一半的数字的统计方法
2018/10/14 PHP
从ThinkPHP3.2.3过渡到ThinkPHP5.0学习笔记图文详解
2019/04/03 PHP
js实现可键盘控制的简单抽奖程序
2016/07/13 Javascript
jquery 点击元素后,滚动条滚动至该元素位置的方法
2016/08/05 Javascript
原生js实现网易轮播图效果
2020/04/10 Javascript
Bootstrap表单简单实现代码
2017/03/06 Javascript
javascript实现下雨效果
2017/03/27 Javascript
JavaScript中splice与slice的区别
2017/05/09 Javascript
vuejs手把手教你写一个完整的购物车实例代码
2017/07/06 Javascript
使用Node.js搭建静态资源服务详细教程
2017/08/02 Javascript
JS实现不用中间变量temp 实现两个变量值得交换方法
2018/02/04 Javascript
详解js类型判断
2018/05/22 Javascript
JavaScript中var、let、const区别浅析
2018/06/24 Javascript
解决vue的过渡动画无法正常实现问题
2019/10/31 Javascript
Vue element-ui父组件控制子组件的表单校验操作
2020/07/17 Javascript
JavaScript实现多层颜色选项卡嵌套
2020/09/21 Javascript
JS实现公告上线滚动效果
2021/01/10 Javascript
python处理json数据中的中文
2014/03/06 Python
python安装cx_Oracle模块常见问题与解决方法
2017/02/21 Python
python实现顺序表的简单代码
2018/09/28 Python
Python爬取智联招聘数据分析师岗位相关信息的方法
2019/08/13 Python
python线程中的同步问题及解决方法
2019/08/29 Python
Python小整数对象池和字符串intern实例解析
2020/03/21 Python
python爬虫基础知识点整理
2020/06/02 Python
Django日志及中间件模块应用案例
2020/09/10 Python
英国领先的男士服装和时尚零售商:Burton
2017/01/09 全球购物
节约粮食标语
2014/06/18 职场文书
2014年党委工作总结
2014/11/22 职场文书
贷款收入证明范本
2015/06/12 职场文书
2015年秋季小学开学标语
2015/07/16 职场文书
公司费用报销管理制度
2015/08/04 职场文书
uniapp引入支付宝原生扫码插件步骤详解
2022/07/23 Javascript