资源描述
安全攻击及防范手册
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
_____________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
版本 1.0
8月
1 概述
1.1 介绍
当今世界,Internet(因特网)已经成为一个很关键基础平台,很多企业全部将应用架设在该平台上,为用户提供更为方便、快捷服务支持。这些应用在功效和性能上,全部在不停完善和提升,然而在很关键安全性上,却没有得到足够重视。伴随WEB技术应用范围越来越广泛,WEB技术相关安全漏洞越来越多被挖掘出来,而针对WEB站点攻击已经成为了最流行攻击路径。
很快前项目管理部对企业内外关键系统进行了一次安全隐患分析测试,并总结出了《企业安全测试问题分类及描述》汇报文档。本文针对此汇报中提到部分重大安全隐患问题逐一分析,并给出对应处理方案。
1.2 参考资料
《Java安全性编程实例》
《网站系统安全开发手册》
《企业级Java安全性(构建安全J2EE应用)》
2 WEB安全隐患及预防方法
2.1 会话标识未更新
2.1.1 描述
登陆过程前后会话标识比较,显示它们并未更新,这表示有可能伪装用户。初步得悉会话标识值后,远程攻击者有可能得以充当已登录正当用户。
2.1.2 安全等级
高。
2.1.3 安全风险
可能会窃取或操纵用户会话和 cookie,它们可能用于模拟正当用户,从而使黑客能够以该用户身份查看或变更用户统计和实施事务。
2.1.4 处理方案
u 不要接收外部创建会话标识。
u 一直生成新会话,供用户成功认证时登录。
u 预防用户操纵会话标识。
u 请勿接收用户浏览器登录时所提供会话标识。
u 假如有验证码。验证码改用application存放。同时记得释放资源
2.1.5 技术实现
u 登陆界面和登陆成功界面一致时
修改后台逻辑,在验证登陆逻辑时候,先强制让目前session过期,然后用新session存放信息。
u 登陆界面和登陆成功界面不一致时
在登陆界面后增加下面一段代码,强制让系统session过期。
request.getSession().invalidate();//清空session
Cookie cookie = request.getCookies()[0];//获取cookie
cookie.setMaxAge(0);//让cookie过期
注意:
框架2.0已经修改了登陆验证类,登陆成功后会清理掉目前session,重新创建一个新session。通常使用框架2.0项目均可统一增加此功效。
2.2 不充足帐户封锁
2.2.1 描述
程序没有使用锁定功效,能够穷举密码,能够造成蛮力攻击,恶意用户发送大量可能密码和/或用户名以访问应用程序尝试。 因为该技术包含大量登录尝试,未限制许可错误登录请求次数应用程序很轻易遭到这类攻击。
2.2.2 安全等级
高。
2.2.3 安全风险
可能会升级用户特权并经过 Web 应用程序获取管理许可权。
2.2.4 处理方案
请确定许可登录尝试次数(通常是 3-5 次),确保超出许可尝试次数以后,便锁定帐户。 为了避免真正用户因帐户被锁定而致电支持人员麻烦,能够仅临时性暂挂帐户活动,并在特定时间段以后启用帐户。帐户锁定大约 10 分钟,通常见这么方法阻止蛮力攻击。
2.2.5 技术实现
u 提供锁定信息配置类,可依据项目特定需求修改此配置信息。
u 修改登陆验证逻辑,依据上面配置信息提供帐户锁定功效。
注意:
框架2.0已经实现了此功效,通常使用框架2.0项目均可统一增加此功效。
2.3 可估计登录凭证
2.3.1 描述
发觉应用程序会使用可预期认证凭证(比如:admin+admin、guest+guest)。 攻击者很轻易估计用户名和密码,登录应用程序,从而获取未获授权特权。
2.3.2 安全等级
高。
2.3.3 安全风险
可能会升级用户特权并经过 Web 应用程序获取管理许可权。
2.3.4 处理方案
不应使用易于估计凭证(比如:admin+admin、guest+guest、test+test 等),因为它们可能很轻易估计,可让用户不妥进入应用程序。
2.3.5 技术实现
只要养成良好习惯,果断不使用轻易估计名和密码,即可根本杜绝这类问题。
2.4 登录错误消息凭证枚举
2.4.1 描述
当试图利用不正确凭证来登录时,当用户输入无效用户名和无效密码时,应用程序会分别生成不一样错误消息。 经过利用该行为,攻击者能够经过反复试验(蛮力攻击技术)来发觉应用程序有效用户名,再继续尝试发觉相关联密码。
2.4.2 安全等级
高。
2.4.3 安全风险
可能会升级用户特权并经过 Web 应用程序获取管理许可权。
2.4.4 处理方案
不管名和密码哪个错误,全部提醒一样消息。且同时加上登陆失败次数达成要求帐户锁定功效。
2.4.5 技术实现
不管名和密码哪个错误,全部提醒以下所表示一样消息:
一旦某个帐户连续登陆失败次数达成了要求数值,就会按配置时间被锁定,以下提醒:
如此一来,攻击者就没机会穷举帐户和密码了,这类攻击也就不可能发生了!
注意:
框架2.0已经实现了此功效,通常使用框架2.0项目均可统一增加此功效。
2.5 已解密登录请求
2.5.1 描述
经过HTTP POST 发送表单数据,这些数据将在HTTP报文中以明文形式传输。对于部分敏感数据(如用户名/密码、信用卡密码)以传统HTTP报文传输存在巨大风险。
2.5.2 安全等级
中。
2.5.3 安全风险
可能会窃取诸如用户名和密码等未经加密即发送了用户登录信息。
2.5.4 处理方案
(1) 饶过AppScan软件扫描
(2) 对提交敏感信息,一律以加密方法传给服务器。
(3) 采取基于SSLHTTPS传输协议,对提交敏感信息在传输过程中加密。
敏感信息包含:用户名、密码、社会保险号码、信用卡号码、驾照号码、电子邮件地址、电话号码、邮政编码等。
注意:假如不是基于SSLHTTPS传输协议话。对于提交敏感信息即使加密后,AppScan软件一样认为该漏洞存在。所以避免这一漏洞有效方案是饶过扫描或采取基于SSLHTTPS传输协议。
即使采取HTTPS传输数据有很高安全性,不过也有以下多个显著缺点:
u 需要申请一个正当组织颁发用来加/解密证书。
u 布署、配置繁琐。
u 一样硬件环境,效率比HTTP慢很多。
经过上述分析,采取基于SSLHTTPS传输协议,付出代价有点大,是否有必需这么做,值得深入讨论。
:把text="password"这个用其它替换,就能够处理已解密登录请求
<div><li>密 码:<input type="text" id="password1" style="width:146px;height:18px;"
onkeypress="javascript:hiddenPass(event)" onkeyup="this.value=this.value.replace(/./g,'*');"/><li>
<li><input id="password" type="hidden" name="password"/><li>
<div>
js代码
function hiddenPass(e){
e = e ? e : window.event;
var kcode = e.which ? e.which : e.keyCode;
var pass = document.getElementById("password1");
var j_pass = document.getElementById("password");
if(kcode!=8)
{
var keychar=String.fromCharCode(kcode);
j_pass.value=j_pass.value+keychar;
j_pass.value=j_pass.value.substring(0,pass.length);
}
2.5.5 技术实现
(1) 饶过AppScan软件扫描
绕过软件扫描有两种方法,以下:
u 修改密码输入框名字和类型
经过不停扫描测试分析,假如密码输入框名字不包含password、pwd等关键字话,大部分页面就不会扫描出漏洞。假如还能扫描出漏洞话,那就在修改密码输入框名字基础上,把输入框type属性值为password值改为text,然后用Javascript函数来模拟实现星号替换输入值密码输入效果即可饶过该扫描。
示例步骤以下:
Ø 在原来密码输入框前添加一个新输入控件:
如:<input type="text" id="code" name="code" value="" size="15" onfocus="document.getElementById(' cipher ').focus();" style="width:100px"/>
注:其中document.getElementById('cipher').focus()中 cipher为原来密码输入框ID值,这个是为了Tab键能起作用而设置。
Ø 修改原来密码框type属性为text,并添加onkeyup函数以下:
<input type="text" id="cipher" name="cipher" style="width:100px" size="15" onkeyup="document.getElementById('code').value=this.value.replace(/./g,'*');" />
其中code为新添加输入控件ID。
Ø 在页面head标签中间添加以下样式:
<style type="text/css">
<!--
#code{ position: absolute;font-size: 13px; font-family: 宋体;}
#cipher{ position: relative; left: 0; top: 0;font-size: 13px; font-family: 宋体; -moz-opacity:0;opacity: 0;filter: alpha(opacity=0); }
-->
</style>
其中code代表新添加输入控件ID,cipher代表原来密码输入框ID。样式表ID必需和控件ID一致。
u 采取AJAX登录
把登陆界面Form标签去掉,采取AJAX登录。示例代码以下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>登陆页面</title>
</head>
<style type="text/css">
</style>
<body>
<table width="500" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td height="332" valign="bottom" background="eaf/images/login_background.gif">
<table width="50%" border="0" align="right" cellpadding="0" cellspacing="0" height="41%">
<tr>
<td width="19%" nowrap="nowrap" style="font-size:13px;">用户:</td><td width="42%"><input type="text" name="j_username" style="width:100px" id="j_username" value="" size="15"/></td>
<td width="27%"><img src="eaf/images/ip.gif" style="cursor:pointer" /></td>
<td width="12%"> </td>
</tr>
<tr>
<td width="19%" nowrap="nowrap" style="font-size:13px;">密码:</td><td>
<input name="j_password" type="password" id="j_password" style="width:100px" size="15"" value="" /></td><td width="27%"><img src="eaf/images/ca.gif" style="cursor:pointer" onclick="caLogin()"/></td>
<td width="12%"> </td>
</tr>
<tr><td align="left"> </td>
<td><input type="checkbox" name="rememberMe" />
记住密码</td>
<td> </td>
<td> </td>
</tr>
<tr><td colspan="2" align="right"><img src="eaf/images/login.gif" style="cursor:pointer" onclick="getCustomerInfo()"/><img src="eaf/images/cancel.gif" style="cursor:pointer" onclick="return false;"/></td>
<td> </td>
<td> </td>
</tr>
<tr><td colspan="4"> </td></tr>
</table>
</td>
</tr>
</table>
<script language="javascript" type="text/javascript">
var request = false;
try {
request = new XMLHttpRequest();
} catch (trymicrosoft) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (othermicrosoft) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = false;
}
}
}
function getCustomerInfo() {
var url = "j_acegi_security_check?j_username="+document.getElementById("j_username").value+"&j_cipher="+document.getElementById("j_password").value;
alert("url"+url);
request.open("GET", url, true);
//调用回调函数
request.onreadystatechange = updatePage;
request.send(null);
}
function updatePage() {
if (request.readyState == 4) {
if (request.status == 200) {
var response = request.responseText;
document.write(response);
} else if (request.status == 404) {
alert("Requested URL is not found.");
} else if (request.status == 403) {
alert("Access denied.");
} else
alert("status is " + request.status);
}
}
</script>
</body>
</html>
(2) 采取基于SSLHTTPS传输协议
以Weblogic默认配置为例,步骤以下:
u 开启Weblogic,进入Console。展开Servers,单击服务器名,在右边配置栏中,选择常规。在出现内容中选中已启用SSL监视端口,并配置监视端口,以下图:
u 重新开启Weblogic,测试HTTPS协议,测试地址格式示例:
https://IP:端口/ApplicationName
2.6 SQL 注入
2.6.1 描述
伴随B/S模式应用开发发展,使得使用这种模式编写应用程序程序员也越来越多。不过因为这个行业入门门槛不高,程序员水平及经验也参差不齐,相当大一部分程序员在编写代码时候,没有对用户输入数据正当性进行判定,使应用程序存在安全隐患。攻击者能够经过互联网输入区域,利用一些特殊结构SQL语句插入SQL特殊字符和指令,提交一段数据库查询代码(通常是在浏览器地址栏进行,经过正常www端口访问),操纵实施后端DBMS查询并取得本不为用户所知数据技术,也就是SQL Injection(SQL注入)。
2.6.2 安全等级
高。
2.6.3 安全风险
可能会查看、修改或删除数据库条目和表。
2.6.4 处理方案
u 过滤掉用户输入中危险字符。
u 检验用户输入字段类型,确保用户输入值和类型(如 Integer、Date 等)有效,且符合应用程序预期。
u 屏蔽部分具体错误消息,因为黑客们能够利用这些消息。
u 使用专业漏洞扫描工具。
u 企业要在Web应用程序开发过程全部阶段实施代码安全检验。首先,要在布署Web应用之前实施安全测试,这种方法意义比以前更大、更深远。企业还应该在布署以后用漏洞扫描工具和站点监视工具对网站进行测试。
2.6.5 技术实现
(1)采取过滤器技术。
过滤技术是经过特定过滤函数,去查找每个请求每个参数对应值,假如出现了不许可字符,就会弹出以下提醒:
点确定后即可回到请求页面,删除敏感字符即可请求成功!
过滤器ParameterFilter.Java代码以下:
/**
* 参数过滤类,过滤一般表单
*/
public class ParameterFilter implements Filter {
private Pattern scriptPattern;
private Pattern sqlPattern;
private Pattern letterPattern;
private String notProtect = "";
private String totalLetter = "";
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filtreChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String contentType = request.getContentType() == null ? "" : request
.getContentType();
if (!contentType.startsWith("multipart/form-data")
&& !FilterUtils.isContainUrl(notProtect, FilterUtils
.getFullUrlFromRequest(req))) {
Enumeration params = req.getParameterNames();
boolean isSecurity = true;
while (null != params && params.hasMoreElements()) {
String para_name = (String) params.nextElement();
String[] para_value = null;
para_value = (String[]) req.getParameterValues(para_name);
for (int i = 0; i < para_value.length; i++) {
String _para_value = para_value[i].toLowerCase();
if (scriptPattern.matcher(_para_value).matches()
|| sqlPattern.matcher(_para_value).matches()) {
isSecurity = false;
break;
}
}
if (!isSecurity)
break;
}
if (!isSecurity) {
response.setContentType("text/html; charset=GBK");
response.getWriter().write("<script language='javascript'>alert('输入有误,请不要包含"+totalLetter+"中任何敏感字符!');history.go(-1);</script>");
return;
}
}
filtreChain.doFilter(request, response);
}
public void init(FilterConfig filterConfig) throws ServletException {
letterPattern = Ppile("[a-z]*");
String scriptPara = filterConfig.getInitParameter("scriptRegx").toLowerCase();
String sqlPara = filterConfig.getInitParameter("sqlRegx").toLowerCase();
notProtect = filterConfig.getInitParameter("notProtect");
scriptPattern = Ppile(resultStr(scriptPara));
sqlPattern = Ppile(resultStr(sqlPara));
totalLetter = scriptPara+"|"+sqlPara;
int index = totalLetter.indexOf("'");
if(index > 0){
totalLetter = totalLetter.substring(0, index)+"\\'"+totalLetter.substring(index+1);
}
}
public String resultStr(String oldStr) {
String newStr = "";
if (oldStr != null && !oldStr.equals("")) {
String[] temp = oldStr.split("\\|");
if (temp.length > 0) {
for (int i = 0; i < temp.length; i++) {
String tempStr = "";
boolean isLetter = false;
for (int j = 0; j < temp[i].length(); j++) {
String str = temp[i].substring(j, j + 1);
if (letterPattern.matcher(str).matches()) {
tempStr = tempStr+ "((\\%"+ parseAscii(str.toUpperCase()) + ")|" + "(\\%"+ parseAscii(str)+ ")|(" + str + "))";
isLetter = true;
} else {
if(str.equals("(" )|| str.equals(")"))
str = "\\"+str;
tempStr = tempStr + "((\\%"+ parseAscii(str) + ")|(" + str+ "))";
isLetter = false;
}
}
if(isLetter)
if (newStr.equals("")) {
newStr = newStr + ".*(\\s+)(" + tempStr + ")(\\s+).*";
} else {
newStr = newStr + "|.*(\\s+)(" + tempStr + ")(\\s+).*";
}
else
if (newStr.equals("")) {
newStr = newStr + ".*(" + tempStr + ").*";
} else {
newStr = newStr + "|.*(" + tempStr + ").*";
}
}
}
}
return newStr;
}
private String toHexUtil(int n){
String rt="";
switch(n){
case 10:rt+="A";break;
case 11:rt+="B";break;
case 12:rt+="C";break;
case 13:rt+="D";break;
case 14:rt+="E";break;
case 15:rt+="F";break;
default:
rt+=n;
}
return rt;
}
public String toHex(int n){
StringBuffer sb=new StringBuffer();
if(n/16==0){
return toHexUtil(n);
}else{
String t=toHex(n/16);
int nn=n%16;
sb.append(t).append(toHexUtil(nn));
}
return sb.toString();
}
public String parseAscii(String str){
StringBuffer sb=new StringBuffer();
byte[] bs=str.getBytes();
for(int i=0;i<bs.length;i++)
sb.append(toHex(bs[i]));
return sb.toString();
}
}
辅助类以下:
public class FilterUtils {
public static String getFullUrlFromRequest(HttpServletRequest httRequest) {
String url = httRequest.getRequestURI();
String queryString = httRequest.getQueryString();
if (null != queryString && !queryString.trim().equals("")) {
url = url + "?" + queryString;
}
return url;
}
public static boolean isContainUrl(String urlList, String url) {
boolean isExclude = false;
if (null != urlList && !urlList.trim().equals("")) {
String[] resources = urlList.split(",");
if (null != resources) {
for (int i = 0; i < resources.length; i++) {
if (null != resources[i] && !resources[i].equals(""))
if (url.indexOf(resources[i].trim()) >= 0) {
isExclude = true;
break;
}
}
}
}
return isExclude;
}
}
web.xml配置片段以下:
<filter>
<filter-name>SecurityFilter</filter-name>
展开阅读全文