如图
我使用的是 sql 拼接的方式实现的不同规则的排序,这个 query.order
是请求接口传过来的值,是一个枚举类型
按道理来说是只能传数字类型,传其他类型接口会报 400 错误
这种情况会有 sql 注入 的风险吗?
1
rocksolid 2023-01-12 15:15:07 +08:00
数字类型不会有
|
2
xuanbg 2023-01-12 15:44:19 +08:00
其实只要检测一下几个特殊符号,拼接 SQL 也不会导致注入。
|
7
h0099 2023-01-12 17:46:31 +08:00
#5 @xuanbg 允许用户输入'和`意味着他可以逃出您亲自写的'和`包裹
例如执行 sql "SELECT `{用户输入}` FROM table" 这里的用户输入可以是"someFieldExistsInTable`, (SELECT * FROM secretTable), `anotherFieldExistsInTable" 导致实际执行的是"SELECT `someFieldExistsInTable`, (SELECT * FROM secretTable), `anotherFieldExistsInTable` FROM table",于是 secretTable 就也被读出来了 '同理 |
9
wtfedc 2023-01-12 18:54:28 +08:00
8 楼没理解 SQL 注入是怎么回事,不管你拼接在哪,都有可能用 union ,或者 ; select 来获取其他表的数据
|
10
wtfedc 2023-01-12 19:00:51 +08:00
印象中有些 unicode 字符,在某些情况下,都可以用来突破,比如 0x39 。预防 SQL 注入,不是光过滤特殊字符就够的。
|
11
IvanLi127 2023-01-12 19:17:52 +08:00 via Android
只是数字的话,应该没事,op 用的是 rust 嘛?
别的比如弱类型语言,就得多一步,转换成数字类型再转成字符串,避免奇怪事情发生。 |
13
ragnaroks 2023-01-12 19:22:50 +08:00
大多数情况下,不使用字符串拼接风险并不大,逻辑拼接是安全的。
字符串拼接:sql = SELECT id,username FROM table WHERE $arg1 AND $arg2 逻辑拼接:quary = repo.from<TableClass>().select(table=>table.id,table.username).where(table=>table.username==arg1&&table.type===arg2).toList() |
14
h0099 2023-01-12 19:49:45 +08:00
#8 @xuanbg 对于 ORDER BY `{用户输入}`
提供 someExistingField`; SELECT *, otherFieldExistsInSecretTable FROM `secretTable 执行 ORDER BY `someExistingField`; SELECT *, otherFieldExistsInSecretTable FROM `secretTable` 您的 secretTable 又被读出来了 |
15
h0099 2023-01-12 19:52:31 +08:00
@ragnaroks 这是使用大多数 orm 附带的 query builder 来构造您要查询的各个 clause 组成,最后由 query builder 来把他们变成 rawsql ,只要您不给 query builder 传入 raw 部分( query builder 通常会把所有是字符串的都作为 prepared statement 的插值参数),如`selectRaw(用户输入)`那就是安全的
|
16
h0099 2023-01-12 19:56:22 +08:00
#10 @wtfedc ASCII 0x39 不是`9`吗,纯数字有啥风险?
#12 @IvanLi127 prepared statement 也不是银弹: https://www.v2ex.com/t/908249#reply15 > phppdo/jdbc/dbdriver 层提供的 prepated statment 不是银弹,无脑 escape 掉用户输入中的所有 sql 语法所使用的特殊字符也不是银弹:phpdelusions.net/pdo/sql_injection_example#escaping `请不要在每一个回复中都包括外链,这看起来像是在 spamming` |
17
sorcerer 2023-01-12 21:09:49 +08:00 via iPhone
@h0099 看了一下 你第二个例子不也算是 sql 拼接么,单纯的 prepared statement with parameters 有 sql 注入的风险么
|
18
h0099 2023-01-12 21:13:14 +08:00
@sorcerer 单纯的没有,但只要遇到有意无意的把用户输入直接拼接进字符串那就需要对用户输入做严格的过滤(如正则)甚至白名单(如只允许已知字段名)
|
20
IvanLi127 2023-01-12 21:20:50 +08:00 via Android
@h0099 #16 你要知道,谈字符串就得结合字符串编码,这里面水深的很,你以为你看到的 9 ,天知道转了几手后到数据库执行是他还是不是 9
|
21
h0099 2023-01-12 21:28:08 +08:00
#19 @IvanLi127 phpdelusions.net/pdo/sql_injection_example 所举的例子就是典型的同时混用 字符串拼接 和 prepared statement 的传参插值以实现将用户输入用于无法作为 prepared statement 的传参插值的地方(原文例子中是用于 UPDATE ... SET 子句,现实中更常见的就是 SELECT ...子句的字段名,或是楼上 ORDER BY ...子句的字段名),所以字符串拼接是这的短板
#20 @IvanLi127 byte0x39=字符串 9 是 ASCII 里的,不论什么 EASCII 编码都不可能修改掉这个 0x39 对应 9 的关系(顶多像 SHIFT-JIS 那样把\改成¥),那么您所说的转了几手难道是传输给使用远古的 BCDIC 编码的环境里了吗?还是阁下直接在前端验证用户输入白名单,所以无法保证后端收到的就是 0x39 byte ? |
22
815979670 2023-01-12 22:49:56 +08:00
如果想要完全避免注入问题,需要使用 sql 预编译,然后通过参数绑定的方式执行,把 SQL 和数据分开绝对不会有问题。
|
23
ToBeHacker 2023-01-12 23:00:10 +08:00
用 prepare sql 啊,这不专门用来解决这个问题的么,性能还好
|
24
h0099 2023-01-12 23:12:16 +08:00
#15 @h0099 所说的 `给 query builder 传入 raw 部分如 selectRaw(用户输入)`实际上就是
#21 的`同时混用 字符串拼接 和 prepared statement 的传参插值` 您可能会觉得我从来都没有做过 在 prepared statement 里又直接拼接了用户输入 的罪恶行径 但实际上 orm 的 query builder 通过抽象隔离使得您在使用其提供的 selectRaw()等 api 时也忘记了这实际上就是危险的拼接 |
25
horou OP @ToBeHacker 我只是在个别规则比较多的参数数量不固定情况使用了这种方式,其他都是用的 pg $1 $2 这种方式传的参,如果我上面的情况用这种的话,需要重复写几个 sql ,感觉比较麻烦,所以就用了这种方式
|
26
horou OP |
27
h0099 2023-01-12 23:48:24 +08:00
您需要控制{}的插值也就是那个 id 变量的取值区间,如果他来自程序内部那还比较容易保证其合法(比如您 1 楼所说的`query.order 是请求接口传过来的值,是一个枚举类型`),如果是用户输入您必须得假定恶意对它做合适的过滤甚至白名单
|
28
IvanLi127 2023-01-13 01:39:34 +08:00 via Android
@h0099 字符编码这个我也不能多说什么,我也没深入研究字符编码的问题,大概是编码长度转换出问题之类的,反正我是不信上古代码里关于字符串的处理。
另外预处理语句解决不了的业务你咋能怪预处理。。。我只是回复别人说替换文本不安全而已,和 op 的问题没关系。 |
32
NewYear 2023-01-13 11:33:57 +08:00
某机器人坚持要用参数化绑定数据,经过多轮交流,终于愿意以拼接字符串的方式写,其实就是黑名单与白名单+类型判断。不知道黑名单的字符串够不够完整。
```C# // 定义变量 int num = 1; string str = "abc"; DateTime dateTime = DateTime.Now; // 将变量转换为 SQL 语句中可以使用的字符串 string numStr = num.ToString(); string strStr = str.Replace("'", "''"); string dateTimeStr = dateTime.ToString("yyyy-MM-dd HH:mm:ss"); // 拼接 SQL 语句 string sql = "SELECT * FROM table WHERE num=" + numStr + " AND str='" + strStr + "' AND datetime='" + dateTimeStr + "'"; // 执行 SQL 语句 // ... ``` |
33
NewYear 2023-01-13 11:34:30 +08:00
准确的说是转义符+类型判断
|