一、SQL注入
1、什么是SQL注入?
SQL注入属于较为常见的一种网络攻击手法,主要攻击目标锁定在数据库上,针对程序员在编写代码时出现的疏漏,借助SQL语句,达成无需账号即可登录、篡改数据库内容的目的。
简单来说,SQL注入就是通过在表单当中填入包含SQL关键字的数据,从而让数据库执行非常规代码的过程。
SQL数据库的各项操作都是借助SQL语句来执行的,这就导致一旦我们在代码里混入了某些SQL关键字(比如DELETE、DROP等),这些关键字就极有可能在数据库进行写入或读取数据时被一并执行。
2、SQL注入攻击的整体思路
寻找SQL注入可能存在的位置;
判断服务器类型以及后台数据库的类型;
针对不同服务器与数据库各自的特性展开SQL注入攻击。
3、SQL注入案例演示
来看一个SQL注入的实际案例。
正常代码
import sqlite3
# 连接数据库
conn = sqlite3.connect('test.db')
# 建立新的数据表
conn.executescript('''
DROP TABLE IF EXISTS students;
CREATE TABLE students
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL);'''
# 插入学生信息
students = ['Paul','Tom','Tracy','Lily'] for name in students:
query = "INSERT INTO students (name) VALUES ('%s')" % (name)
conn.executescript(query);
# 检视已有的学生信息
cursor = conn.execute("SELECT id, name from students")
print('IDName') for row in cursor:
print('{0}{1}'.format(row[0], row[1]))
conn.close()
SQL注入代码
# 连接数据库
conn = sqlite3.connect('test.db')
# 插入包含注入代码的信息
name = "Robert');
DROP TABLE students;
--" query = "INSERT INTO students (name) VALUES ('%s')" % (name) conn.executescript(query)
# 检视已有的学生信息
cursor = conn.execute("SELECT id, name from students")
print('IDName') for row in cursor:
print('{0}{1}'.format(row[0], row[1]))
conn.close()
上述代码一旦执行,其造成的后果可想而知。
二、怎样防范SQL注入
凡是存在SQL注入漏洞的程序,都是因为程序需要接收来自客户端用户输入的变量,或是URL传递过来的参数,而这个变量或参数又恰恰是构成SQL语句的一部分,对于用户输入的内容或传递而来的参数,我们理应时刻保持警觉,这正是安全领域中”外部数据不可轻信”这一原则的体现。
纵观Web安全领域中的各类攻击手段,大多数都是由于开发者违背了这一原则所导致的,因此自然而然能想到的应对方式,便是从变量的检测、过滤、验证这几个环节着手,确保变量符合开发者原本的预期。
1、核查变量的数据类型与格式
如果SQL语句是类似where id={$id}这样的形式,而数据库中所有的id均为数字类型,那么就应当在SQL语句被执行之前,核实确保变量id属于int类型;倘若是其他类型,比如日期、时间等,道理也是相通的。只要是具备固定格式的变量,在SQL语句执行前,都应当严格依照固定格式进行核查,确保变量符合我们的预期格式,这样在很大程度上便能规避SQL注入攻击的发生。
2、过滤特殊符号
对于无法确定固定格式的变量,务必要进行特殊符号的过滤或转义处理。
3、绑定变量,采用预编译语句
事实上,绑定变量并采用预编译语句是预防SQL注入最为理想的方式,采用预编译的SQL语句其语义不会发生变化,在SQL语句当中,变量以问号?来表示,黑客即便本领再高强,也无法更改SQL语句原本的结构。
为何SQL预编译能够有效防御SQL注入
1、什么是预编译语句?
一条SQL语句从数据库接收到最终执行完毕并返回结果,可以划分为以下三个阶段:
词法与语义解析;
对SQL语句进行优化,制定相应的执行计划;
执行语句并返回结果。
许多情况下,同一类型的SQL语句可能会被反复执行,倘若每次都要经历上述的词法语义解析、语句优化、制定执行计划等流程,不仅会影响执行效率,也存在安全隐患。
所谓预编译语句,就是将这类语句当中的具体数值用占位符加以替代,可以将其理解为把SQL语句模板化或者说参数化处理,通常将这类语句称为Prepared Statements。
预编译语句的优势可以归纳为:一次编译、多次运行,省去了反复解析优化等环节;此外预编译语句还能够有效防止SQL注入的发生。
2、为何Statement容易遭受SQL注入?
Statement之所以容易遭受SQL注入,是因为SQL语句的结构发生了变化。
比如:
"select * from tablename where username='"+uesrname+ "'and password='"+password+"'"
在用户输入’or true or’之后SQL语句结构便发生了改变。
select * from tablename where username='' or true or '' and password=''
这样一来,原本应当是判断用户名与密码同时匹配才算通过,但经过改动之后变成了”或”的逻辑关系,无论用户名和密码是否匹配,该表达式的返回值永远都是true;
3、为何Preparement能够防止SQL注入?
其原理在于采用了预编译的方式,先将SQL语句中可被客户端所控制的参数集合进行编译处理,生成相应的临时变量集合,再借助对应的设置方法,为临时变量集合当中的元素赋值,赋值函数setString()会对传入的参数实施强制类型检查以及安全检查,因此便避免了SQL注入现象的产生。
Preparement的样式如下:
select*from tablename where username=? and password=?
该SQL语句会在获取用户输入之前先在数据库中完成预编译处理,这样一来无论用户输入怎样的用户名和密码,其判断逻辑始终都是”并”的关系,从而有效防止了SQL注入的发生。
原创文章,作者:余初云,如若转载,请注明出处:https://blog.jidcy.com/jsjc/3130.html
