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 相关文章推荐
SQLServer2019 数据库的基本使用之图形化界面操作的实现
Apr 08 SQL Server
sql查询结果列拼接成逗号分隔的字符串方法
May 25 SQL Server
sql中mod()函数取余数的用法
May 29 SQL Server
SQL Server查询某个字段在哪些表中存在
Mar 03 SQL Server
SQL Server实现分页方法介绍
Mar 16 SQL Server
SQL CASE 表达式的具体使用
Mar 21 SQL Server
SQLServer RANK() 排名函数的使用
Mar 23 SQL Server
SQL Server内存机制浅探
Apr 06 SQL Server
SQL Server中使用表变量和临时表
May 20 SQL Server
SQL Server使用T-SQL语句批处理
May 20 SQL Server
SQL Server中搜索特定的对象
May 25 SQL Server
SQL中的连接查询详解
Jun 21 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 session应用实例 登录验证
2009/03/16 PHP
PHP FOR MYSQL 代码生成助手(根据Mysql里的字段自动生成类文件的)
2011/07/23 PHP
关于laravel后台模板laravel-admin select框的使用详解
2019/10/03 PHP
ThinkPHP 5.1 跨域配置方法
2019/10/11 PHP
jquery foreach使用示例
2013/09/12 Javascript
原生JS和JQuery动态添加、删除表格行的方法
2015/05/28 Javascript
ionic组件ion-tabs选项卡切换效果实例
2016/08/27 Javascript
javascript的document中的动态添加标签实现方法
2016/10/24 Javascript
利用JS轻松实现获取表单数据
2016/12/06 Javascript
js eval函数使用,js对象和字符串互转实例
2017/03/06 Javascript
Vue组件tree实现树形菜单
2017/04/13 Javascript
Vue+element 解决浏览器自动填充记住的账号密码问题
2019/06/11 Javascript
python字典多条件排序方法实例
2014/06/30 Python
粗略分析Python中的内存泄漏
2015/04/23 Python
python通过openpyxl生成Excel文件的方法
2015/05/12 Python
python 实现网上商城,转账,存取款等功能的信用卡系统
2016/07/15 Python
Pycharm 字体大小调整设置的方法实现
2019/09/27 Python
django框架F&amp;Q 聚合与分组操作示例
2019/12/12 Python
Pycharm最新激活码2019(推荐)
2019/12/31 Python
python实现逻辑回归的示例
2020/10/09 Python
Python3读写ini配置文件的示例
2020/11/06 Python
用python写一个带有gui界面的密码生成器
2020/11/06 Python
查找适用于matplotlib的中文字体名称与实际文件名对应关系的方法
2021/01/05 Python
美国家居装饰和豪华家具购物网站:One Kings Lane
2018/12/24 全球购物
一个精品风格的世界:Atterley
2019/05/01 全球购物
应届毕业生自我评价分享
2013/12/15 职场文书
网上卖盒饭创业计划书范文
2014/02/07 职场文书
园林系毕业生求职信
2014/06/23 职场文书
年度安全生产目标责任书
2014/07/23 职场文书
党委班子对照检查材料
2014/08/19 职场文书
入党积极分子学习党的纲领思想汇报
2014/09/13 职场文书
悬空寺导游词
2015/02/05 职场文书
母亲去世追悼词
2015/06/23 职场文书
你为什么是穷人?可能是这5个缺点造成
2019/07/11 职场文书
尝试使用Python爬取城市租房信息
2022/04/12 Python
MySQL下载安装配置详细教程 附下载资源
2022/09/23 MySQL