如何避免使用“SELECT * FROM {table_name}”进行 SQL 注入?

moy*_*260 4 python sql postgresql sql-injection psycopg2

在 Python 中使用 Psycopg2 和以下代码:

import psycopg2

import getpass

conn = psycopg2.connect("dbname=mydb user=%s" % getpass.getuser())
cursor = conn.cursor()

tables = ["user", "group", "partner", "product"]
for table in tables:

    # with sql injection
    cursor.execute("SELECT name FROM %s LIMIT 1" % (table,))
    print "table", table, "result", len(cursor.fetchone())

    # without sql injection
    cursor.execute("SELECT name FROM %s LIMIT 1", (table,))
    print "table", table, "result", len(cursor.fetchone())
Run Code Online (Sandbox Code Playgroud)

输出是:

table res_partner result 1
Traceback (most recent call last):
  File "my_psycopg2_example.py", line 16, in <module>
    cursor.execute("SELECT name FROM %s LIMIT 1", (table,))
psycopg2.ProgrammingError: syntax error at or near "'res_partner'"
LINE 1: SELECT name FROM 'res_partner' LIMIT 1
Run Code Online (Sandbox Code Playgroud)

使用 SQL 注入它工作正常。

但我们不想造成安全问题。

我们阅读了此文档,并在其中找到了以下评论:

只应通过此方法绑定变量值:不应使用它来设置表或字段名称。对于这些元素,在运行之前应该使用普通的字符串格式execute()

但是如果我们使用“普通字符串格式”,我们也会有 SQL 注入。

管理这种特殊情况并避免 SQL 注入的好方法是什么?

Dan*_*etz 5

我认为您混淆了 SQL 注入的定义。SQL 注入是对您的软件的一种攻击,其中有人使您的 SQL 查询执行您不希望它执行的操作。字符串插值不是 SQL 注入。字符串插值有时可以启用 SQL 注入,但并非总是如此。要了解字符串插值并不总是不安全,请考虑以下哪项最安全:

  1. sql = 'SELECT name FROM user'
  2. sql = 'SELECT name FROM ' + 'user'
  3. sql = 'SELECT name FROM %s' % ['user']
  4. sql = 'SELECT name FROM {}'.format('user')

这些代码行中的每一行都做完全相同的事情,因此它们中的任何一条都不会比其他代码更安全或更不安全。在您的确切示例中,没有 SQL 注入的危险,因为您只是在构建硬编码的 SQL 查询字符串。

另一方面,如果您的table价值来自用户,则可能存在安全问题:

您可以通过检查表名是否允许来防止这种情况:

if table.lower() not in ["user", "group", "partner", "product"]:
    raise Something('Bad table name: %r' % table)
Run Code Online (Sandbox Code Playgroud)