执行用户表:
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 9, in count_rowspsycopg2.errors.SyntaxError: syntax error at or near "'users'"LINE 5: 'users'该命令无法生成SQL 。数据库适配器将变量视为字符串或文字,但是表名不是普通的字符串 。所以这就是SQL组合的用武之地 。
现在已经知道使用字符串插值表达式来编写SQL是不安全的 。幸好,Psycopg提供了一个名为Psycopg的模块 。帮助我们安全地编写sql查询 。让我们使用psycopg.sql()重写这个函数:
from psycopg2 import sqldef count_rows(table_name: str) -> int: with connection.cursor() as cursor: stmt = sql.SQL(""" SELECT count(*) FROM {table_name} """).format( table_name = sql.Identifier(table_name), ) cursor.execute(stmt) result = cursor.fetchone() rowcount, = result return rowcount现在有两个不同之处 。首先,使用sql()来组合查询 。然后,使用sql.Identifier()来注释参数值table_name 。(标识符是列或表名 。)
现在,尝试执行用户表上的函数:
>>> count_rows('users')2接下来,让我们看看当表不存在时会发生什么:
>> count_rows('foo')Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 11, in count_rowspsycopg2.errors.UndefinedTable: relation "foo" does not existLINE 5: "foo" 该函数抛出UndefinedTable异常 。在接下来的步骤中,我们将使用这个异常来表明函数不会受到Python SQL注入攻击 。
为了将它们放在一起,添加一个选项来将表中的行数计数到一定的限制,这个特性对非常大的表很有用 。要实现这一点,在查询中添加一个LIMIT子句,以及LIMIT值的查询参数:
from psycopg2 import sqldef count_rows(table_name: str, limit: int) -> int: with connection.cursor() as cursor: stmt = sql.SQL(""" SELECT COUNT(*) FROM ( SELECT 1 FROM {table_name} LIMIT {limit} ) AS limit_query """).format( table_name = sql.Identifier(table_name), limit = sql.Literal(limit), ) cursor.execute(stmt) result = cursor.fetchone() rowcount, = result return rowcount在这个代码块中,使用sql.Literal()注释了limit 。与前面的示例一样,psycopg在使用时将所有查询参数绑定为文字 。但是,在使用sql()时,需要使用sql.Identifier()或sql.Literal()显式地注释每个参数 。
执行该功能,以确保运行正常:
>> count_rows('users', 1)1>>> count_rows('users', 10)2
运行正常,确保也是安全的:
>> count_rows("(select 1) as foo; update users set admin = true where name = 'haki'; --", 1)Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 18, in count_rowspsycopg2.errors.UndefinedTable: relation "(select 1) as foo; update users set admin = true where name = '" does not existLINE 8: "(select 1) as foo; update users set adm...此返回显示psycopg转义了该值,并且数据库将其视为表名 。由于不存在具有此名称的表,因此引发了UndefinedTable异常,攻击失败了
私信小编01 领取完整项目代码
结论
我们已经成功地实现了一个组成动态SQL的函数,系统面临Python SQL注入的风险也没有了!
我们在查询中既使用了字面值,又使用了标识符,没有影响安全性 。
推荐阅读
- Word如何关闭自动输入序号
- RHEL8和CentOS8怎么设置网络
- 戴尔游戏本g3的外星人控制系统在哪 戴尔g3外星人控制中心怎么一直升级组件
- 13个需要知道的方法:使用 JavaScript 来操作 DOM
- 多肉断头了下面怎么办 多肉断头了下面还能要不
- 淘宝卖家选择货源的渠道有哪些 淘宝卖东西怎么找货源怎么找
- 欧特朗照明怎么样 欧特朗照明价格
- 木林森照明质量怎么样
- 飞利浦照明怎么样 飞利浦照明价格
- 墙纸翘起来了打玻璃胶有用么,墙纸大面积翘起来了怎么办