【问题标题】:PyMySQL - Escaping identifiersPyMySQL - 转义标识符
【发布时间】:2018-08-15 22:59:18
【问题描述】:

我正在使用 PyMySQL,在编写查询时,我需要转义某些用户提供的标识符(表名等)。例如,这里有一个不好的例子来说明我的意思:

sql = "SELECT * FROM {}".format(table_name)
cursor.execute(sql)

在这种情况下,table_name 是用户提供的,因此上述代码显然容易受到 SQL 注入攻击。

我首先尝试做这样的事情:

sql = "SELECT * FROM %s"
cursor.execute(sql, (table_name,))

似乎上面的代码在转义标识符时不起作用,它只适用于值。这并不奇怪,因为我在其他语言的其他 MySQL 库中看到了类似的行为。

我使用过其他库,例如 Node.js 中的 mysql,它们具有转义标识符的功能。我在PyMySQL documentation 中看不到任何表明类似功能的东西。我发现这个StackOverflow question 问了一个类似的问题,但答案类似于“你不应该这样做”。好吧,我确实需要这样做! ;)

在 Python 中使用 MySQL 时,我有哪些转义标识符的选项?谢谢!

【问题讨论】:

  • “这并不奇怪,因为我在其他语言的其他 MySQL 库中也看到过类似的行为” 然后由框架模拟,MySQL 原生准备协议只允许准备值而不是数据库,表或列名。

标签: python mysql sql-injection pymysql


【解决方案1】:

python 驱动程序模拟查询参数,但遵循类似的规则。像 %s 这样的占位符将被替换为 quoted 字符串,并转义字符串,因此任何文字撇号字符都将插入 \

sql = "SELECT * FROM mytable WHERE name = %s"
cursor.execute(sql, (myname,))

会产生SQL:

SELECT * FROM mytable WHERE name = 'O\'Reilly'

反斜杠是防止 SQL 注入所必需的。

但它对表名没有用,因为单引号用于字符串或日期文字,而不是标识符。

你可以这样做:

sql = "SELECT * FROM `{}`".format(table_name)

反引号分隔标识符,以便您可以在表名中使用保留字或空格或其他特殊字符。

您需要确保table_name 的值不包含任何文字反引号字符,或者如果表名中有文字反引号,则插入反斜杠。

【讨论】:

    【解决方案2】:

    @Bill Karwin 扩展答案,您需要自己转义反引号,因为 pymysql 本身并没有这样做,甚至它的escape_string() 方法也没有。

    然而,与比尔所说的相反,为了避免表名中可能出现的反引号,您需要将它们加倍,而不是使用 \。来自mysql's documentation

    如果您引用标识符,则标识符引号字符可以包含在标识符中。如果要包含在标识符中的字符与用于引用标识符本身的字符相同,则需要将字符加倍。

    这基本上意味着为了逃避反引号,您需要双反引号。这就是我最终采取的措施:

    table = 'table`; drop table users; -- '
    sane_table = pymysql.escape_string(table).strip('`').replace('`', '``')
    cursor.execute(f"SELECT * FROM `{sane_table}`"))
    

    这将正确地转义表名并报告正确的错误:

    pymysql.err.ProgrammingError: (1103, "Incorrect table name 'table`; drop table users; -- '")
    

    【讨论】:

      猜你喜欢
      • 2023-03-15
      • 1970-01-01
      • 2021-11-16
      • 2021-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多