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——索引+基于单表的数据插入与简单查询【1】
Apr 05 SQL Server
sql字段解析器的实现示例
Jun 23 SQL Server
SqlServer数据库远程连接案例教程
Jul 15 SQL Server
sql通过日期判断年龄函数的示例代码
Jul 16 SQL Server
SQL中的三种去重方法小结
Nov 01 SQL Server
SQL SERVER触发器详解
Feb 24 SQL Server
使用SQL实现车流量的计算的示例代码
Feb 28 SQL Server
sql时间段切分实现每隔x分钟出一份高速门架车流量
Feb 28 SQL Server
详解在SQLPlus中实现上下键翻查历史命令的功能
Mar 18 SQL Server
如何使用SQL Server语句创建表
Apr 12 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 if 想到的些问题
2008/03/22 PHP
php查看当前Session的ID实例
2015/03/16 PHP
PHP之多条件混合筛选功能的实现方法
2019/10/09 PHP
几个高效,简洁的字符处理函数
2007/04/12 Javascript
元素的内联事件处理函数的特殊作用域在各浏览器中存在差异
2011/01/12 Javascript
js获取html参数及向swf传递参数应用介绍
2013/02/18 Javascript
js为鼠标添加右击事件防止默认的右击菜单弹出
2013/07/29 Javascript
简易的投票系统以及js刷票思路和方法
2015/04/07 Javascript
jquery 中ajax执行的优先级
2015/06/22 Javascript
JavaScript如何实现在文本框(密码框)输入提示语
2015/12/25 Javascript
JavaScript阻止表单提交方法(附代码)
2017/08/15 Javascript
手把手教你vue-cli单页到多页应用的方法
2018/05/31 Javascript
ES6中let 和 const 的新特性
2018/09/03 Javascript
值得收藏的八个常用的js正则表达式
2018/10/19 Javascript
laydate只显示时分 不显示秒的功能实现方法
2019/09/28 Javascript
python sort、sorted高级排序技巧
2014/11/21 Python
python使用自定义user-agent抓取网页的方法
2015/04/15 Python
对python中的控制条件、循环和跳出详解
2019/06/24 Python
Python 迭代,for...in遍历,迭代原理与应用示例
2019/10/12 Python
Python 复平面绘图实例
2019/11/21 Python
python实现磁盘日志清理的示例
2020/11/05 Python
python 爬虫爬取京东ps4售卖情况
2020/12/18 Python
俄罗斯设计师家具购物网站:The Furnish
2019/12/01 全球购物
密封类可以有虚函数吗
2014/08/11 面试题
css animation配合SVG制作能量流动效果
2021/03/24 HTML / CSS
自荐书封面下载
2013/11/29 职场文书
六个一活动实施方案
2014/03/21 职场文书
2014年党务公开方案
2014/05/08 职场文书
信用卡工资证明格式
2014/09/13 职场文书
出售房屋委托书范本
2014/09/24 职场文书
还款承诺书范本
2015/01/20 职场文书
幼儿园小班教师个人工作总结
2015/02/06 职场文书
创业计划书之烤红薯
2019/09/26 职场文书
《天使的翅膀》读后感3篇
2019/12/20 职场文书
Java基础——Map集合
2022/04/01 Java/Android
Nginx代理Redis哨兵主从配置的实现
2022/07/15 Servers