为什么 PyMySQL 不易受到 SQL 注入攻击?
Why is PyMySQL not vulnerable to SQL injection attacks?
我是 PyMy 的新手SQL,刚刚尝试执行查询:
c.execute('''INSERT INTO mysql_test1 (
data,
duration,
audio,
comments
) VALUES (
?,
?,
?,
?
);
''', [
comments_var,
duration_var,
audio_var,
comments_var
]
);
但是,它引发了以下错误:
TypeError: not all arguments converted during string formatting
我注意到我的变量一定有问题,并阅读了如何在 PyMySQL 中正确处理它们,期待参数替换的方法,但令我惊讶的是我找不到任何东西。相反,我发现的每个线程都使用了字符串操作(例如 , , and (有一条评论声称字符串操作将是 PyMySQL 的标准)。
这对我来说很有趣,因为我以前只处理过 SQLite,其中 DBAPI 文档 explicitly warns 对变量使用字符串操作:
SQL operations usually need to use values from Python variables. However, beware of using Python’s string operations to assemble queries, as they are vulnerable to SQL injection attacks.
文档使用以下代码片段对此进行了举例说明:
Never do this -- insecure!
symbol = 'RHAT'
cur.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
Instead, use the DB-API’s parameter substitution.
阅读 PyMySQL docs 时,我找不到任何提及此类危险的信息。它只是证实了我之前的发现:
If args is a list or tuple, %s can be used as a placeholder in the query. If args is a dict, %(name)s can be used as a placeholder in the query.
为什么在 sqlite3
中使用字符串操作被认为容易受到 SQL 注入攻击,同时在 pymysql
中没有受到质疑?
可惜pymysql的设计者选择了%s
作为参数占位符。它使许多开发人员感到困惑,因为它与字符串格式化函数中使用的 %s
相同。但它在 pymysql 中并没有做同样的事情。
这不仅仅是简单的字符串替换。 Pymysql 将在将值插入 SQL 查询之前将 escaping 应用于这些值。这可以防止特殊字符更改 SQL 查询的语法。
事实上,使用 pymysql 也会遇到麻烦。以下是不安全的:
cur.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
因为它将变量 symbol
插入到字符串中,然后再将其作为参数传递给 execute()
。唯一的参数是一个完成的 SQL 字符串,其中包含格式化的变量。
虽然这是安全的:
cur.execute("SELECT * FROM stocks WHERE symbol = %s", (symbol,))
因为它将包含 symbol
变量的列表作为第二个参数传递。 execute()
函数中的代码将转义应用于列表中的每个元素,并将结果值插入到 SQL 查询字符串中。注意 %s
没有用单引号分隔。 execute()
的代码会处理这个问题。
我是 PyMy 的新手SQL,刚刚尝试执行查询:
c.execute('''INSERT INTO mysql_test1 (
data,
duration,
audio,
comments
) VALUES (
?,
?,
?,
?
);
''', [
comments_var,
duration_var,
audio_var,
comments_var
]
);
但是,它引发了以下错误:
TypeError: not all arguments converted during string formatting
我注意到我的变量一定有问题,并阅读了如何在 PyMySQL 中正确处理它们,期待参数替换的方法,但令我惊讶的是我找不到任何东西。相反,我发现的每个线程都使用了字符串操作(例如
这对我来说很有趣,因为我以前只处理过 SQLite,其中 DBAPI 文档 explicitly warns 对变量使用字符串操作:
SQL operations usually need to use values from Python variables. However, beware of using Python’s string operations to assemble queries, as they are vulnerable to SQL injection attacks.
文档使用以下代码片段对此进行了举例说明:
Never do this -- insecure!
symbol = 'RHAT'
cur.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
Instead, use the DB-API’s parameter substitution.
阅读 PyMySQL docs 时,我找不到任何提及此类危险的信息。它只是证实了我之前的发现:
If args is a list or tuple, %s can be used as a placeholder in the query. If args is a dict, %(name)s can be used as a placeholder in the query.
为什么在 sqlite3
中使用字符串操作被认为容易受到 SQL 注入攻击,同时在 pymysql
中没有受到质疑?
可惜pymysql的设计者选择了%s
作为参数占位符。它使许多开发人员感到困惑,因为它与字符串格式化函数中使用的 %s
相同。但它在 pymysql 中并没有做同样的事情。
这不仅仅是简单的字符串替换。 Pymysql 将在将值插入 SQL 查询之前将 escaping 应用于这些值。这可以防止特殊字符更改 SQL 查询的语法。
事实上,使用 pymysql 也会遇到麻烦。以下是不安全的:
cur.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)
因为它将变量 symbol
插入到字符串中,然后再将其作为参数传递给 execute()
。唯一的参数是一个完成的 SQL 字符串,其中包含格式化的变量。
虽然这是安全的:
cur.execute("SELECT * FROM stocks WHERE symbol = %s", (symbol,))
因为它将包含 symbol
变量的列表作为第二个参数传递。 execute()
函数中的代码将转义应用于列表中的每个元素,并将结果值插入到 SQL 查询字符串中。注意 %s
没有用单引号分隔。 execute()
的代码会处理这个问题。