SQL注入
1.SQL注入的原理
SQL注入的方式包括直接将代码插入到与SQL命令串联在一起并使其得以执行的用户输入变量。一种间接的攻击会将恶意代码注入要在表中存储的字符串。在存储字符串随后串联到一个动态的SQL命令中时,将执行该恶意代码。
注入过程的工作方式是提前终止文本字符串,然后追加一个新的命令。由于插入的命令可能在执行前追加其他字符,因此攻击者将用注释标记“–”来终止注入的字符串。执行时,此后的文本将被忽略。
举个例子
下面代码提示用户输入一个市县的名字。
var ShipCity;
ShipCity = Request.form ("ShipCity");
var sql = "select * from OrdersTable where ShipCity = '" + ShipCity + "'";
如果输入Redmond,则查询的SQL语句为
SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond'
但是,假设用户输入的内容为
Redmond'; drop table OrdersTable--
此时,SQL语句将被拼接成以下内容
SELECT * FROM OrdersTable WHERE ShipCity = 'Redmond'; drop table OrdersTable-- '
分号(;)表示一个查询的结束和另一个查询的开始。双连字符(–)指示当前行余下的部分是一个注释,应该被忽略。如果修改后的代码语法正确、则服务器将执行该代码。数据库处理该语句时,会将字段ShipCity值为Redmond的所有记录返回,然后删除OrdersTable。
只要输入的SQL代码语法正确,就无法采用编程方式来检测篡改。所以,必须验证所有用户输入,并仔细检测构造SQL命令的代码。
2.SQL注入预防
2.1验证所有输入
- 测试输入的大小和数据类型,强制执行适当的限制。 这有助于防止有意造成的缓冲区溢出。
- 测试字符串变量的内容,只接受所需的值。 拒绝包含二进制数据、转义序列和注释字符的输入内容。 这有助于防止脚本注入,防止某些缓冲区溢出攻击。
- 使用 XML 文档时,根据数据的架构对输入的所有数据进行验证。
- 绝不直接使用用户输入内容来生成 Transact-SQL 语句。
- 使用存储过程来验证用户输入。
- 在多层环境中,所有数据都应该在验证之后才允许进入可信区域。 未通过验证过程的数据应被拒绝,并向前一层返回一个错误。
- 实现多层验证。 对无目的的恶意用户采取的预防措施对坚定的攻击者可能无效。 更好的做法是在用户界面和所有跨信任边界的后续点上验证输入。
例如,在客户端应用程序中验证数据可以防止简单的脚本注入。 但是,如果下一层认为其输入已通过验证,则任何可以绕过客户端的恶意用户就可以不受限制地访问系统。 - 绝不串联未验证的用户输入。 字符串串联是脚本注入的主要输入点。
- 不接受以下来自可构造文件名的字段中的字符串:AUX、CLOCK、COM1 到 COM8、CON、CONFIG、LPT1 到 LPT8、NUL 和 PRN。
如果可能,拒绝包含以下字符的输入。
输入字符 | 在 Transact-SQL 中的含义 |
---|---|
; | 查询分隔符。 |
“ | 字符数据字符串分隔符。 |
– | 单行注释分隔符。 服务器不计算在 – 之后直到该行结束的文本。 |
/* … */ | 注释分隔符。 服务器不计位于 /* 和 */ 之间的文本。 |
xp_ | 用于目录扩展存储过程的名称的开头,如 xp_cmdshell 。 |
2.2ORM
一种将内存中的对象保存到关系数据库中的技术,负责实体域对象的持久化,封装数据库访问细节。
ORM提供了实现持久化层的另一种模式,采用映射元数据(XML)来描述对象-关系的映射细节,使得ORM中间件能在任何以及Java应用的业务逻辑层和数据库之间充当桥梁。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中,我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。
当你开发一个应用程序的时候(不使用O/R Mapping),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。你在DAL中写了很多的方法来读取对象数据,改变状态对象等等任务。而这些代码写起来总是重复的。ORM解决的主要问题是对象关系的映射。域模型和关系模型分别是建立在概念模型的基础上的。域模型是面向对象的,而关系模型是面向关系的。一般情况下,一个持久化类和一个表对应,类的每个实例对应表中的一条记录,类的每个属性对应表的每个字段。将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。因此ORM的目的是为了方便开发人员以面向对象的思想来实现对数据库的操作。
面向对象概念 面向关系概念 类 表 对象 表的行(记录) 属性 表的列(字段)
2.3MyBatis
什么是MyBatis
Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。
参数占位符${}和#{}的区别
${}
用于传递参数值时,它直接将参数值替换到SQL语句中。这意味着如果参数值中包含SQL代码片段,它可能会被当作代码执行,从而导致SQL注入攻击。因此,应该避免使用${}
进行用户输入的处理。#{}
在传递参数值时,MyBatis会对参数值进行预编译和转义,以避免SQL注入攻击。因此,使用#{}
是更安全的选择。
为了避免SQL注入风险,应该始终使用#{}
进行参数值的传递。同时,应该对用户输入进行适当的验证和清理,以减少潜在的注入攻击风险。另外,使用ORM框架(如MyBatis)提供的参数绑定功能也是一种有效的防止SQL注入的方法。
预编译对于Mybatis
而言,相当于构建出了一条SQL
的模板,将#{}
对应的参数改为?
而已。届时只需要更改参数的值即可,无需再对SQL
进行语法解析等操作。