- SQL注入
- 解决方案
- 解决方案
SQL注入
SQL注入 是一个很常见的形式,在SQL注入中,攻击者改变web网页的参数(例如 GET
/POST
数据或者URL地址),加入一些其他的SQL片段。 未加处理的网站会将这些信息在后台数据库直接运行。
这种危险通常在由用户输入构造SQL语句时产生。 例如,假设我们要写一个函数,用来从通信录搜索页面收集一系列的联系信息。 为防止垃圾邮件发送器阅读系统中的email,我们将在提供email地址以前,首先强制用户输入用户名。
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = '%s';" % username
# execute the SQL here...
备注
在这个例子中,以及在以下所有的“不要这样做”的例子里,我们都去除了大量的代码,避免这些函数可以正常工作。 我们可不想这些例子被拿出去使用。
尽管,一眼看上去,这一点都不危险,实际上却不尽然。
首先,我们对于保护email列表所采取的措施,遇到精心构造的查询语句就会失效。 想象一下,如果攻击者在查询框中输入 "' OR 'a'='a"
。 此时,查询的字符串会构造如下:
SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a';
由于我们允许不安全的SQL语句出现在字符串中,攻击者加入 OR
子句,使得每一行数据都被返回。
事实上,这是最温和的攻击方式。 如果攻击者提交了 "'; DELETE FROM user_contacts WHERE 'a' = 'a'"
,我们最终将得到这样的查询:
SELECT * FROM user_contacts WHERE username = ''; DELETE FROM user_contacts WHERE 'a' = 'a';
哦!我们整个通信录名单去哪儿了? 我们整个通讯录会被立即删除
解决方案
尽管这个问题很阴险,并且有时很难发现,解决方法却很简单: 绝不信任用户提交的数据,并且在传递给SQL语句时,总是转义它。
Django的数据库API帮你做了。 它会根据你所使用的数据库服务器(例如PostSQL或者MySQL)的转换规则,自动转义特殊的SQL参数。
举个例子,在下面这个API调用中:
foo.get_list(bar__exact="' OR 1=1")
Django会自动进行转义,得到如下表达:
SELECT * FROM foos WHERE bar = '\' OR 1=1'
完全无害。
这被运用到了整个Django的数据库API中,只有一些例外:
传给
extra()
方法的where
参数。 (参考 附录 C。) 这个参数故意设计成可以接受原始的SQL。使用底层数据库API的查询。 (详见第十章)
以上列举的每一个示例都能够很容易的让您的应用得到保护。 在每一个示例中,为了避免字符串被篡改而使用 绑定参数 来代替。这样,本节开始的例子应该写成这样:
from django.db import connection
def user_contacts(request):
user = request.GET['username']
sql = "SELECT * FROM user_contacts WHERE username = %s"
cursor = connection.cursor()
cursor.execute(sql, [user])
# ... do something with the results
底层 execute
方法采用了一个SQL字符串作为其第二个参数,这个SQL字符串包含若干’%s’占位符,execute方法能够自动对传入列表中的参数进行转义和插入。 你应该用 always 这种方式构造自定义的SQL。
不幸的是,您并不是在SQL中能够处处都使用绑定参数,绑定参数不能够作为标识符(如表或列名等)。 因此,如果您需要这样做—我是说—动态构建 POST
变量中的数据库表的列表的话,您需要在您的代码中来对这些数据库表的名字进行转义。 Django提供了一个函数, django.db.backend.quote_name
,这个函数能够根据当前数据库引用结构对这些标识符进行转义。