【问题标题】:Python: best practice and securest way to connect to MySQL and execute queriesPython:连接到 MySQL 和执行查询的最佳实践和最安全的方法
【发布时间】:2011-12-17 06:42:44
【问题描述】:

在 MySQL 上运行查询最安全的方法是什么?我知道 MySQL 和 SQL 注入所涉及的危险。

但是,我不知道应该如何运行查询以防止注入其他用户(网络客户端)可以操作的变量。我曾经编写自己的转义函数,但显然这是“未完成”。

我应该使用什么以及如何使用它通过 python 在 MySQL 数据库上安全地查询和插入而不冒 MySQL 注入的风险?

【问题讨论】:

    标签: python mysql sql-injection


    【解决方案1】:

    为避免注入,请使用execute%s 代替每个变量,然后通过列表或元组将值作为execute 的第二个参数传递。这是example from the documentation

    c=db.cursor()
    max_price=5
    c.execute("""SELECT spam, eggs, sausage FROM breakfast
              WHERE price < %s""", (max_price,))
    

    请注意,这是使用 逗号,而不是 %(这将是直接的字符串替换,而不是转义)。 不要这样做

    c.execute("""SELECT spam, eggs, sausage FROM breakfast
              WHERE price < %s""" % (max_price,))
    

    此外,如果参数是一个字符串,因为驱动程序提供了这些,您不能在位置持有者 ('%s') 周围使用单引号。

    【讨论】:

    • 也使用%s(参见上面的max_price 示例)。
    • 为什么 max_price 后面有逗号?这是什么意思 ?对不起,如果我的问题看起来很无聊,但我对 python 很陌生 :)
    • max_price 后面的逗号是 1 元素元组的表示法:docs.python.org/tutorial/…
    • @HussainTamboli,是的,这正是我所说的:逗号是使用参数占位符的正确方法(它会进行所有必要的转义),% 不会转义参数。跨度>
    • @lucidbrot 表名不是参数。如果您需要使用来自变量的表名动态构建查询,则需要在将它们放入查询字符串之前手动清理这些变量(而不是通过参数占位符)。例如,只允许与[a-z0-9_]+ 匹配的表名。
    【解决方案2】:

    作为布鲁诺答案的扩展,您的 MySQL 客户端库可能支持几种不同格式中的任何一种来指定命名参数。从PEP 249 (DB-API),您可以编写如下查询:

    'qmark'

    >>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = ?", (lumberjack,))
    

    '数字'

    >>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = :1", (lumberjack,))
    

    '命名'

    >>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = :jack", {'jack': lumberjack})
    

    '格式'

    >>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = %s", (lumberjack,))
    

    'pyformat'

    >>> cursor.execute("SELECT spam FROM eggs WHERE lumberjack = %(jack)s", {'jack': lumberjack})
    

    您可以通过查看paramstyle 模块级变量来了解您的客户端库支持哪些:

    >>> clientlibrary.paramstyle
    'pyformat'
    

    在处理您可能不安全的数据方面,上述任何选项都应该做正确的事。正如布鲁诺指出的那样,请不要尝试自己插入参数。常用的客户端库在正确处理数据方面比我们普通人要好得多。

    【讨论】:

    • 哪些客户端库支持“命名”? PyMySQL 和 MySQLdb 支持 'format',oursql 支持 'qmark'。
    • sqlite3 至少支持'named'。我没有安装任何 MySQL 适配器来检查它们是否支持“命名”。
    • 我知道这是一个老问题,但我正在尝试正确编码我的网页并且在安全 SQL 方面没有太多经验。使用上述方法是否足以防止 SQL 注入,或者除此之外我还需要做其他事情吗?谢谢。
    • 这本身就足够了。不过,我强烈建议您查看像 SQLAlchemy 这样的 ORM 并让它为您处理细节。
    【解决方案3】:

    如果您使用的是 mysqldb,则可以使用内置的 escape_string 函数。像这样。

    sql = "SELECT spam FROM eggs WHERE lumberjack = '" + MySQLdb.escape_string(str(lumberjack)) + "';"
    cursor.execute(sql)
    

    我总是更喜欢使用数据库连接器的转义功能 - 它按预期工作,并且自己手动编写转义函数存在安全风险。

    【讨论】:

    猜你喜欢
    • 2011-03-19
    • 1970-01-01
    • 1970-01-01
    • 2011-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-18
    相关资源
    最近更新 更多