开发同事反馈有系统收到SQL注入的风险告警,整理一下SQL注入攻击常见方式及预防方法。

SQL注入攻击是输入参数未经过滤,直接拼接到SQL语句当中解析,执行达到开发者预想之外行为的攻击方式。

下面是网上找到的例子

 

一、 如何进行SQL注入攻击

以php编程语言、mysql数据库为例,介绍一下SQL注入攻击的构造技巧、构造方法

1. 数字注入

在浏览器地址栏输入:learn.me/sql/article.php?id=1,正常情况下,应该返回一个id=1的文章信息。这是一个get型接口,发送这个请求相当于调用一个查询语句:

$sql = "SELECT * FROM article WHERE id =",$id

但是,如果在浏览器地址栏输入:learn.me/sql/article.php?id=-1 OR 1 =1,这就是一个SQL注入攻击,会返回文章的所有信息。因为1=1永远是true,所以where条件相当于没有,查询的结果相当于整张表的内容。

 

2. 字符串注入

有这样一个用户登录场景:登录界面包括用户名和密码输入框,以及登录按钮。

SQL注入攻击常见方式及预防方法_SQL

这是一个post请求,登录时调用接口learn.me/sql/login.html,首先连接数据库,然后后台对post请求参数中携带的用户名、密码进行参数校验,即sql的查询过程。

假设正确的用户名和密码为user和pwd123,输入正确的用户名和密码、提交,相当于调用了以下的SQL语句:

SELECT * FROM user WHERE username = 'user' ADN password = 'pwd123'

由于用户名和密码都是字符串,SQL注入方法即把参数携带的数据变成mysql中注释的字符串。

mysql中有2种注释的方法:

  • #:#后所有的字符串都会被当成注释来处理

用户名输入:user'#(单引号用于闭合user左边的单引号),密码随意输入,如:111,然后提交。等价于SQL语句:

SELECT * FROM user WHERE username = 'user'#'ADN password = '111'

由于后面都被注释掉了,相当于SQL没有了密码验证:

SELECT * FROM user WHERE username = 'user'
  • -- (--后面有个空格):-- 后面的字符串都会被当成注释来处理

用户名输入:user'-- (注意--后面有个空格,单引号用于闭合user左边的单引号),密码随意输入,如:111,然后提交。等价于SQL语句:

SELECT * FROM user WHERE username = 'user'-- 'AND password = '111'

因此,以上两种情况可能输入一个错误的密码或者不输入密码就可登录用户名为'user'的账号,这是十分危险的事情。

 

二、 预防方法

  • 严格区分普通用户与管理员用户的权限。如果页面查询用户使用的是root,注入时被带入了drop table,drop database等语句,后果将不堪设想
  • 采用参数化语句。使用参数而不是将用户输入变量嵌入到SQL语句中,可以杜绝大部分的SQL注入式攻击。
  • 加强对用户输入的验证。测试用户输入内容的大小和数据类型,强制执行适当的限制与转换。有助于防止缓冲区溢出攻击和注入式攻击
  • 使用存储过程。这个要根据业务实际而定
  • 多使用SQL Server自带的安全参数,SQL Server数据库中提供了Parameters集合,这个集合提供了类型检查和长度验证的功能。
  • 实现多层验证
  • 必要的情况下使用专业的漏洞扫描工具来寻找可能被攻击的点
  • 设置陷阱账号。一个是普通管理员帐号,一个是防注入的帐号。将防注入的账号设置的很象管理员,如 admin,以制造假象吸引软件的检测,而密码是大于千字以上的中文字符,迫使软件分析账号的时候进入全负荷状态甚至资源耗尽而死机。

 

三、 为什么预编译能防止SQL注入

例如java就有PreprareStatement进行预编译。

在程序运行时第一次操作数据库之前,SQL已经被数据库编译和解析,对应的执行计划也会缓存下来并以参数化的形式进行查询。当把参数传给PreprareStatement时,即使参数里有敏感字符如 or '1=1’,数据库也只会将它作为一个参数值来处理而不会作为一个SQL指令,如此,就起到防止SQL注入的作用了。

例如之前的语句

SELECT * FROM article WHERE id = -1 OR 1=1

预编译会将输入整体作一个参数,而不是SQL的一部分,即解释为

SELECT * FROM article WHERE id = '-1 OR 1=1'

如果限定了id是数字类型,该sql执行会报错;如果是字符串类型,应该也查询不出结果