SQL 注入攻击的分析与防范
2021-06-24翟宝峰
翟宝峰
(辽宁工业大学 软件学院,辽宁 锦州 121001)
互联网已经在社会各领域中得到了广泛应用,互联网上应用程序模式采用B/S 框架结构,即WEB应用程序,优点是不局限于某个单位的局域网内部,而是面向全世界,具有开放性的特点,部署起来也很方便,促进了各行各业信息化的发展。
大量单位及个人可以通过网络访问WEB 应用程序,给用户带来了方便,但由于互联网的开放性,给数据访问带来了风险,不法分子对以数据库为核心的攻击越来越多,方式也是多种多样。目前大多数数据库均采用关系型数据库,而关系型数据库采用的操纵语言是SQL 语言,SQL 语言的安全使用成为数据安全访问的核心问题。
1 SQL 注入产生原因
1.1 WEB 应用程序结构
WEB 应用程序由3 大部分组成,包括客户端程序、WEB 服务程序、数据库服务程序[1],如图1所示。
图1 WEB 应用程序结构
客户端用户通过浏览器向WEB 服务器发出URL 请求,WEB 服务器调用相应的程序处理请求,如果处理过程中需要访问数据库,WEB 服务器会把访问数据库的请求发给数据库服务器,数据库服务器根据此请求,对数据进行处理,再把结果传给WEB 服务器,WEB 把最后的处理结果以网页形式传回客户端,客户端浏览器再把结果呈现给用户。互联网的用户原则上都能访问WEB 应用程序,方便了用户的同时,也给网络攻击者提供了机会,给数据库服务器的访问带来了不安全的因素,SQL 攻击也存在了可能性。
1.2 SQL 注入
WEB 应用程序的SQL 注入,就是把SQL 命令插入到网页表单、URL 的参数或页面请求的查询字符串中,形成含有SQL 攻击的语句,服务器执行这些语句,攻击者就达到欺骗服务器执行恶意SQL命令的目的[2]。
SQL 注入产生的原因主要有[3]:
(1)SQL 语言是属于拼接式的语言,尤其查询条件字符串也可以拼接而成,注入者违反程序设计者的初衷,拼接了合法的和非法的查询条件,形成含有SQL 注入的SQL 语句。
(2)应用程序设计编写不严谨,没有对SQL 命令的参数进行相应的检查和过滤,直接把用户提交的内容与SQL 命令的关键词拼接成SQL 语句,提交给数据库服务器执行,完成了SQL 的注入攻击。
2 SQL 注入类型及实现
SQL 注入从不同角度看,有不同的类型。从注入点上看,有数字注入点、字符注入点、搜索注入点;从提交方式来分类,可分为GET 注入、POST注入、Cookie 注入、HTTP 头注入;从执行效果来分类,有布尔盲注、时间盲注、报错注入、联合查询注入、堆查询注入,还有其他种种划分方法。
一般根据构造SQL 语句的类型,划分不同SQL注入类型,是漏洞检查及攻击防御所采用的基本方法[4]。
(1)永真条件,将注入代码插入到查询条件中,使查询条件永为真。例如用户登录时提供用户名和密码,输入参数值注入or ‘1’=’1’,则无论用户名、密码是否正确,查询条件都为真,语句就会形成如下:
Select * from t_users where name=’***’ and pwd=’***’ or ‘1’=’1’
(2)系统报错,DBMS 内部信息存放在变量中或函数表示,如SQLServe 中当前数据库连接用户名用user,数据库名用db_name()等等,用这些变量名或函数写成产生错误的表达式,如:and user>1拼接,因为user 为字符型的,转成int 型会报错,系统提示:“将nvarchar 转换int 异常,XXXX 不能转换成int”,就可以知道当前连接的用户名是XXXX。也可以利用附加and (select count(字段名) from 表名)>1 猜测表名或字段名。这种方法可以嗅探出数据库系统的相关信息。
(3)Union 联合查询,Union 是将2 个Select 查询结果放在一起返回,利用这一特点进行拼接,得到想要的信息,例如:Select * from t_users where name=’***’ and pwd=’***’是用户登录验证语句,在后面加上Union Select * from t_users where id=1,前一语句结果为空时就会得到序号为‘1’用户信息。另外,当后面加入Union Select 1,2,3,可以试探出字段名。
(4)连接更新语句,一旦攻击者通过试探注入得知表名及字段名,就可以在提交的参数中植入更新语句,实现增加、删除、修改记录,用‘;’把提交的参数与注入的语句隔开一起提交执行,如:Select * from t_users where name=’***’ and pwd=’***’;update t_users set name=‘李军’where id=3,将3 号用户名更改为‘李军’。如果登录者权限很高时,还可以连接DDL 语句如drop student,如果完整性符合要求,将删除student 表,造成更大损失。
(5)编码转换,当系统中设有过滤防御机制时,将注入的特殊命令,以十六进制、ASCⅡ等不常用的编码机制输入,如:SHUTDOWN(关闭数据库)写成0x5348555444F574E十六进制形式,SQL语句:Select * from t_users where name=’ABC’;exec(char(0x5348555444F574E))--and pwd=’***’,将绕过过滤机制而提交。
(6)SQL 盲注[5],当系统对SQL 语句响应只返回true 或false 时,有时甚至没有响应结果,例如:登录是否成功。这时系统报错的注入就无效了,响应信息极少或没有的情况下的注入攻击称为盲注,这种注入是一种暴力破解,需要时间较长。可分为布尔盲注与时间盲注。
布尔盲注:虽然系统响应仅仅有2 个状态,无法根据响应信息得到所需数据,但可以通过逻辑判断来逐步得到完整信息。例如,想得到数据库的名字,可以在查询参数上附加:
and select length(database())>n//判断数据库名长度and ascii(substr(database(),m,1))>n//第m 个字符并转换成ascii 码,判断具体值。
写一个循环程序反复试探n 的值,结果为真时,附加条件为真,结果为假时,附加条件为假,根据结果的真伪来得到正确数据,在循环程序中也可用折半方法加快检索速度。类似方法可以从系统表中得到表名,以及从具体业务表中得到表的字段名、类型、长度等信息。
时间盲注:在SQL 注入时,不管注入是否成功,系统都没有相应,这时可通过数据库的延时函数来判断注入点,采用响应时间上的差异来判断是否存在SQL 注入,即时间盲注。
这里需要用到一个MySQL 的sleep(n)函数,功能是休眠n 秒,sleep()函数执行是有条件的,必须保障SQL 语句执行结果存在数据记录,才会停止指定的秒数,如果SQL 语句查询结果为空,那么sleep函数不会停止。枚举当前数据库名:
id=1' and sleep(3) and ascii(substr (database(),m,1))>n --+
逻辑判断函数if(),if (expr1,expr2,expr3)如果expr1 为真,则if 函数执行expr2 语句,否则if 函数执行expr3 语句。枚举当前数据库的表名:
id=1' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit a,1),m,1)) >n,sleep(3),1) --+
根据响应时间可以分析出所要的数据,同样采用循环方法,得出全部信息。
以上是当前SQL 注入攻击的常用方法,随着关系库数据库的广泛应用,SQL 注入也会不断增加,SQL 注入的防范方法也会随之加强。
3 SQL 注入防范
由于WEB 应用程序实现的技术不同,SQL 注入攻击也不同,对应的防范方法也有所区别,但总体上,还是针对WEB 应用程序3 个组成部分上进行的。归纳起来有以下几种方法。
(1)输入检查,网页在提交数据时,可以通过正则表达式的筛选及长度的限制对提交数据进行检查。如正则表达式[6]:
@"s?ors*|s?;s?|s?drops|s?grants|^'|s?--|s?u nions|s?deletes|s?truncates|"+@"s?sysobjectss?|s?xp_.*?|s?sysloginss?|s?sysremotes?|s?sysuserss?|s?sysxloginss?|s?sysdatabasess?|s?aspnet_.*?|s?ex ecs?"。但是,有时攻击者可以得到网页源代码,进行修改后,将非法数据提交给服务器。因此,WEB服务器接收数据时,也应进行相应的验证。有些特殊注入,也可特殊处理,如针对登录操作的永真条件注入时,可以多加判断条件,例如:
If (Select count(*) from t_users where name=’***’ and pwd=’***’or‘1’=’1’)>1
当条件满足时,往往是注入攻击,拒绝登录。
(2)错误消息封装,系统提供的错误消息不要直接提供给客户端,经过对象封装后,把提示信息尽量减少,使注入者得不到有效的数据。
(3)避免拼接SQL 语句使用,从SQL 注入分类上看,注入基本上是通过SQL 语句拼接实现的。使用参数化的SQL 语句或存储过程进行数据查询,例如:prepareStatement(String sql),第一次操作数据库之前,SQL 语句已经被数据库分析和编译,对应的执行计划也会缓存下来,之后数据库就会以参数化的形式进行查询。当运行时动态地把参数传给PreprareStatement 时,即使参数里有敏感字符or ‘1=1’,也会作为一个字段的值来处理,而不会作为另一个查询条件。
(4)禁止使用管理员账号连接数据库,管理员对数据库具有最高的访问权限,一旦被注入者登录成功,数据库的危险性会更大。要为每个应用程序分配独立的连接账户,数据库对象权限由管理员严格控制,慎重分配更新操作insert、delete、update权限,对于DDL 命令权限drop、alter、create 等,原则上应禁止分配给连接账号。
(5)数据加密,对于重要数据要加密保存,例如用户的密码以MD5 方法加盐加密,保存到数据库中[7]。当用户登录时,输入的密码也必须通过同样方法加密后,与数据库中的加密密码比较,客户端无论输入什么参数值,即使含有or '1=1',都必须加密形成一个值进行比较,起不到永真条件作用,登录失败。
(6)使用框架,WEB 应用程序开发时,可以使用各大公司提供的开发框架,这不仅有利于程序的开发,也会有效地防止大多SQL 注入攻击,例如:安全框架security、shiro 以及ORM 框架Hibernate、Hibernate 等。
(7)专业工具检查,利用WEB 应用程序漏洞扫描工具,如Acunetix、jsky 等工具软件。对系统进行全面的扫描检查,发现系统中存在的SQL 注入漏洞,及时进行防范处理。
(8)安全性验收,系统竣工验收时,除了进行功能和性能的验收外,还要详细地对系统的安全性进行验收,听取专家的意见,对不安全的隐患进行改进,对可能产生SQL 注入之处进行检查,使系统更安全。
4 结束语
SQL 注入攻击具有隐蔽性、欺骗性,不易被察觉,注入方法也在不断翻新,直接威胁着WEB 应用程序的安全。所以在WEB 应用程序开发时,SQL 注入攻击的防御也应重点考虑,编程人员要提高代码的安全性意识,编写代码要严谨,采用完备的防范攻击手段,对漏洞进行全面检测,不给攻击者留下SQL 注入的漏洞,确保系统安全运行。