V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
horou
V2EX  ›  程序员

关于 sql 拼接和 sql 注入的问题

  •  
  •   horou · 2023-01-12 15:07:48 +08:00 · 3520 次点击
    这是一个创建于 681 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如图

    Imgur

    我使用的是 sql 拼接的方式实现的不同规则的排序,这个 query.order 是请求接口传过来的值,是一个枚举类型

    按道理来说是只能传数字类型,传其他类型接口会报 400 错误

    这种情况会有 sql 注入 的风险吗?

    33 条回复    2023-01-13 11:34:30 +08:00
    rocksolid
        1
    rocksolid  
       2023-01-12 15:15:07 +08:00
    数字类型不会有
    xuanbg
        2
    xuanbg  
       2023-01-12 15:44:19 +08:00
    其实只要检测一下几个特殊符号,拼接 SQL 也不会导致注入。
    horou
        3
    horou  
    OP
       2023-01-12 17:00:17 +08:00
    @rocksolid 好的,谢谢解答
    horou
        4
    horou  
    OP
       2023-01-12 17:12:22 +08:00
    @xuanbg 我拼接都是用的数值类型,这种应该没什么问题吧
    xuanbg
        5
    xuanbg  
       2023-01-12 17:17:14 +08:00
    @horou 纯数字和字母都是安全的,符号我只允许使用_,'`四个。
    horou
        6
    horou  
    OP
       2023-01-12 17:29:51 +08:00
    @xuanbg 明白了,谢谢
    h0099
        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 就也被读出来了

    '同理
    xuanbg
        8
    xuanbg  
       2023-01-12 18:43:00 +08:00
    @h0099 你想多了,前端输入的参数是仅限于被拼接到 order by 子句的。
    wtfedc
        9
    wtfedc  
       2023-01-12 18:54:28 +08:00
    8 楼没理解 SQL 注入是怎么回事,不管你拼接在哪,都有可能用 union ,或者 ; select 来获取其他表的数据
    wtfedc
        10
    wtfedc  
       2023-01-12 19:00:51 +08:00
    印象中有些 unicode 字符,在某些情况下,都可以用来突破,比如 0x39 。预防 SQL 注入,不是光过滤特殊字符就够的。
    IvanLi127
        11
    IvanLi127  
       2023-01-12 19:17:52 +08:00 via Android
    只是数字的话,应该没事,op 用的是 rust 嘛?
    别的比如弱类型语言,就得多一步,转换成数字类型再转成字符串,避免奇怪事情发生。
    IvanLi127
        12
    IvanLi127  
       2023-01-12 19:19:48 +08:00 via Android
    @xuanbg 黑名单覆盖不全,不安全的,得上白名单。但就上白名单,还不如用预处理语句省事。
    ragnaroks
        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()
    h0099
        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 又被读出来了
    h0099
        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(用户输入)`那就是安全的
    h0099
        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`
    sorcerer
        17
    sorcerer  
       2023-01-12 21:09:49 +08:00 via iPhone
    @h0099 看了一下 你第二个例子不也算是 sql 拼接么,单纯的 prepared statement with parameters 有 sql 注入的风险么
    h0099
        18
    h0099  
       2023-01-12 21:13:14 +08:00
    @sorcerer 单纯的没有,但只要遇到有意无意的把用户输入直接拼接进字符串那就需要对用户输入做严格的过滤(如正则)甚至白名单(如只允许已知字段名)
    IvanLi127
        19
    IvanLi127  
       2023-01-12 21:18:26 +08:00 via Android
    @h0099 你自己转的地址里不就是说用预处理语句的方法是坚固的么?你再看看?
    IvanLi127
        20
    IvanLi127  
       2023-01-12 21:20:50 +08:00 via Android
    @h0099 #16 你要知道,谈字符串就得结合字符串编码,这里面水深的很,你以为你看到的 9 ,天知道转了几手后到数据库执行是他还是不是 9
    h0099
        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 ?
    815979670
        22
    815979670  
       2023-01-12 22:49:56 +08:00
    如果想要完全避免注入问题,需要使用 sql 预编译,然后通过参数绑定的方式执行,把 SQL 和数据分开绝对不会有问题。
    ToBeHacker
        23
    ToBeHacker  
       2023-01-12 23:00:10 +08:00
    用 prepare sql 啊,这不专门用来解决这个问题的么,性能还好
    h0099
        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 时也忘记了这实际上就是危险的拼接
    horou
        25
    horou  
    OP
       2023-01-12 23:14:40 +08:00
    @ToBeHacker 我只是在个别规则比较多的参数数量不固定情况使用了这种方式,其他都是用的 pg $1 $2 这种方式传的参,如果我上面的情况用这种的话,需要重复写几个 sql ,感觉比较麻烦,所以就用了这种方式
    horou
        26
    horou  
    OP
       2023-01-12 23:27:06 +08:00
    @h0099 大佬,我上面图片的例子感觉不是很恰当,我下面这张图上,我就是用 prepared statement 和拼接混用的情况,这种会有风险吗?

    ![Imgur]( )
    h0099
        27
    h0099  
       2023-01-12 23:48:24 +08:00
    您需要控制{}的插值也就是那个 id 变量的取值区间,如果他来自程序内部那还比较容易保证其合法(比如您 1 楼所说的`query.order 是请求接口传过来的值,是一个枚举类型`),如果是用户输入您必须得假定恶意对它做合适的过滤甚至白名单
    IvanLi127
        28
    IvanLi127  
       2023-01-13 01:39:34 +08:00 via Android
    @h0099 字符编码这个我也不能多说什么,我也没深入研究字符编码的问题,大概是编码长度转换出问题之类的,反正我是不信上古代码里关于字符串的处理。
    另外预处理语句解决不了的业务你咋能怪预处理。。。我只是回复别人说替换文本不安全而已,和 op 的问题没关系。
    xuanbg
        29
    xuanbg  
       2023-01-13 06:49:49 +08:00
    @h0099 你传入的字符串包含了;,并不在允许的规则之内。所以还是安全的。
    horou
        30
    horou  
    OP
       2023-01-13 09:41:35 +08:00
    @h0099 我这个 id 是一个 i32 的数值类型,这种也需要过滤一些值吗
    southwolf
        31
    southwolf  
       2023-01-13 10:42:13 +08:00
    @h0099 ChatGPT 机器人你好...
    NewYear
        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 语句
    // ...
    ```
    NewYear
        33
    NewYear  
       2023-01-13 11:34:30 +08:00
    准确的说是转义符+类型判断
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1092 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 18:52 · PVG 02:52 · LAX 10:52 · JFK 13:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.