开发反模式 - SQL注入
一、目标:编写SQL动态查询
SQL常常和程序代码一起使用。我们通常所说的SQL动态查询,是指将程序中的变量和基本SQL语句拼接成一个完整的查询语句。
string sql = SELECT * FROM Person WHERE Id = $Id
我们期望$Id是一个整型,因此当数据库接收到这个请求时,$Id的值就是查询语句的一部分。
SQL动态查询是有效利用数据库很自然的方法。当你使用程序内的变量来指定如何进行查询时,就是将SQL作为连接程序和数据库的桥梁。程序和数据库之间通过这种方式进行“对话”。
然而,要让程序按照你想要的方式执行并不难,难的是让程序变得安全,不执行你不想让它执行的操作。但软件在收到SQL注入攻击时,通常都无法保证安全。
二、反模式:将未经验证的输入作为代码执行
当往SQL查询的字符串中插入别的内容,而这些被插入的内容以你不希望的方式修改了查询语句的语法时,SQL注入就成功了。如,对于上面所写的SQL语句,$Id的值变为"123; DELETE FROM Person"。那么最终的查询语句会变成这样:
SELECT * ROM Person WHERE Id = 123; DELETE FORM Person
如果你的程序真的执行了这样一行SQL语句,那么悲剧了,Person表里的数据就完全被清空了。
1、对Web安全的严重威胁
当攻击者能够使用SQL注入操控你的SQL查询时,它就变成了一个巨大的威胁。假设你的数据库中修改密码的SQL语句是这样写的:
UPDATE Account SET Password = SHA2('$password') WHERE AccountId = 123;
那么聪明的攻击者会猜测你请求参数对应在SQL语句中的作用,并且精心选择每个参数对应的值:
http://www.xxx.com/setpassword?password=123456&userid=123 OR TRUE
如果你的程序真的被攻击者所绕过了,那么你的数据库将会执行如下SQL语句:
UPDATE Account SET Password = 123456 WHERE AccountId = 123 OR TRUE
Account表中,所有用户的密码都被改成了123456。
有数不尽的方法选择一个恶意的字符串来改变SQL语句的行为。它只受制于攻击者的想象力和你保护SQL语句的能力。
2、寻找治愈良方
有很多文章,声称某一种技术室对抗SQL注入的万能药。而事实上,这些技术都被证明无法阻挡所有类型的SQL注入。因此你需要在不同的情况下,将所有这些技术组合起来使用。
(1).转义
防止SQL语句包含任何不匹配的引号是最古老的方法,就是对所有的引号字符进行转义操作,使它们不至于成为字符串的结束符。在标准SQL语句中,可以使用两个连续的单引号来表示一个单引号字符:
SELECT * FROM Person WHERE Name = 'O''Hare'
大多数数据库还支持使用反斜杠对单引号进行转义操作:
SELECT * FROM Person WHERE Name = 'O\'Hare'
这么做的原理是,将应用程序中的数据插入到SQL语句之前就进行转换,大多数SQL的编程接口都会提供一个简便的函数来做这个操作。
这样做了之后,所有的字符串都会被包上引号,如果对上面修改账号的SQL语句这样操作的话,SQL语句会变成这样:
UPDATE Account SET Password = SHA2('')
WHERE AccountId = '123 OR TRUE'
在SQL中,是没有办法让一个数值列直接和一个带有数字的字符串进行比较的,不管是哪种数据库,这都不可以。在标准SQL中,将字符串转换为数字时,必须明确使用CASE()函数,因此上面的SQL语句只是报个错误,还不至于全部用户账号的密码都被修改了。
(2).查询参数
一个经常被认为是防止SQL语句注入的万能解决方案是使用"参数化查询",不同于在SQL语句中插入动态内容,查询参数的做法是在准备查询语句的时候,在对应参数的地方使用参数占位符。随后,在执行这个预先准备好的查询时提供一个参数。
cmd.CommandText = "Update Person Set Name = 'Ado.net' WHERE Id = @Id"; //设置操作语句
cmd.Parameters.Add("@Id", SqlDbType.Int); //添加参数
cmd.Parameters["@Id"].Value = ; //设置参数值
大多数开发人员都推荐这个方案,因为你不需要对动态内容进行转义,或者担心有缺陷的转义函数。
事实上,查询参数这个方法的确是对付SQL注入一个强劲有效的解决方案。但这并不是一个通用的方案,因为查询参数总是被视为一个字面值。
多个值的列表不可以当成单一参数:
cmd.CommandText = "Update Person Set Name = 'Ado.net' WHERE Id IN (@Id)"; //设置操作语句
cmd.Parameters.Add("@Id", SqlDbType.String); //添加参数
cmd.Parameters["@Id"].Value = "1,2,3"; //设置参数值
这个做法会导致数据库认为传入的是一个包含数字和逗号的字符串,处理过程将和一系列整数作为参数进行查询并不一样。
真正生成到数据库中执行的SQL语句为:
Update Person Set Name = 'Ado.net' WHERE Id IN ('1,2,3')
表名无法作为参数:
cmd.CommandText = "SELECT * FROM @Table; //设置操作语句
cmd.Parameters.Add("@Table", SqlDbType.String); //添加参数
cmd.Parameters["@Table"].Value = "Person"; //设置参数值
这么做是想将一个字符串插入表名所在的位置,但只会得到一个语法错误的提示。
真正生成到数据库执行的SQL语句如下:
SELECT * FROM 'Person'
列名无法作为参数:
cmd.CommandText = "SELECT * FROM Person ORDER BY @Column; //设置操作语句
cmd.Parameters.Add("@Column", SqlDbType.String); //添加参数
cmd.Parameters["@Column"].Value = "Id"; //设置参数值
真正生成到数据库执行的SQL语句如下:
SELECT * FROM Person ORDER BY 'Id'
SQL关键字不能作为参数:
cmd.CommandText = "SELECT * FROM Person ORDER BY Id @Sort; //设置操作语句
cmd.Parameters.Add("@Sort", SqlDbType.String); //添加参数
cmd.Parameters["@Sort"].Value = "DESC"; //设置参数值
参数将被当做字面字符串插入而非SQL关键字。在这个例子中,会返回语法错误的提示:
SELECT * FROM Person ORDER BY Id 'DESC'
(3).存储过程
存储过程,是很多程序员生成可以抵御SQL注入攻击的方法。通常来说,存储过程包含固定的SQL语句,这些语句是在定义这个存储过程的时候被解析的。
然而,存储过程也是使用SQL动态查询的,无法绝对保证完全杜绝SQL注入。不过存储过程的确是有强大的防止SQL注入的作用。不过,如果你依然是在存储过程中拼接SQL语句,存储过程也一样能够注入。
假设你的存储过程如下(这在比较复杂一些的操作时经常出现的):
CREATE PROC SelectAccount
@name varchar(50),
@password varchar(50)
AS
DECLARE @sql varchar(1000);
SET @sql = 'SELECT * FROM Account WHERE Name = ''' + @name + ''' AND Password = ''' + @password + ''''
EXEC (@sql)
如果在存储过程当中拼接SQL语句,那么注入方式如下:
--正常登录
EXECUTE SelectAccount 'admin',''
--SQL注入,无需账号行数不返回0
EXECUTE SelectAccount 'a'' or 1=1 --','';
返回结果:
事实上几乎所有的数据库应用程序都动态地构建SQL语句。如果你使用拼接字符串的形式或者将变量插入到字符串中的方法来构建哪怕一句SQL语句,那这一句查询语句就会让应用程序暴露在SQL注入攻击的威胁之下。
三、解决方案:不相信任何人
没有哪一种技术能使SQL代码变得安全,你应该学习下面所描述的所有技术,并在合理的地方使用它们。
1、过滤输入内容
你应该将所有不合法的字符从用户输入中剔除掉,而不是纠结于是否有些输入包含了有危险的内容。也就是说,如果你需要一个整数,那就只使用输入中的整数部分。根据你所使用的开发语言不同,方法也不尽相同。
2、参数化动态内容
如果查询中的变化部分是一些简单的类型,你应该使用查询参数将其和SQL表达式分离。
参数化动态内容之后,一个参数只能被替换成一个值。如果你是在RDMBS解析完SQL语句之后才插入这个参数值,没有那种SQL注入的攻击能够改变一个参数化了的查询的语法结构。即使攻击者尝试使用带有恶意的参数值,注入123 OR TRUE,RDBMS会将这个字符串当成一个完整的值插入。最坏的情况下,这个查询没办法返回任何记录,它不会返回错误的行。
3、给动态输入的值加引号
查询参数通常来说是最好的解决方案,但在有些很特殊的情况下,参数的占位符会导致查询优化器无法正确选择使用哪个索引来进行优化。要规避参数查询对索引的影响,直接将变量内容插入到SQL语句中会是更好的方法,不要去理会查询参数。一旦你决定这么做了,就一定要小心地引用字符串。你需要确信你插入的字符串是经过严格测试、不会带有安全隐患的。
4、将用户与代码隔离
查询参数和转移字符能帮助你将字符串类型的值插入到SQL表达式中,但这些技术在需要插入表/列名或者SQL关键字的时候不起作用。你需要另一项技术来使得这些部分也能动态化。
对应的解决方案是,将请求参数作为索引值去查找预先定义好的值,然后用这些预先定义好的值来组织SQL查询语句。
(1)、预定义值
如在一个数据字典中存储如下值:
up => "ASC" ,down => "DESC"
(2)、定义默认值
定义一个默认值,当用户选择的值不在数据字典中时,使用默认值。
比如当用户输入的值不是up也不为down,那么就使用ASC。这样的字符串就是安全的了。
下面给出一个简单的SQL注入过滤函数:
public string NoSqlHack(string Inner)
{
if (!string.IsNullOrEmpty(Inner))
{
//特殊的字符
Inner = Inner.Replace("<", "");
Inner = Inner.Replace(">", "");
Inner = Inner.Replace("*", "");
Inner = Inner.Replace("-", "");
Inner = Inner.Replace("?", "");
Inner = Inner.Replace("'", "''");
Inner = Inner.Replace(",", "");
Inner = Inner.Replace("/", "");
Inner = Inner.Replace(";", "");
Inner = Inner.Replace("*/", "");
Inner = Inner.Replace("\r\n", "");
Inner = Inner.Replace(" ", ""); return Inner;
}
else
{
return string.Empty;
}
}
另外,后端一定要验证,以防止用户POST请求。
SQL注入绕过某些字符过滤:
1,避免使用被阻止的字符,即不使用这些字符仍然达到攻击目的。
A,如果注入一个数字数据字段,就不需要使用单引号。
B,输入注释符号被阻止使用,我们可以设计注入的数据,既不破坏周围的查询语法。
比如, http://www.xxx.net/article.asp?id=1' 这里存在注入,过滤了注释符合,我们可以输入 http://www.xxx.net/article.asp?id=1' or 'a'='a
目的其实很简单,就是把后面的单引号给闭合掉。
C,在一个MSSQL注入中注入批量查询的时候,不必使用分号分隔符。
只要纠正所有批量查询的语法,无论你是否使用分号,查询的解析器依然能正确的去解释它们的。
2,避免使用简单确认
一些输入确认机制使用一个简单的黑名单,组织或删除任何出现在这个名单中的数据,比如防注入程序。
这一般要看这个机制是否做的足够的好了,黑名单是否足够能确保安全。如果只是简单的黑名单,那也有机会突破的。
A,如果select关键词被阻止或删除
我们可以输入:
SeLeCt 注意大小写
selselectect 还记得ewebeditor是怎么过滤asp的么?
%53%45%4c%45%43%54 URL编码
%2553%2545%254c%2545%2543%2554 对上面的每个%后加了一个25
3,使用SQL注释符
A,使用注释来冒充注入的数据中的空格。
select/*alocne*/username,password/*alocne*/from/*alocne*/admin
/*alocne*/来冒充空格
B,使用注释来避开某些注入的确认过滤。
SEL/*alocne*/ECT username,password fr/*alocne*/om admin
4,处理被阻止的字符串
比如,程序阻止了admin,因为怕攻击者注入admin表单中的数据。
我们可以这样
A,oracle数据库: 'adm'||'in'
B,MSSQL数据库: 'adm'+'in'
C,MYSQL数据库: concat ('adm','in')
D,oracle中如果单引号被阻止了,还可以用chr函数
sleect password from admin where username = char(97) || chr(100) || chr(109) || chr(105) || chr(110)
开发反模式 - SQL注入的更多相关文章
- 开发反模式(GUID) - 伪键洁癖
一.目标:整理数据 有的人有强迫症,他们会为一系列数据的断档而抓狂. 一方面,Id为3这一行确实发生过一些事情,为什么这个查询不返回Id为3的这一行?这条记录数据丢失了吗?那个Column到底是什么? ...
- Java应用开发中的SQL注入攻击
1. 什么是SQL注入攻击? SQL注入攻击是黑客对数据库进行攻击的常用手段之一.随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员越来越多.但是由于程序员的水平及经验参差不齐,相当一部分 ...
- php web开发安全之sql注入和防范:(一)简单的select语句注入和防范
sql注入主要是指通过在get.post请求参数中构造sql语句,以修改程序运行时所执行的sql语句,从而实现获取.修改信息甚至是删除数据的目的,sql被注入的原因主要是代码编写的有问题(有漏洞),只 ...
- web开发中防止SQL注入
一.SQL注入简介 SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库. 二.SQL注入攻击的总体 ...
- SQL反模式学习笔记1 开篇
什么是“反模式” 反模式是一种试图解决问题的方法,但通常会同时引发别的问题. 反模式分类 (1)逻辑数据库设计反模式 在开始编码之前,需要决定数据库中存储什么信息以及最佳的数据组织方式和内在关联方式. ...
- 高级sql注入
1. 避开输入过滤 输入过滤存在于外部和内部,外部属于web应用防火墙WAF,入侵防御系统IPS,入侵检测系统IDS,内部属于代码中对输入进行过滤 过滤select,insert等sql关键字和' | ...
- SQL反模式学习笔记21 SQL注入
目标:编写SQL动态查询,防止SQL注入 通常所说的“SQL动态查询”是指将程序中的变量和基本SQL语句拼接成一个完整的查询语句. 反模式:将未经验证的输入作为代码执行 当向SQL查询的字符串中插入别 ...
- SQL反模式学习笔记5 外键约束【不用钥匙的入口】
目标:简化数据库架构 一些开发人员不推荐使用引用完整性约束,可能不使用外键的原因有一下几点: 1.数据更新有可能和约束冲突: 2.当前的数据库设计如此灵活,以至于不支持引用完整性约束: 3.数据库为外 ...
- SQL反模式学习笔记19 使用*号,隐式的列
目标:减少输入 反模式:捷径会让你迷失方向 使用通配符和未命名的列能够达到减少输入的目的,但是这个习惯会带来一些危害. 1.破坏代码重构:增加一列后,使用隐式的Insert插入语句报错: 2.查询中使 ...
随机推荐
- 通过代理访问nginx和直接访问nginx区别
80.82.78.38 [23/Sep/2016:05:36:18 +0800] "GET http://www.baidu.com/cache/global/img/gs.gif HTTP ...
- AS3排序
package { import flash.display.Sprite; public class Sort extends Sprite { private var arr:Vector.< ...
- 黑马程序员_Java面向对象_包
7.包 7.1包(package) 对类文件进行分类管理. 给类提供多层命名空间. 写在程序文件的第一行. 类名的全称是:包名.类名. 包也是一种封装形式. 利用命令行自动生成文件夹格式:D:\jav ...
- cf437C The Child and Toy
C. The Child and Toy time limit per test 1 second memory limit per test 256 megabytes input standard ...
- DOS命令大全--具体解释
在Linux和Windows下都能够用nslookup命令来查询域名的解析结果 DOS命令大全一)MD--建立子文件夹 1.功能:创建新的子文件夹 2.类型:内部命令 3.格式:MD[盘符:][路径名 ...
- 图像重采样(CPU和GPU)
1 前言 之前在写影像融合算法的时候,免不了要实现将多光谱影像重采样到全色大小.当时为了不影响融合算法整体开发进度,其中重采样功能用的是GDAL开源库中的Warp接口实现的. 后来发现GDAL War ...
- UIActivityIndicatorView-初识IOS
UIActivityIndicatorView是一个加载动画的视图,一般加载一个网页页面之前会经常用到. 上一个随笔,我讲到了页面加载的页面的那些代理方法 - (void) viewWillAppea ...
- Fedora24安装常用软件方法
# 添加chrome源 cd /etc/yum.repos.d/ # 下载google-chrome.repo并保存# wget http://repo.fdzh.org/chrome/google ...
- vim的用法
1. vi 与 vim 有什么区别呢,它们之间有什么关系?Vim是从Vi发展出来的一个文本编辑器,可以看作是vi的升级版.Vim的主要功能与原始的Vi完全兼容,vi不会显示颜色,而vim会根据文件内容 ...
- 转载--DEV GridControl 的一些基本操作
1. 如何解决单击记录整行选中的问题 View->OptionsBehavior->EditorShowMode 设置为:Click 2. 如何新增一条记录 (1).gridView.Ad ...