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数据定义——模式与基本表操作
Apr 05 SQL Server
SQL Server2019数据库之简单子查询的具有方法
Apr 27 SQL Server
使用SQL实现车流量的计算的示例代码
Feb 28 SQL Server
通过T-SQL语句创建游标与实现数据库加解密功能
Mar 16 SQL Server
SQL Server数据库基本概念、组成、常用对象与约束
Mar 20 SQL Server
sql server偶发出现死锁的解决方法
Apr 10 SQL Server
SQL Server数据库查询出现阻塞之性能调优
Apr 10 SQL Server
MSSQL基本语法操作
Apr 11 SQL Server
SQL Server中的游标介绍
May 20 SQL Server
SQL Server携程核心系统无感迁移到MySQL实战
Jun 01 SQL Server
SqlServer常用函数及时间处理小结
May 08 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 和 XML: 使用expat函数(三)
2006/10/09 PHP
php生成随机字符串可指定纯数字、纯字母或者混合的
2014/04/18 PHP
PHP中spl_autoload_register()和__autoload()区别分析
2014/05/10 PHP
删除PHP数组中头部、尾部、任意元素的实现代码
2017/04/10 PHP
百度判断手机终端并自动跳转js代码及使用实例
2014/06/11 Javascript
js实现兼容IE和FF的上下层的移动
2015/05/04 Javascript
理解javascript闭包
2015/12/15 Javascript
JavaScript每天必学之事件
2016/09/18 Javascript
RequireJS简易绘图程序开发
2016/10/28 Javascript
canvas学习之API整理笔记(二)
2016/12/29 Javascript
VUE利用vuex模拟实现新闻点赞功能实例
2017/06/28 Javascript
angular项目中bootstrap-datetimepicker时间插件的使用示例
2018/03/15 Javascript
vue自定义底部导航栏Tabbar的实现代码
2018/09/03 Javascript
详解mpvue中小程序自定义导航组件开发指南
2019/02/11 Javascript
layui 数据表格复选框实现单选功能的例子
2019/09/19 Javascript
vue中js判断长时间不操作界面自动退出登录(推荐)
2020/01/22 Javascript
Vue基于iview实现登录密码的显示与隐藏功能
2020/03/06 Javascript
[57:09]DOTA2-DPC中国联赛 正赛 Phoenix vs Dynasty BO3 第一场 1月26日
2021/03/11 DOTA
解决Python传递中文参数的问题
2015/08/04 Python
python requests 使用快速入门
2017/08/31 Python
python 3.6 tkinter+urllib+json实现火车车次信息查询功能
2017/12/20 Python
Python把csv数据写入list和字典类型的变量脚本方法
2018/06/15 Python
详解Django-channels 实现WebSocket实例
2019/08/22 Python
使用Tensorflow实现可视化中间层和卷积层
2020/01/24 Python
浅谈python累加求和+奇偶数求和_break_continue
2020/02/25 Python
parser.add_argument中的action使用
2020/04/20 Python
阿里旅行:飞猪
2017/01/05 全球购物
Python中pass语句的作用是什么
2016/06/01 面试题
大专生自荐信
2013/10/04 职场文书
教育学专业实习生的自我鉴定
2013/11/26 职场文书
幼儿园保教管理制度
2014/02/03 职场文书
应聘英语教师求职信
2014/04/24 职场文书
法制宣传口号
2014/06/16 职场文书
2015年企业员工工作总结范文
2015/05/21 职场文书
预备党员转正党小组意见
2015/06/01 职场文书
Oracle 死锁的检测查询及处理
2021/09/25 Oracle