V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MySQL 5.5 Community Server
MySQL 5.6 Community Server
Percona Configuration Wizard
XtraBackup 搭建主从复制
Great Sites on MySQL
Percona
MySQL Performance Blog
Severalnines
推荐管理工具
Sequel Pro
phpMyAdmin
推荐书目
MySQL Cookbook
MySQL 相关项目
MariaDB
Drizzle
参考文档
http://mysql-python.sourceforge.net/MySQLdb.html
mostkia
V2EX  ›  MySQL

sql 防止注入,用 base64 对用户输入内容进行预处理,是否可行?

  •  
  •   mostkia · 2019-02-14 15:05:05 +08:00 · 10385 次点击
    这是一个创建于 2143 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,相对于 PDO 来讲,肯定是 PDO 更好,但按照题目里的说法,是否可行呢?(我使用的是 SQLite )

    77 条回复    2019-02-17 01:22:47 +08:00
    Greenm
        1
    Greenm  
       2019-02-14 15:14:24 +08:00   ❤️ 1
    没搞懂 base64 怎么防注入的,我的理解是你传入后端的时候编码一下,你后端收到数据的时候再解码,然后拼接这个 SQL 语句进行查询,这样除了一些乱码外其他好像没啥作用啊,比如我写这样的语句,1' and 1=1,base64 编码解码后不还是一样的吗,怎么防止注入的?

    噢应该可以防止宽字符注入,其他的好像不行。 要不就是我理解错你的意思了。
    CloudMx
        2
    CloudMx  
       2019-02-14 15:15:05 +08:00
    有毛用。
    misaka19000
        3
    misaka19000  
       2019-02-14 15:15:29 +08:00
    额,不管你怎么编码,最终在转为 SQL 的时候难道不都需要解码回原来的字符?
    est
        4
    est  
       2019-02-14 15:16:51 +08:00
    还不如 md5 一下。保证不可能出现引号。
    ThirdFlame
        5
    ThirdFlame  
       2019-02-14 15:17:21 +08:00
    编码 、解码 不还是原来的输入,所以没有任何防注入能力。
    pimin
        6
    pimin  
       2019-02-14 15:18:17 +08:00
    当然不可行
    mostkia
        7
    mostkia  
    OP
       2019-02-14 15:20:12 +08:00
    @Greenm 前台拿到的数据是明文字符串,到后台存入数据库前,进行编码后再存入数据库。需要时再取出来,然后直接解码还原。
    ryweer
        8
    ryweer  
       2019-02-14 15:20:53 +08:00
    --tamper base64encode.py 了解一下
    mostkia
        9
    mostkia  
    OP
       2019-02-14 15:22:02 +08:00
    想不通为什么大家都理解为传输阶段进行编码呢。。我的意思其实是存入数据库前由后台进行编码,存入数据库的就是 base64 内容。
    misaka19000
        10
    misaka19000  
       2019-02-14 15:22:16 +08:00
    @mostkia #7 这是可以的,不过这种思路不是和关键词替换是一样的道理吗?前端中用的很多啊。。。
    funcman
        11
    funcman  
       2019-02-14 15:24:24 +08:00 via iPhone
    没用。
    sql 注入利用的核心点在于字符串缺乏反映类型的手段。防注入要从类型校验一直做到逻辑校验才靠谱。
    base64 只是种编码,没有任何加密。没有加密的传输很容易随意伪造。
    公开的网络交互是很不安全的,所以既要让伪造难以做到,也要能够识破伪造。
    gamexg
        12
    gamexg  
       2019-02-14 15:28:50 +08:00   ❤️ 3
    你如果对所有内容都 base64 后就应该不会有 sql 注入问题。
    但是但是,这么搞搜索等功能全挂了。

    防注入难道不是应该用参数化查询?
    mostkia
        13
    mostkia  
    OP
       2019-02-14 15:30:54 +08:00
    @funcman =_=神奇。。好吧,看来我说的不清楚,大家都有些理解错误。我的意思在明确一下:字符串处理完全在后台存数据时进行,存入到数据库的就是 b64,SQL 增删改查的关键词都是预设的字符串,固定的,前台来的变量则通通都会被编码成 b64 再存储,网络传输阶段都是明文传输的,和传输阶段没有什么关系。
    passerbytiny
        14
    passerbytiny  
       2019-02-14 15:31:56 +08:00   ❤️ 1
    @mostkia #7 你还是先说下你对 sql 注入的理解把,你现在这需求,跟 sql 注入扯不上一点关系,貌似跟 sql 都没关系。
    yagao0o
        15
    yagao0o  
       2019-02-14 15:32:53 +08:00
    有个问题,如果某个字段需要模糊查询怎么办……把也把查询条件 base64 之后查询吗……
    mostkia
        16
    mostkia  
    OP
       2019-02-14 15:33:00 +08:00
    @gamexg 恩,你说的很对,我也考虑过。不过目前我数据库需求,都是完整精确匹配的,可以将输入内容也进行 b64 预处理后再搜索,类似于 MD5 的密码校验吧。复杂的多条件估计是不行的。所以也是处于考虑中是否要这样做。
    mostkia
        17
    mostkia  
    OP
       2019-02-14 15:34:52 +08:00
    @yagao0o 是啊,我也这样考虑了,这个问题比较困难,虽然目前数据库虽然没有模糊匹配的需求,但以后就不确定了,所以也只是探讨一下是否可行。
    opengps
        18
    opengps  
       2019-02-14 15:37:56 +08:00
    其实全文替换就行了,把一个单引号全文替换为两个单引号(在 sql 两个单引号属于注释输入一个单引号)
    funcman
        19
    funcman  
       2019-02-14 15:38:18 +08:00 via iPhone
    @mostkia 我后来才看到你说的方式。怎么说呢,更不靠谱。防注入主要防止数据泄漏。数据泄漏之后,除非由强密码加密过,不然就漏光了啊。
    一般来讲,为了逻辑操作,使用数据库尽量把序列化的数据拆开成字段。少数无需操作的是直接存序列化的数据。这些是教科书里都会提到各种数据库设计范式,学会拿来用即可。
    gam2046
        20
    gam2046  
       2019-02-14 15:41:09 +08:00
    需要的参数化查询替代字符串拼接 SQL 语句。

    只要不使用 SQL 语句的直接拼接,可杜绝绝大部分的注入。
    Greenm
        21
    Greenm  
       2019-02-14 15:42:10 +08:00
    @mostkia #7 如果是这样的话,在将这些字符还原的场景下,会带来一些意想不到安全隐患,考虑如下情况:

    比如,你有一些数据需要展示出来,如用户名,地址等消息,我注册的时候随便填的数据,比如 <script>alert(1)</script>,你 base64 放到数据库了,展示的时候解码展示,就会造成 XSS。

    为了避免上述问题,你还是需要对用户输入的数据进行过滤和限制,这就让你的 Base64 编码失去了最初的意义。 另外,由于你对入库的数据做了编码转换,这会导致一些数据不直观,数据还需要多做处理,浪费性能。

    所以为了达到安全的最佳实践,建议不要自己想出一些方法,这会引起其他意想不到的问题。

    总的的说,如果某方法简单有效,那么肯定有人已经想到且运用上了。
    passerbytiny
        22
    passerbytiny  
       2019-02-14 15:43:46 +08:00
    @mostkia #10 所有值都 b64 转码存储,所有输入的参数值都 b64 转码后再往 sql 上拼,你是这意思吗?
    方案倒是可行,但是弊远大于利,随便一项都有一大堆弊端,譬如已经有人提到的模糊查询,譬如该字段是铁定无法用索引优化了,譬如专业的 dba 看不懂你的数据库了
    898601566
        23
    898601566  
       2019-02-14 15:44:55 +08:00
    我看到有说用 addslashes 的
    Greenm
        24
    Greenm  
       2019-02-14 15:46:28 +08:00   ❤️ 3
    #18 @opengps 这样是完全错误的做法,全局替换非常容易造成一些安全问题,如果我这样写的话 \' 会变成 \'',会将第一个单引号转义,第二个单引号就逃逸出来了,造成 SQL 语句闭合引起注入。

    很多问题都是因为一些程序员所谓的“小聪明”导致的,好的坏的程序员都犯过这种错误。 请严格遵守最佳实践。
    mostkia
        25
    mostkia  
    OP
       2019-02-14 15:46:49 +08:00
    @opengps 好的,谢谢,我认真考虑你说的方案的。
    shuax
        26
    shuax  
       2019-02-14 15:47:52 +08:00
    不是 escape 一下就行吗?
    Vegetable
        27
    Vegetable  
       2019-02-14 15:48:54 +08:00
    我也不明白,怎么都看不明白呢?
    人家说存数据库之前先编码,这样无论什么样的 SQL 语句都一起编码了,当然没了注入的问题

    但是这么解决路子太野了吧?不推荐..
    bunnyblueair
        28
    bunnyblueair  
       2019-02-14 15:54:42 +08:00
    @est 难道对 md5 有什么误解
    mostkia
        29
    mostkia  
    OP
       2019-02-14 16:15:42 +08:00
    @Vegetable 没有复杂查询条件我觉得是可以的,但碰到如果有模糊查询就不行了。我目前需求只是偶尔有一些精确匹配搜索,所以问问可行性,没想到那么多人理解错了,哈哈,没关系。实在不行就老老实实的直接做标准过滤吧,反正也就是处理一些用户名、密码、数量、型号、日期什么的,按照它们的类别做好过滤即可,一般也用不上这些特殊字符,保留英文数字,正则判断禁止所有的特殊字符就可以了。
    opengps
        30
    opengps  
       2019-02-14 16:53:00 +08:00
    @Greenm 真正进入数据库的,\'' 还是 写入原始的 \' ,多增加一个'就是为了让编译器放行写入单引号文本
    est
        31
    est  
       2019-02-14 17:07:38 +08:00
    @bunnyblueair 没误解。db 那边再查一下彩虹表就翻译过来原始值了。
    wellR
        32
    wellR  
       2019-02-14 18:20:48 +08:00
    类似 一表用户表,密码保存的是加密后的值,用户设置的原密码,我们不记录
    daozhihun
        33
    daozhihun  
       2019-02-14 18:54:46 +08:00 via Android
    可行,但是建议用参数
    murmur
        34
    murmur  
       2019-02-14 19:02:52 +08:00
    @gamexg base64 一样可以索引的 就是 like 会挂 而且也未必挂 毕竟是连续编码
    akira
        35
    akira  
       2019-02-14 20:11:17 +08:00
    可以。 顺便在写个中间件自动编解码,嗯。。
    leo108
        36
    leo108  
       2019-02-14 20:45:02 +08:00   ❤️ 1
    @est 你可能对彩虹表有什么误解
    lihongjie0209
        37
    lihongjie0209  
       2019-02-14 20:53:33 +08:00
    @mostkia 存 base64 怎么查询
    hxt
        38
    hxt  
       2019-02-14 20:56:20 +08:00
    防注入不行,不可能所有字段都 base64。不过对长内容可以压缩再 base64 下,比如 php 的 gzcompress 后再 base64 下,存储的长度一般会比原来小。
    caola
        39
    caola  
       2019-02-14 21:01:25 +08:00
    PDO 的预处理,已经是在帮你防注入了,也没太必要还做其他的专门处理
    yidinghe
        40
    yidinghe  
       2019-02-14 21:03:52 +08:00 via Android
    歪门邪道。不同的语言有不同的处理方法。
    iyaozhen
        41
    iyaozhen  
       2019-02-14 21:38:50 +08:00 via Android
    666
    仔细一想,别说还真行。索引也有,like 查询其实也行
    除了存储大小会变大点
    msg7086
        42
    msg7086  
       2019-02-14 22:44:19 +08:00
    可以,但是意义不大。
    madNeal
        43
    madNeal  
       2019-02-14 22:56:00 +08:00
    使用现有的 sql 参数化方法比自己想办法造轮子有效得多
    reself
        44
    reself  
       2019-02-14 23:34:26 +08:00 via Android
    你怕是对 SQL 注入的理解有误。
    reself
        45
    reself  
       2019-02-14 23:36:42 +08:00 via Android
    抱歉,貌似确实能起到类似转义的作用。
    whoami9894
        46
    whoami9894  
       2019-02-14 23:41:11 +08:00 via Android
    如果是数据库所有字段都是 b64 编码内容,那么是可以的
    比如拼接 sql 查询时 select * from users where uname = '{param}',b64 可以保证不会闭合掉前面的单引号,但这么麻烦为什么不直接参数化 sql 语句呢
    claysec
        47
    claysec  
       2019-02-14 23:44:55 +08:00
    楼主是想防止二次 SQL 注入吗。。
    yuikns
        48
    yuikns  
       2019-02-15 00:08:37 +08:00 via iPhone
    其实是可以的。只要没有 like 查询。其实就是利用 b64 字符集没有比较危险的那几个特殊字符。怎么这么多人死活不理解别人的中文呢

    但是,现在一个 prepared statement 能解决的问题没必要那么复杂
    glishijie
        49
    glishijie  
       2019-02-15 00:12:55 +08:00 via Android
    @mostkia 可以的
    just1
        50
    just1  
       2019-02-15 03:30:15 +08:00 via Android
    是可以的,楼上很多人怕是对注入有误解
    all4fun
        51
    all4fun  
       2019-02-15 06:29:30 +08:00
    直接参数化比较好吧
    q397064399
        52
    q397064399  
       2019-02-15 07:20:56 +08:00 via iPhone
    一般 orm 都做掉了,自己弄这个 真的是蛋疼
    jalja27
        53
    jalja27  
       2019-02-15 07:52:41 +08:00 via Android
    以我的经验来看,sql 注入的问题有直接的解决方案,就用这个方案。用编码这种间接的,中间多了一层数据转换逻辑,扩展性和其它关联影响是很大的隐患,特别是多人开发的团队中
    reus
        54
    reus  
       2019-02-15 08:25:22 +08:00
    我看你是不知道 SQL 可以传参数吧。
    paranoiagu
        55
    paranoiagu  
       2019-02-15 08:35:16 +08:00 via Android
    数据库存了 base64 后的数据,你查询 like 怎么搞?
    查出来的数据都要解码显示,性能行不行?
    lzxz1234
        56
    lzxz1234  
       2019-02-15 08:44:47 +08:00
    内容编码入库是常用的,但依赖这种方式防注入也太低级了
    huahuajun9527
        57
    huahuajun9527  
       2019-02-15 09:02:05 +08:00
    你的意思是查询字符串只允许是 base64 的字符集里的,包含其他的字符就不放进去查询???
    master13
        58
    master13  
       2019-02-15 09:06:04 +08:00
    其实在做软件工程的时候,我们经常会遇到一个问题就是:业内通常怎么做。你说的方法也可以,但是不利于后期业务数据分析处理。方法可行,但业内不这么做,肯定是有它的道理。
    firechat
        59
    firechat  
       2019-02-15 09:23:20 +08:00
    给我的感觉就是放着圆的轮子不用,要造一个方的轮子。
    直接用现有的 sql 防注入方案就行了
    xmdbb
        60
    xmdbb  
       2019-02-15 09:23:23 +08:00
    可以肯定可以,但是我只想问问,ID 系列的你怎么处理,难不成整个自增 ID 你都用 BASE64 吧?
    xmdbb
        61
    xmdbb  
       2019-02-15 09:25:28 +08:00
    还有就是不清楚你业务是怎样,如果涉及计算的,涉及日期对比的,你 BS64 后就只能通过取出来后在计算?
    另外如果计算后还要连表查询,那不就变了要跑很多次吗?这样做不就弊大于利了。
    其实 PDO 没啥不好吧?如果说不习惯,那就自己封装下成习惯的就可以了。你这简直是杀鸡取卵啊.....
    qiyuey
        62
    qiyuey  
       2019-02-15 09:53:34 +08:00
    参数化查询,不要去拼接 SQL
    darktutu
        63
    darktutu  
       2019-02-15 10:00:28 +08:00
    我挺心疼楼主的,这下面回复有多少人是题目都没看明白的。这都什么人混迹于此啊?
    AlphaTr
        64
    AlphaTr  
       2019-02-15 10:03:46 +08:00
    可以是可以,但将 base64encode 换为 escape 工作量是一样的,效果却更好;
    rizon
        65
    rizon  
       2019-02-15 10:07:13 +08:00
    参数占位符,sql 预编译就可以解决,注入只发生在 sql 编译时。
    CRVV
        66
    CRVV  
       2019-02-15 10:20:59 +08:00 via Android
    @Greenm
    把单引号替换成两个单引号确实不是好方案,但是楼主说了用的是 SQLite,这个做法是对的
    SQLite 和 PostgreSQL 里,字符串里只有单引号是特殊字符,反斜杠只是反斜杠
    beginor
        67
    beginor  
       2019-02-15 10:37:24 +08:00 via Android
    进行参数化查询就那么难么?
    missdeer
        68
    missdeer  
       2019-02-15 10:40:51 +08:00
    数据库存 base64 后的文本,之后搜索会麻烦一点吧
    a194259440
        69
    a194259440  
       2019-02-15 11:35:23 +08:00
    @darktutu 所以你拿个方案啊,站着说话不腰疼么
    unco020511
        70
    unco020511  
       2019-02-15 12:29:42 +08:00
    可以肯定可以啊,但是后期很多统计搜索之类的就功能就不好处理了吧
    evilmiracle
        71
    evilmiracle  
       2019-02-15 12:55:38 +08:00
    预编译啊
    darktutu
        72
    darktutu  
       2019-02-15 13:11:31 +08:00
    @a194259440 你腰已经断了
    mostkia
        73
    mostkia  
    OP
       2019-02-15 18:18:25 +08:00
    已重写控制程序,目前使用 pdo 预处理解决注入问题,今天一点开就 40 多条回复,没想到那么讨论激烈。其实这个被疯狂吐槽的需求锅不应该由我来背。。。因为之前的数据库控制程序并不是我写的,项目做到一半由我接手的,现在想想的确挺蛋疼的,之前的控制程序直接是标准的 sqlite 接口,当然也有一些基本的字符串过滤,但感觉不太好,但也因为怕出 BUG 实在不想改数据库控制程序这部分,所以才想了一些投机取巧的方法。后来一想长痛不如短痛,索性都重写了,好在业务逻辑也不复杂,已经全部搞定了,感谢大家的意见。
    iwtbauh
        74
    iwtbauh  
       2019-02-16 11:36:17 +08:00 via Android
    @Greenm #1
    @CloudMx #2
    @misaka19000 #3
    @pimin #6

    我以 mysql/mariadb 举例

    mysql/mariadb 提供了一个叫 FROM_BASE64 的函数

    我在拼接 SQL 的时候,后端把用户的输入都 base64 一下,然后套到这个函数里去,就成了这样

    var = base64(${var})
    ..... WHERE `id` = FROM_BASE64(${var}) …

    所有用户的输入都不可能破坏 sql 语句,并且,存到数据库里 /查询数据库的时候,还是原来的用户输入。

    这是我以前用 bash 写个家庭内网自用程序的后端时,因为 bash 没法参数化查询,所做的防注入措施。

    可见,base64 防注入十分可行
    misaka19000
        75
    misaka19000  
       2019-02-16 11:53:17 +08:00
    @iwtbauh #72 没说不可行啊,只是这个方法太民科了,缺点也很明显
    iwtbauh
        76
    iwtbauh  
       2019-02-16 12:38:42 +08:00 via Android
    @misaka19000 #75

    除了有点 dirty 之外。有什么明显缺点吗。
    mostkia
        77
    mostkia  
    OP
       2019-02-17 01:22:47 +08:00 via Android
    @iwtbauh 74#受教了,以后有需要的情况下也有可行对策了,虽然有轮子用自然最好了的,pdo 也挺方便的。但多一个办法多一条路总归也是好的。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2765 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 02:11 · PVG 10:11 · LAX 18:11 · JFK 21:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.