V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Tornado Documentation
http://www.v2ex.com/tornado/
Tornado on GitHub
https://github.com/facebook/tornado/
Tornado Gists
http://tornadogists.org/
nichan
V2EX  ›  Tornado

Tornado的防注入应该怎么做?

  •  
  •   nichan · 2013-04-14 19:41:22 +08:00 · 7776 次点击
    这是一个创建于 4270 天前的主题,其中的信息可能已经有所发展或是发生改变。
    在网上查到的教程中说应该使用:
    cursor.execute("SELECT * FROM user_info WHERE email = %s", youremail)

    而不是:
    cursor.execute("SELECT * FROM user_info WHERE email = %s" % youremail)

    但是第一种形式似乎不能够处理自定义表名,如:
    cursor.execute("SELECT * FROM %s WHERE email = %s", (user_info, youremail)
    会自动把表名加上双引号变成
    SELECT * FROM "user_info" WHERE email = "youremail"

    我想,能不能在get_argument处下手,通过过滤某些字符以达到防注入的效果?或者有没有其他更好的方法?
    9 条回复    1970-01-01 08:00:00 +08:00
    lilian57
        1
    lilian57  
       2013-04-15 11:46:10 +08:00   ❤️ 1
    简单说一下
    1.首先要理解为什么能防止sql注入:参数化查询,sql能重用查询计划(有助于提高性能)。而表名是不能参数化查询的
    2.如果你用的sql模块的paramstyle不是'format', 'pyformat',也就是你的sql 形式是"SELECT * FROM user_info WHERE email = ?" 之类的你会更好的理解代码为什么是这样的。
    3.自定义表名的时候应该先拼接表名(因为表名是安全的),再参数化查询(用户输入条件不安全)。不建议转义或过滤特殊字符。 cursor.execute("SELECT * FROM %s WHERE email = ?" % user_info, youremail) 或者 cursor.execute("SELECT * FROM %s WHERE email = %%s" % user_info, youremail)
    alexrezit
        2
    alexrezit  
       2013-04-15 12:25:15 +08:00   ❤️ 1
    之前也遇到过这个问题, 我来说一下吧: 表名都要自己过滤的, 包括其他语言例如 C 的也一样, 只能 bind 参数.
    如果一定要用参数的话请去掉除去 a-Z, 0-9 之外的字符.

    ```
    def scrub(table_name):
    return ''.join( chr for chr in table_name if chr.isalnum() )

    scrub('); drop tables --') # returns 'droptables'
    ```

    ref: http://stackoverflow.com/questions/3247183/variable-table-name-in-sqlite
    Livid
        3
    Livid  
    MOD
       2013-04-15 12:32:29 +08:00   ❤️ 1
    如果用了一个 ORM 框架的话,大部分这类细节 ORM 框架会搞定的。
    1ang
        4
    1ang  
       2013-04-16 16:24:59 +08:00   ❤️ 1
    1. 如果你的SQL表名都是从HTML FORM里提交过来的话,说明这个设计已经不是很好了,想一下如何做的更好把
    2. 如果一定要这样做的话,先自己过滤表名,只允许0-9a-zA-Z$_这些字符,然后 sql = "SELECT * FROM %s WHERE email = %%s" % tablename,最后再cursor.execute(sql, yourmail)

    这个use case 大部分ORM也解决不了,自己做就好了。
    nichan
        5
    nichan  
    OP
       2013-04-16 17:52:35 +08:00
    感谢各位回复~

    事实上我的表名并不是直接从html里提交过来的,我会对每一个用户提交过来的表名做一次类似于base64的加密,以保证最终执行的sql语句符合语法,这样从表名处进行注入也似乎是不太可能了。但是转换过的表名依然是一个变量,依然要使用第二种(不安全的)语句带入到sql语句中。
    非常感谢@lilian57 @alexrezit @1ang 提供的思路!已实验成功~
    binux
        6
    binux  
       2013-04-16 17:54:55 +08:00   ❤️ 2
    @nichan base64不是加密算法!即使用了base64也能被注入
    nichan
        7
    nichan  
    OP
       2013-04-16 18:08:21 +08:00
    @binux 嗯~base64不是,只是类似于base64。
    不过即使是base64应该也很难被注入吧?加密后的字符只有a-z和0-9
    1ang
        8
    1ang  
       2013-04-17 04:37:38 +08:00
    所以你的这个应用总共会有多少个表?
    nichan
        9
    nichan  
    OP
       2013-04-17 09:20:07 +08:00
    @1ang 大概有十几个。
    面向内部用户使用,不考虑seo,所以在尝试一些新的东西,Tornado基本上自己不主动生成页面,主体页面的布局、内容由储存在用户本地的js通过ajax请求生成。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3768 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 05:04 · PVG 13:04 · LAX 21:04 · JFK 00:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.