首页 > 解决方案 > 以安全的方式从字典数据创建表

问题描述

我有一个问题,我有一个字典列表,例如以下数据:

columns = [{
    'name': 'column1',
    'type': 'varchar'
},
{
    'name': 'column2',
    'type': 'decimal'
},
.
.
.
]

从该列表中,我需要根据列表中包含列名称和类型的每个字典动态创建一个 CREATE TABLE 语句,并使用 psycopg2 适配器在 PostgreSQL 数据库上执行它。

我设法做到了:

columns = "(" + ",\n".join(["{} {}".format(col['name'], col['type']) for col in columns]) + ")"
cursor.execute("CREATE TABLE some_table_name\n {}".format(columns))

但是这种解决方案容易受到 SQL 注入的影响。我试图用 psycopg2 的 sql 模块做同样的事情,但没有运气。总是出现语法错误,因为它将类型括在引号中。

有什么方法可以安全地完成吗?

标签: pythonsql-injectionpsycopg2

解决方案


您可以使用AsIs来获取添加的列类型不带引号:

import psycopg2
from psycopg2.extensions import AsIs
import psycopg2.sql as sql

conn = psycopg2.connect("dbname=mf port=5959 host=localhost user=mf_usr")

columns = [{
    'name': "column1",
    'type': "varchar"
},
{
    'name': "column2",
    'type': "decimal"
}]

# create a dict, so we can use dict placeholders in the CREATE TABLE query.
column_dict = {c['name']: AsIs(c['type']) for c in columns}

createSQL = sql.SQL("CREATE TABLE some_table_name\n ({columns})").format(
    columns = sql.SQL(',').join(
        sql.SQL(' ').join([sql.Identifier(col), sql.Placeholder(col)]) for col in column_dict)
)

print(createSQL.as_string(conn))
cur = conn.cursor()
cur.execute(createSQL, column_dict)
cur.execute("insert into some_table_name (column1) VALUES ('foo')")
cur.execute("select * FROM some_table_name")
print('Result: ', cur.fetchall())

输出:

CREATE TABLE some_table_name
 ("column1" %(column1)s,"column2" %(column2)s)
Result:  [('foo', None)]

注意:
psycopg2.sql对 SQL 注入是安全的,AsIs可能不是。测试使用'type': "varchar; DROP TABLE foo"导致 Postgres 语法错误:

b'CREATE TABLE some_table_name\n ("column1" varchar; DROP TABLE foo,"column2" decimal)'
Traceback (most recent call last):
  File "pct.py", line 28, in <module>
    cur.execute(createSQL, column_dict)
psycopg2.errors.SyntaxError: syntax error at or near ";"
LINE 2:  ("column1" varchar; DROP TABLE foo,"column2" decimal)

推荐阅读