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 窗口函数实现高效分页查询的案例分析
May 21 SQL Server
SQLServer之常用函数总结详解
Aug 30 SQL Server
SQL中的三种去重方法小结
Nov 01 SQL Server
SQL SERVER存储过程用法详解
Feb 24 SQL Server
SQL Server中常用截取字符串函数介绍
Mar 16 SQL Server
SQL CASE 表达式的具体使用
Mar 21 SQL Server
如何使用SQL Server语句创建表
Apr 12 SQL Server
SQL Server #{}可以防止SQL注入
May 11 SQL Server
SQL使用复合索引实现数据库查询的优化
May 25 SQL Server
SQL bool盲注和时间盲注详解
Jul 23 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 Mysqli 常用代码集合
2016/11/12 PHP
php生成微信红包数组的方法
2019/09/05 PHP
thinkphp框架表单数组实现图片批量上传功能示例
2020/04/04 PHP
JavaScript判断undefined类型的正确方法
2015/06/30 Javascript
CascadeView级联组件实现思路详解(分离思想和单链表)
2016/04/12 Javascript
window.open不被拦截的简单实现代码(推荐)
2016/08/04 Javascript
Jquery Easyui进度条组件Progress使用详解(8)
2020/03/26 Javascript
vue-cli+axios实现文件上传下载功能(下载接收后台返回文件流)
2019/05/10 Javascript
解决小程序无法触发SESSION问题
2020/02/03 Javascript
Electron实现应用打包、自动升级过程解析
2020/07/07 Javascript
vue+elementUI(el-upload)图片压缩,默认同比例压缩操作
2020/08/10 Javascript
Python脚本实现格式化css文件
2015/04/08 Python
用Python程序抓取网页的HTML信息的一个小实例
2015/05/02 Python
Python装饰器基础详解
2016/03/09 Python
python爬虫使用cookie登录详解
2017/12/27 Python
教你使用python实现微信每天给女朋友说晚安
2018/03/23 Python
python抓取搜狗微信公众号文章
2019/04/01 Python
django 中QuerySet特性功能详解
2019/07/25 Python
Pytorch实现基于CharRNN的文本分类与生成示例
2020/01/08 Python
python 实现将Numpy数组保存为图像
2020/01/09 Python
Python拼接字符串的7种方式详解
2020/03/19 Python
python3中datetime库,time库以及pandas中的时间函数区别与详解
2020/04/16 Python
Zavvi荷兰:英国大型音像制品和图书游戏零售商
2018/03/22 全球购物
JAVA代码查错题
2014/10/10 面试题
金融专业个人求职信
2013/09/22 职场文书
应聘自荐书
2013/10/08 职场文书
质检部岗位职责
2013/11/11 职场文书
第一批党的群众路线教育实践活动工作总结
2014/03/03 职场文书
计算机专业毕业生自荐信范文
2014/03/06 职场文书
和解协议书
2014/04/16 职场文书
2014年爱国卫生工作总结
2014/11/22 职场文书
劳动保障个人工作总结
2015/03/04 职场文书
2016年主题党日活动总结
2016/04/05 职场文书
centos8安装MongoDB的详细过程
2021/10/24 MongoDB
基于Redis zSet实现滑动窗口对短信进行防刷限流的问题
2022/02/12 Redis
PYTHON InceptionV3模型的复现详解
2022/05/06 Python