背景
上周,我写的一个web项目(基于tornado框架)中对数据库部分的操作被同事质疑。同事质疑此段代码的写法存在被sql注入攻击的风险,故我分析了torndb的源码,看我写的代码是否真的存在被sql注入攻击的风险。同时撰写此博客,记录源码分析结果。
相关前提
Google: python mysqldb injection ,或者百度: python mysql 防注入,很容易得到一个结论:
利用MySQLdb的cursor的execute方法,将众参数作为一个tuple传入execute,避免通过拼接字符串的方式直接传入一个完整的sql语句,就可以做到防sql注入,大致情形如下:
# 可以防止sql注入的写法
cur.execute('select * from table where col=%s and row=%s',(user_col_in, user_row_in))
# 不能防止sql注入的写法(拼接字符串)
cur.execute('select * from table where col=%s and row=%s' % (user_keyin, user_row_in))
项目源代码样例
def get_all(self):
list = self.db.query('SELECT * FROM table')
return list
def get_xxx_by_id(self, id):
xxx = self.db.get('SELECT xxx FROM table WHERE id=%s', id)
return xxx
def insert_xxx(self, xxx):
sql_format = 'INSERT INTO `xxx` (`username`, `name`, `email`, `status`, `category`, `add_time`, `update_time`, `comment`) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)'
xxx_id = self.db.insert(sql_format, xxx['username'], xxx['name'], xxx['email'], 1, 1, xxx['add_time'], xxx['update_time'], xxx['comment'])
return staff_id
同事认为这些代码没有使用execute()方法,而是使用了get,query,insert等方法,类似于直接拼接字符串,存在非常高的被sql注入攻击的隐患。
torndb源码剖析
查看 torndb.py,可以发现:
说明get方法其实也就是调用了query,然后取第一条结果返回,如果不止一条结果,就抛出异常。
那就观察query方法,发现其调用了一个_execute方法,是不是跟MySQLdb的execute()有点像呢?
跟入_execute函数:
可以看到这完全就是防止sql注入的写法,而这个cursor就是:
结论
可以看到torndb就是对MySQLdb进行了一层很简单的封装。不管get还是query还是insert等等等等,底层都是用MySQLdb的execute的防注入形式实现的。
所以你只要不是把所有参数直接拼接进了一个字符串,比如query_str,然后只传query_str这一个参数进去,就不存在被sql注入攻击的风险。
无论get还是query还是insert,都可以按照get/query/insert(query_str, (query_tuple))
的方式直接使用,没有任何问题,因为底层都是用execute(query_str, (query_tuple))
封装的。
什么?你说:“谁跟你说execute(query_str, (query_tuple))
就是安全的?”
请移步我的另一篇博客:
Python - MySqldb 防sql注入 - 底层原理分析