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
SQLServer 日期函数大全(小结)
Apr 08 SQL Server
SQL Server2019数据库之简单子查询的具有方法
Apr 27 SQL Server
SQLServer 错误: 15404,无法获取有关 Windows NT 组/用户 WIN-8IVSNAQS8T7\Administrator 的信息
Jun 30 SQL Server
SQL SERVER存储过程用法详解
Feb 24 SQL Server
Sql Server之数据类型详解
Feb 28 SQL Server
SQL Server一个字符串拆分多行显示或者多行数据合并成一个字符串
May 25 SQL Server
SQL Server删除表中的重复数据
May 25 SQL Server
SQL中的连接查询详解
Jun 21 SQL Server
在SQL Server中使用 Try Catch 处理异常的示例详解
Jul 15 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 Document 代码注释规范
2009/04/13 PHP
php 强制下载文件实现代码
2013/10/28 PHP
php操作MongoDB基础教程(连接、新增、修改、删除、查询)
2014/03/25 PHP
php限制ip地址范围的方法
2015/03/31 PHP
在Mac上编译安装PHP7的开发环境
2015/07/28 PHP
thinkphp项目部署到Linux服务器上报错“模板不存在”如何解决
2016/04/27 PHP
PHP从二维数组得到N层分类树的实现代码
2016/10/11 PHP
javascript读取RSS数据
2007/01/20 Javascript
Jquery post传递数组方法实现思路及代码
2013/04/28 Javascript
同域jQuery(跨)iframe操作DOM(示例代码)
2013/12/13 Javascript
AJAX和jQuery动态加载数据的实现方法
2016/12/05 Javascript
bootstrap modal+gridview实现弹出框效果
2017/08/15 Javascript
微信小程序人脸识别功能代码实例
2019/05/07 Javascript
JS中getElementsByClassName与classList兼容性问题解决方案分析
2019/08/07 Javascript
基于JS实现父组件的请求服务过程解析
2019/10/14 Javascript
Python下的Softmax回归函数的实现方法(推荐)
2017/01/26 Python
Python二叉树的镜像转换实现方法示例
2019/03/06 Python
Python开发之Nginx+uWSGI+virtualenv多项目部署教程
2019/05/13 Python
解决pyecharts运行后产生的html文件用浏览器打开空白
2020/03/11 Python
实现ECharts双Y轴左右刻度线一致的例子
2020/05/16 Python
如何使用Cython对python代码进行加密
2020/07/08 Python
localStorage、sessionStorage使用总结
2017/11/17 HTML / CSS
Oracle快照(snapshot)
2015/03/13 面试题
公司新员工的演讲稿注意事项
2014/01/01 职场文书
大学四年职业生涯规划书范文
2014/01/02 职场文书
给交警的表扬信
2014/01/12 职场文书
企业军训感言
2014/02/08 职场文书
《手指教学》反思
2014/02/14 职场文书
社区居务公开实施方案
2014/03/27 职场文书
贷款委托书
2014/08/01 职场文书
司法局群众路线教育实践活动开展情况总结
2014/10/25 职场文书
毕业设计指导教师评语
2014/12/30 职场文书
2015年园林绿化工作总结
2015/05/23 职场文书
羊脂球读书笔记
2015/06/30 职场文书
老干部座谈会主持词
2015/07/03 职场文书
利用Python读取微信朋友圈的多种方法总结
2021/08/23 Python