0%

Python 防范 SQL 注入 —— 预编译语句

前一段时间刚好用到了 Python 去直连数据库做一些数据分析的操作,习惯了 ORM 之后 SQL 语句也丢下比较长的时间了,好一会才想起来 SQL 注入这一回事。之前学习 Python 的时候也没额外留意这一个点(也有可能是我忘记其实有这个知识点),原以为可能要我自己去实现这一套玩意了。但是 Python 的哲学里面有这么一条 Although never is often better than *right* now. 格言,就先花点时间去搜一下,果然发现了在 DB API 2.0 已经有解决方案。

在关于 execute 方法的定义中, 有这么一行:

Parameters may be provided as sequence or mapping and will be bound to variables in the operation. Variables are specified in a database-specific notation (see the module’s paramstyle attribute for details)

简单来说就是 .execute(operation [, parameters]) 函数本身有接受sql语句参数位的,可以通过 python 自身的函数处理sql注入问题, 通过传入特定形式的参数来生成预编译语句。那么,这个特定形式的参数又是什么呢?引用中可以看到 DB API 2.0 中包含的另一个属性:paramstyle。我们看一下他的定义:

String constant stating the type of parameter marker formatting expected by the interface.

有点笼统,表明的是 paramstyle 是接口所期望的格式化类型,由接口这里展开的话会涉及 goose type 风格的内容(因为 Python 没有接口),想了一下还是觉得用 C# 的 IFormatProvider 类型比较相似,熟悉 C# 的可能一下就明白了。但这里如果不明白细节的话也不太影响我们使用,就把他当成是一个字符串常量就好了 :)。这里顺带列一下所支持的 paramstyle

paramstyle Meaning
qmark Question mark style, e.g. …WHERE name=?
numeric Numeric, positional style, e.g. …WHERE name=:1
named Named style, e.g. …WHERE name=:name
format ANSI C printf format codes, e.g. …WHERE name=%s
pyformat Python extended format codes, e.g. …WHERE name=%(name)s

这个东西呢,也不用记得太牢,因为实际上 DB 模块并不会都支持以上所有的 paramstyle 。而且这里是 Python,当你使用任何实现了 DB API 2.0 标准的模块的时候(获悉当前模块支持的是哪个api标准,可以用 apilevel 模块属性查看),都可以查得到它支持对应的哪个 paramstyle (使用 paramstyle 模块属性)。比方说 Cx_Oracle 就只支持 named 风格,而自带的 sqlite3 模块就只支持 qmark 风格…

看来之前关于 Python DB 这部分光靠 《Core Python programming》 学的还是不够深入,有时间的话要好好的把 DB API 2.0 的标准看一遍。