AppScan -- web安全问题
首先简单描述一下系统情况:采用的技术struts1.2,EJB3.0,另外公司以前封装了一套jsp标签;应用服务器jboss4.0.2;数据库为SQL Server2005。
目前我还有2类问题还没有解决—1:会话标识未更新
2:发现数据库错误模式
希望有解决过此类问题的大虾们,能给予一点帮助!
再次先行谢过!!
系统将近验收的时候,对方发过来一份安全报告- -我们开发的系统有安全漏洞,让我们把漏洞级别为严重的全部解决,否者不予验收。对于这些问题,公司其它项目组包括我都没有人曾经做过(也就是说系统从开始打框架到后来的开发过程没有任何人注意这些问题)。此时脑海浮现一句较为经典的网语“内事问baidu,外事问google…”。经过几天的搜索,简单描述一下我对以下几个安全问题的理解:
1发现数据库错误模式:主要是一些数据连接错误信息,通过提交特殊构造的字符,程序会暴露一些数据库信息,也容易引起SQL注入攻击。
2会话标识未更新:是cookies过期时间未设置,可能会引发一些cookies欺骗攻击。
3跨站点请求伪造:这个是高级攻击技术,是黑客作为中间人攻击,这个漏洞是程序造成的,在不同的会话中两次发送同一请求并且收到相同的响应,也就是说客户端与服务端建立连接后,客户端向服务端提交数据,服务端只认客户端需要什么而返回什么内容给客户端,并没有对客户端的身份进行认证。
4不充分帐户封锁:程序没有使用锁定功能,可以穷举密码。
5跨站点脚本编制:实际翻译应该是跨站脚本攻击。也就是XSS,是服务端没有对客户端提交的参数进行过滤,例如:2009>%22%27><img%20src%3d%22javascript:alert(473443)%
22>,这个就会弹出一个窗口,还可以弹出其他页面,或者做页面跳转等攻击,通常攻击者用来挂马。
6 SQL 盲注也是要对参数进行过滤,包括危险字符,‘,;@,==,而且通过cookies提交的值也要进行过滤。
以下内容是我解决这些问题的过程
首先找了个IBM Rational AppScan 7.8的破解版装上
(一)既然http访问有问题安全,那么我换个安全点的https试了试,结果发现严重级别的没有减少,反而严重程度为一般的倒是增加了好几类,晕!
(jboss4.0.2配置https服务的步骤在压缩文件里面)
(二)对于sql盲注有一下2种情况
1:http://172.16.200.219:9090/project/projectmemberaction.do (2)
code
name
2:http://172.16.200.219:9090/system/orgbaseinfoaction .do (3)
TcnSystemOrgDto.code
TcnSystemOrgDto.fullname
TcnSystemOrgDto.addrss
[(2)(3)分别表示该链接下面漏洞注入的个数]
我针对安全扫描的提示,逐个修改了对数据库列表及查询的操作,使其全部采用prepareStatement预编译的方式。因为系统涉及到的页面太多,对于用户输入框的过滤就不可能一个一个的去修改,因此我写了个filter
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <filter>
<filter-name>SQLFilter</filter-name>
<filter-class>com.ebuilds.aas.SqlInjection</filter-class>
<init-param>
<param-name>sqlkey</param-name>
<param-value> '|exec|execute|insert|create|drop|table|having|user|%20|union|where|select|delete|update|count|*|document.cookie|xp_cmdshell|'|chr|mid|master|truncate|char|declare|or |;|--|,|%20and%20|%20or%20|//|/|%|#|script|<img|%3cimg </param-value>
</init-param>
</filter>
*
* <filter-mapping>
<filter-name>SQLFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SQLFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
*/
public class SqlInjection implements Filter {
protected FilterConfig filterConfig;
protected String encodingName;
protected String sqlkey = null;
public void init(FilterConfig filterConfig) throws ServletException{
this.filterConfig = filterConfig;
this.sqlkey = filterConfig.getInitParameter( "sqlkey" );}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httprequest = (HttpServletRequest) request;
HttpServletResponse httpresponse = (HttpServletResponse) response;
//用于的使 Browser 不缓存页面的过滤器
httpresponse.setHeader("Cache-Control","no-cache");
httpresponse.setHeader("Pragma","no-cache");
httpresponse.setDateHeader ("Expires", -1);
List<String> list=new ArrayList<String>();
//String responseURI = "";
//int i = 0;
//StringBuffer sb = new StringBuffer("");
java.util.Enumeration enu = httprequest.getParameterNames();
String strurl = httprequest.getRequestURI();
//System.out.println("strurl:"+strurl);
String strenu = "";
while (enu.hasMoreElements()) {
strenu = (String) enu.nextElement();
if(!"xml.req.fields".equals(strenu) && !"xml.req.charset".equals(strenu)&& !"xml.req.parentid".equals(strenu)&& !"xml.req.querystr".equals(strenu)&& !"xml.req.ids".equals(strenu)){
//if (i == 0)
//sb.append(strenu + "=");
//else
//sb.append("&" + strenu + "=");
//sb.append(new String(httprequest.getParameter(strenu).getBytes("iso-8859-1"), "UTF-8"));
//i++;
String val=new String(httprequest.getParameter(strenu).getBytes("iso-8859-1"), "UTF-8").toLowerCase();
if(!isNum(val)){//过滤掉是数字形式的参数
list.add(val);
}
}
}
//responseURI = sb.toString().toLowerCase();
if (null!=strurl && !"".equals(strurl) && null!=list && list.size()>0) {
boolean blag = false;
java.util.StringTokenizer stk = new java.util.StringTokenizer(sqlkey, "//|");
String temp = "";
while (stk.hasMoreTokens()) {
temp = stk.nextToken();
if(null!=temp && !"".equals(temp)){
for(int s=0;s<list.size();s++){
String para=list.get(s).toString();
if(para.indexOf(temp) != -1){
blag = true;
break;
}
}
}
}
if (blag) {
httpresponse.sendRedirect("/Sqlinjection.jsp");
} else {
chain.doFilter(request, response);
}
} else {
chain.doFilter(request, response);
}
}
public void destroy() {
this.sqlkey = null;
this.filterConfig = null;
}
//是数字返回true
private static boolean isNum(String str){
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(str);
if( !isNum.matches()){
return false;
}
return true;
}
}
做完这些之后,再继续扫描,发现第1种注入问题已经解决,然而第2种问题依旧,而且每次扫描的链接还不一样,正在苦恼之际看到CSDN上有个如下的帖子
彻底杜绝SQL注入
1.不要使用sa用户连接数据库
2、新建一个public权限数据库用户,并用这个用户访问数据库
3、[角色]去掉角色public对sysobjects与syscolumns对象的select访问权限
4、[用户]用户名称-> 右键-属性-权限-在sysobjects与syscolumns上面打“×”
其原文见如下路径
http://topic.csdn.net/u/20091221/18/6693545C-A659-4E3B-9454-EBB77D6C8D1F.html
至此,SQl盲注问题就已经解决。(前面的那个过滤器是否多余?这个问题还没来得及验证)
(三)对于XSS问题,网上给的意见同样是过滤用户的输入。系统都在验收的阶段,哪有时间对输入框去做限制呢!同样写个filter
代码如下:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class XSSFilter implements Filter {
private FilterConfig config;
private static boolean no_init = true;
public XSSFilter() {
config = null;
}
public void init(FilterConfig filterconfig) throws ServletException {
config = filterconfig;
no_init = false;
}
public void destroy() {
config = null;
}
public FilterConfig getFilterConfig() {
return config;
}
public void setFilterConfig(FilterConfig filterconfig) {
if (no_init) {
no_init = false;
config = filterconfig;
}
}
public void doFilter(ServletRequest servletrequest, ServletResponse servletresponse, FilterChain filterchain) throws IOException, ServletException {
filterchain.doFilter(new RequestWrapper((HttpServletRequest)servletrequest), servletresponse);
}
}
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public final class RequestWrapper extends HttpServletRequestWrapper {
public RequestWrapper(HttpServletRequest httpservletrequest) {
super(httpservletrequest);
}
public String[] getParameterValues(String s) {
String str[] = super.getParameterValues(s);
if (str == null) {
return null;
}
int i = str.length;
String as1[] = new String[i];
for (int j = 0; j < i; j++) {
as1[j] = cleanXSS(str[j]);
}
return as1;
}
public String getParameter(String s) {
String s1 = super.getParameter(s);
if (s1 == null) {
return null;
} else {
return cleanXSS(s1);
}
}
public String getHeader(String s) {
String s1 = super.getHeader(s);
if (s1 == null) {
return null;
} else {
return cleanXSS(s1);
}
}
private String cleanXSS(String s) {
s = s.replaceAll("<", "<").replaceAll(">", ">");
s = s.replaceAll("\\(", "(").replaceAll("\\)", ")");
s = s.replaceAll("'", "'");
s = s.replaceAll("eval\\((.*)\\)", "");
s = s.replaceAll("[\\\"\\'][\\s]*javascript:(.*)[\\\"\\']", "\"\"");
s = s.replaceAll("script", "");
return s;
}
}
Web.xml中的配置参见上面SQLFilter的方式
(四)对于不充分的账户封锁
我在登录页面加了一个验证码,在扫描的时候,发现appscan无法扫描,原来在这个工具有个地方设置为自动登录,我加上验证码后,Appscan无法流畅的运行,于是固定了登录action中关于验证的参数。这个问题就没有出现,奇怪???我屏蔽了验证参数的比较,因而页面验证码输入框只是一个摆设,这个问题怎么就没有了呢??
至此,我遇到的安全问题还有2类没有解决
一个是会话标识未更新,这个问题出现的地方是在登录的链接
我试过在登录的login.jsp页面加上session="false"
<%@ page contentType="text/html; charset=GBK" language="java" session="false"%>
在系统登录过程中也没有操作cookie,而且系统其他的地方也没有操作cookie,在登录的时候我设置了cookies[i].setMaxAge(1800);也没有任何作用。
另外一个未解决的安全问题是发现数据库错误模式,这个问题出现在另外一个系统,该系统采用Struts2+ Hibernate3开发
[解决办法]
帮你顶顶!这几年编程就没注意过安全。
会话标识未更新解决方法:
这个退出你要把cookie删除掉,或是置换掉。时间过期对黑客来说太easy人为制造了。
SessionID 存储在浏览器未到期会话的 Cookie 中。NET有这方面的配置。但是JAVA 就不好整了。你可以
将会话标识符不作为Cookie发送,而是直接编码到URL里面,这时候进行随机函数加密。服务端进行随机函数解密。
实在不行你把容器生成SESSION ID的策略整的复杂一点。
发现数据库错误模式
我没看懂,是显示数据库错误信息?插入非法字符的时候?那把struts部署一下全局异常把sql异常屏蔽掉或是重定向应该能行吧!
[解决办法]
会话标识未更新问题:
问题的理解:按我的理解是合法用户登录了服务器后,非法用户可以伪合法用户的cookies冒充合法用户访问服务器
首先.看合法用户访问服务器的交互过程下:
1.用户user1登录了服务器server1
2.服务器server1检查用户user1合法,生产session,并将sessionID通过cookies返回给用户user1
3.用户user1通过服务器server1返回来带用sessionID的cookies访问服务器server1的资源
4.服务器server1从user1传上来的cookies中取出sessionID,然后找到相应的session,
根据session受权返回访问资源.
其次.这个过程中用户user1的cookies很容易得到,那就很易容就得合法的sessionID,然后非法通过这个
sessionID就可以冒充合法用户user1访问系统.
解决办法:验证用户的sessionID是否合法持有
首先知道合法用户与非法用户是在不同的电脑上的,那准一可以别的是IP地址(冒充源地址是没法得到返的IP包的),
其次就是要让服务能认别sessionID是不合法用户的.
方法如下:
修改合法用户访问服务器的交互过程的第2步和第4步
第2步.将sessionID通过cookies返回给用户user1的同时,也将sessionID+IP地址的验证码一同通过cookies返回给用户
第4步.在服务器操作之前插入过滤器:取sessionID+IP地址运算与验证码比对,如果不一致,则将其挡处.
验证码的设计方案:md5(sessionID+IP地址+经常换的字符串)
解决办法的自我评价:
1.如果问题的理解正确且合法用户与非法用户的IP地址是不同,那么有效
2.如果合法用户与非法用户在同一台电脑上,那么无效
3.如果合法用户与非法用户都通过同一个NAT路由访问服务器,则它们的IP地址是相同的,那么无效