Web系统安全漏洞(JAVA)

  • Post author:
  • Post category:java




引言

最近,公司中通过AppScan和360两种工具,对系统进行漏洞扫描。由于我们做的是传统行业项目,运用的框架等技术,比较落后。所以扫描结果可想而知,“雪崩”。于是乎,我们就开始了无尽的折磨…



1.代码注入



(1)命令注入

我们的系统,是跑在WinServer上的,里面有部分代码会对操作系统的CPU、内存等进行监控,这时就用到了

System.Runtime.getRuntime().exec();

这个方法,执行对应的cmd命令。由于对入参的String,未做任何关键校验,导致出现此漏洞。



下面我举个栗子:

以下代码来自一个Web应用程序,该段代码通过运行rmanDB.bat脚本启动Oracle数据库备份,然后运行cleanup.bat脚本删除一些临时文件。脚本文件rmanDB.bat接受一个命令行参数,其中指明需要执行的备份类型。

...
String btype = request.getParameter("backuptype");
String cmd = new String("cmd.exe /K \"c:\\util\\rmanDB.bat "+btype+"&& c:\\utl\\cleanup.bat\"");
System.Runtime.getRuntime().exec(cmd);
...

该段代码没有对来自用户请求中的backuptype参数做任何校验。

通常情况下,Runtime.exec()函数不会执行多条命令,但在以上代码中,为了执行多条命令,程序调用Runtime.exec()方法,首先运行了cmd.exe指令,因此能够执行用&&分隔的多条命令了。

如果攻击者传递了一个形式为

"&& del c:\\dbms\\*.*"

的字符串,那么该段代码将会在执行其他指定命令的同时执行这条命令,对系统造成伤害。



(2)SQL注入

什么是SQL注入呢?它是一种数据库攻击手段,用户可以通过输入的参数,对原本的这个sql语句,进行更改,导致该sql执行出其它效果,对系统造成伤害。



例如:

我们在进行系统登陆的场景中,系统会执行这样一条SQL语句,


" SELECT id,username,password FROM db_user WHERE username = ' " + username + " ' AND password = ' " + pwd + " ' ";

如果将username的值写为

张三’ OR ‘1’=1

,这时这个sql语句,就变为:

SELECT id,username,password FROM db_user WHERE username=‘张三’ OR ‘1’=‘1’ AND password=’’;

这样的情况,攻击者会在不知道用户的方式,登入你的系统,进行侵入。


同样

,攻击者可以为password提供如下字符串,

’ OR ‘1’=’1


SELECT id,username,password FROM db_user WHERE username=’’ AND password=’’OR ‘1’=‘1’;

也可以达到入侵系统的目的。

而在我们的项目中,使用公司自己的框架,其中框架中与数据库的操作,大多是对JDBC操作的封装。很多地方使用

java.sql.Statement

拼接出来的sql语句都是类似上面的场景描述的那样,会引起SQL注入。而且,前人在写代码时,也未对具体的入参进行关键校验。



下面说说怎么改进:

就是,将之前的

java.sql.Statement

替换为

java.sql.PreparedStatement

,更换了API,使用后者的好处不少,我来简单罗列一下:

  1. 很好的,防止SQL注入,具体的原因还是因为有预编译的能力,在后期参入传入时通过替换占位符,将指定参数传入。(SQL注入只会在编译期存在隐患)
  2. 有预编译效果,使数据库多次执行同一个sql语句,性能更高。
  3. 使代码更加简洁(强加的优点,哈哈)。

鉴于以上优点,尽量使用“PreparedStatement” 。不过身为21世纪的程序猿,在项目中与数据库的交互,普遍都在用MyBatis、Hibernate框架,这类框架已经很好的屏蔽了这一层次的问题。但是在这些框架中依然存在SQL注入的情况,这个安全问题,值得注意。



MyBatis框架SQL注入

这个框架在我们的项目中,没有用到,我在这里作为知识点,简单总结一下。

其实主要就是通过

#{}

防止SQL注入。

可以通过DeBug得到,它通过代码执行出的sql语句是这样的:

	//数据库执行该语句之前,会进行预编译
	SELECT id,username,password FROM db_user WHERE username = ? AND password = ?;	

在数据库执行时,会动态的将 “?”这个占位符替换为具体参数,执行SQL语句。从而避免SQL注入问题。本质上还是使用了JDBC的PreparedStatement先进行了预编译,这是个关键点。

还有一种表达式是**${}**,它也可以将变量插入sql语句中,但是它未进行预编译,是非安全的,存在SQL注入的隐患。因为它在代编译期的时候,会将参数值传入SQL语句中,与上面的登陆场景效果一样。这种表达式,可以用于动态传入数据库对象,例如传入表名。



版权声明:本文为chenfei2744原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。