93. 如何防止SQL注入攻击

SQL是指结构化查询语言,在关系型数据库中,使用SQL来操作数据库。

SQL注入攻击是伪造一些特殊请求字符串,来操作数据库。产生一些和程序员预期行为不一致的结果。

例如用户登录,通常有几种实现方法,假定设计用户认证表为包含三个字段,id, user_name, passwd, 表中含有三条记录:

1, zhang   abcdef
2, wang    666666
3, zhao    999999

如果某个用户登录,我们的python代码如下:

import sqlite3

class DatabaseConn:
    def __init__(self, sql_db):
        self.conn = sqlite3.connect(sql_db)

        self.conn.row_factory = sqlite3.Row
        self.c = self.conn.cursor()
        self.c.execute('''CREATE TABLE  IF NOT EXISTS users (user_name text, passwd text)''')

    def query(self, username, passwd):
        """不安全的用法"""
        sql = "select * from users where user_name='{}' and passwd='{}'".format(username, passwd)
        print(sql)

        self.c.execute(sql)

        r = self.c.fetchone()
        print("row type:", type(r))

    def query2(self, username, passwd):
        """安全用法,在传入参数时,使用?来格式化执行"""

        self.c.execute("select * from users where user_name=? and passwd=? ", [username, passwd])
        r = self.c.fetchone()
        print("row type:", type(r))

    def __del__(self):
        self.conn.close()


conn = DatabaseConn('newdict.db')
conn.query("zhang", "abcdef")
conn.query("zhang", "123456")
conn.query("zhang", "' or 1!='")

conn.query2("zhang", "abcdef")
conn.query2("zhang", "123456")
conn.query2("zhang", "' or 1!='")

我们使用DatabaseConn对象来管理sqlite数据库连接,对象创建时连接数据库。 释放时关闭数据库。通过query,query2方法来对数据进行查询,我们对比一下 两个方法的差异。

如果我们使用query方法来查询用户账号和密码,如果是正确的密码,会找到记录 ,允许登录。如果是错误的密码,例如123456,找不到记录,不允许登录。 假设用户输入密码为带有单引号字符”’ or 1!=’”, 也会执行找到记录, 因为sql语句会转换为:

select * from users where user_name='zhang' and passwd='' or 1!=''

这就是sql注入攻击,利用了我们代码实现的缺陷来对平台进行操作,甚至销毁数据库等等。 因此我们要防范数据库注入攻击。

使用query2方法可以避免这种sql注入攻击。他会把参数进行编译,只是当做一个参数, 而不会当做sql关键字来执行。