安全漏洞扫描,风险原因分析及解决方案

时间:2024-03-06 12:05:15

1 会话标识未更新 
1.1 原因 
在用户进入登录页面,但还未登录时,就已经产生了一个session,用户输入信息,登录以后,session的id不会改变,也就是说还是以前的那个session(事实上session也确实不会改变,因为没有建立新session,原来的session也没有被销毁)。 
很多人只是让会话invalidate没有用(request.getSession().invalidate();),是因为invalidate方法不是真正的将session销毁,只是将session中的内容清空,所以当invalidate以后再新建session,新建的session其实不是新的,是将之前的session重新启用了。于是session的id不变就不奇怪了。只有cookie失效掉,才能换成新的session id 
1.2 解决 
在登录页面上加上一段代码: 

request.getSession().invalidate() ;         //清空session 

if (request.getCookies()!=null) { 
   Cookie cookie = request.getCookies()[0];     // 获取cookie 
   cookie.setMaxAge(0);                    // 让cookie过期 
} 


注:会话失效后,请不要在代码前面使用SESSION保存数据。 
2 SQL注入 
2.1 原因 
1. 没有正确过滤转义字符  
在用户的输入没有为转义字符过滤时,就会发生这种形式的注入式攻击,它会被传递给一个SQL语句。这样就会导致应用程序的终端用户对数据库上的语句实施操纵。比方说,下面的这行代码就会演示这种漏洞: 

statement := "SELECT * FROM users WHERE name = \'" + userName + "\'; " 


将用户名变量(即username)设置为:a\' or \'t\'=\'t,此时原始语句发生了变化. 

2. 用户输入错误的数据类型 
如果一个用户提供的字段并非一个强类型,或者没有实施类型强制,就会发生这种形式的攻击。当在一个SQL语句中使用一个数字字段时,如果程序员没有检查用户输入的合法性(是否为数字型)就会发生这种攻击。例如: 
statement := "SELECT * FROM data WHERE id = " + a_variable + "; " 
从这个语句可以看出,作者希望a_variable是一个与“id”字段有关的数字。不过,如果终端用户选择一个字符串,就绕过了对转义字符的需要。例如,将a_variable设置为:1; DROP TABLE users,它会将“users”表从数据库中删除,SQL语句变成: 

SELECT * FROM DATA WHERE id = 1; DROP TABLE users; 


3. 数据库服务器中的漏洞 
有时,数据库服务器软件中也存在着漏洞,如MYSQL服务器中mysql_real_escape_string()函数漏洞。这种漏洞允许一个攻击者根据错误的统一字符编码执行一次成功的SQL注入式攻击。 
4. 盲目SQL注入式攻击 
当一个Web应用程序易于遭受攻击而其结果对攻击者却不见时,就会发生所谓的盲目SQL注入式攻击。有漏洞的网页可能并不会显示数据,而是根据注入到合法语句中的逻辑语句的结果显示不同的内容。这种攻击相当耗时,因为必须为每一个获得的字节而精心构造一个新的语句。但是一旦漏洞的位置和目标信息的位置被确立以后,一种称为Absinthe的工具就可以使这种攻击自动化。 
5. 条件响应 
注意,有一种SQL注入迫使数据库在一个普通的应用程序屏幕上计算一个逻辑语句的值: 

SELECT booktitle FROM booklist WHERE bookId = \'OOk14cd\' AND 1=1 


这会导致一个标准的面面,而语句 
SELECT booktitle FROM booklist WHERE bookId = \'OOk14cd\' AND 1=2 
在页面易于受到SQL注入式攻击时,它有可能给出一个不同的结果。如此这般的一次注入将会证明盲目的SQL注入是可能的,它会使攻击者根据另外一个表中的某字段内容设计可以评判真伪的语句。 
6. 条件性差错 
如果WHERE语句为真,这种类型的盲目SQL注入会迫使数据库评判一个引起错误的语句,从而导致一个SQL错误。

例如: 

SELECT 1/0 FROM users WHERE username=\'Ralph\'


显然,如果用户Ralph存在的话,被零除将导致错误。 
7. 时间延误 
时间延误是一种盲目的SQL注入,根据所注入的逻辑,它可以导致SQL引擎执行一个长队列或者是一个时间延误语句。攻击者可以衡量页面加载的时间,从而决定所注入的语句是否为真。 
2.2 解决 
1. 用预编译处理语言 
要防御SQL注入,用户的输入就绝对不能直接被嵌入到SQL语句中。恰恰相反,用户的输入必须进行过滤,或者使用参数化的语句。参数化的语句使用参数而不是将用户输入嵌入到语句中。在多数情况中,SQL语句就得以修正。然后,用户输入就被限于一个参数。下面是一个使用Java和JDBC API例子: 

PreparedStatement prep = conn.prepareStatement("SELECT * FROM USERS WHERE PASSWORD=?"); 

prep.setString(1, pwd); 


总体上讲,有两种方法可以保证应用程序不易受到SQL注入的攻击,一是使用代码复查,二是强迫使用参数化语句的。强迫使用参数化的语句意味着嵌入用户输入的SQL语句在运行时将被拒绝。 
2. 轨范出错处理 
防范SQL注入,还要避免出现一些详细的错误消息,因为黑客们可以利用这些消息。要使用一种标准的输入确认机制来验证所有的输入数据的长度、类型、语句、企业规则等。 
3. 使用专业的漏洞扫描工具 
但防御SQL注入攻击也是不够的。攻击者们目前正在自动搜索攻击目标并实施攻击。其技术甚至可以轻易地被应用于其它的Web架构中的漏洞。企业应当投资于一些专业的漏洞扫描工具,如大名鼎鼎的Acunetix的Web漏洞扫描程序等。一个完善的漏洞扫描程序不同于网络扫描程序,它专门查找网站上的SQL注入式漏洞。最新的漏洞扫描程序可以查找最新发现的漏洞。 

3 XSS跨站脚本编制 
3.1 原因 
它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。XSS属于被动式的攻击,因为其被动且不好利用,所以许多人常忽略其危害性。 
3.2 解决 
在防止这类问题时,输入内容的转义效果远比内容过滤要好。 
具体实施可以增加一个request的转码过滤器。代码: 

package com.apusic.portal.sso; 
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.*; 
import java.util.*; 
/** 
* Servlet Filter implementation class SqlEscapeFilter 
*/ 
public class SqlEscapeFilter implements Filter { 
    /** 
     * Default constructor. 
     */ 
    public SqlEscapeFilter() { 
        // TODO Auto-generated constructor stub 
    } 
    /** 
     * @see Filter#destroy() 
     */ 
    public void destroy() { 
        // TODO Auto-generated method stub 
    } 
    /** 
     * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) 
     */ 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 
        // TODO Auto-generated method stub 
        // place your code here 
        HttpServletRequest hreq = (HttpServletRequest)request; 
        Map map = hreq.getParameterMap(); 
        Iterator itr = map.keySet().iterator(); 
        while( itr.hasNext() ) 
        { 
            String key = itr.next().toString(); 
            String [] values = hreq.getParameterValues(key); 
            if( values != null ) 
            { 
                for( int i = 0; i < values.length; i++ ) 
                { 
                    values[i] = cleanXSS(values[i]); 
                } 
            } 
            hreq.setAttribute(key, values); 
        } 
        // pass the request along the filter chain 
        chain.doFilter(request, response); 
    } 
    /** 
     * @see Filter#init(FilterConfig) 
     */ 
    public void init(FilterConfig fConfig) throws ServletException { 
        // TODO Auto-generated method stub 
    } 
    private String cleanXSS(String value) 
    { 
        value = value.replaceAll("<", "& lt;").replaceAll(">", "& gt;"); 
        value = value.replaceAll("\\(", "& #40;").replaceAll("\\)", "&#41;"); 
        value = value.replaceAll("\'", "& #39;"); 
        value = value.replaceAll("eval\\((.*)\\)", ""); 
        value = value.replaceAll("[\\\"\\\\'][\\s]*javascript:(.*)[\\\"\\\\']", "\"\""); 
        value = value.replaceAll("script", ""); 
        return value; 
    } 
} 


配置应用中的web.xml 
Web.xml: 

<filter> 
     <display-name>SqlEscapeFilter</display-name> 
     <filter-name>SqlEscapeFilter</filter-name> 
     <filter-class>com.apusic.portal.sso.SqlEscapeFilter</filter-class> 
</filter> 
<filter-mapping> 
     <filter-name>SqlEscapeFilter</filter-name> 
     <url-pattern>*.jsp</url-pattern> 
</filter-mapping> 


4 XSRF跨站请求伪造 
4.1 原因 
CSRF利用方式比较类似XSS(跨站脚本 Cross Site Scripting) ,不过不同的是CSRF是构造一个提交来让其他人访问后,利用站点对这些人的信任来进行一些所期望的操作. 
比如:A和B在同一个有XSS漏洞的站点C,B登录过D站点,并且有这个D站点的Cookies,这时候如果A构造一个CSRF,内容为给 A在D站点的账户转移一些虚拟币,如果这时候在C站点浏览的B用户打开了A构造的含有CSRF的页面,这时候B的D站点用户会因为对B用户的信任而进行给 A转账的操作. 
4.2 解决 
此类攻击的情景相对的比较复杂,具体解决可以参考以下5点: 
第一:限制验证cookie的到期时间。这些cookie的合法时间越短,黑客利用你的Web应用程序的机会就越小。不过,这个时间越短,用户就越不方便。因此,你需要在安全性和方便性之间进行平衡。 
第二:执行重要业务之前,要求用户提交额外的信息。要求用户在进行重要业务前输入口令,这可以防止黑客发动CSRF攻击(只要浏览器中没有包含口令),因为这种重要信息无法预测或轻易获得。 
第三:使用秘密的无法预测的验证符号。当保存在用户浏览器中的cookie仅由一次会话确认时,CSRF攻击才会有效。所以在每次HTTP请求(当然攻击者无法提前知道)中都有附加的特定会话的信息,这样就可以挫败CSRF攻击。不过,如果这种应用程序存在跨站脚本漏洞,黑客就有可能访问这种验证符号。 
第四:使用定制的HTTP报头。如果执行交易的所有请求都使用XMLHttpRequest并附加一个定制的HTTP报头,同时拒绝缺少定制报头的任何请求,就可以用XMLHttpRequest API来防御CSRF攻击。由于浏览器通常仅准许站点将定制的HTTP报头发送给相同站点,从而了防止由CSRF攻击的源站点所发起的交易。 
第五:检查访问源的报头。在浏览者发送HTTP请求时,它通常会包含源自访问源报头的URL。理论上讲,你可以使用这些信息来阻止源自其它任何站点(而不是来自Web应用程序自身)的请求。 

综合公司以往平台报出此类漏洞产出的原因,基本上都可以通过以下简单做法加以解决: 
在请求后面加上一次性令牌。如验证码,手机短信验证,或者sessionID等。 
5 登录错误消息凭证枚举(不充分帐户*) 
5.1 原因 
当试图利用不正确的凭证来登录时,当用户输入无效的用户名和无效的密码时,应用程序会分别生成不同的错误消息。通过利用该行为攻击者可以通过反复试验,加暴力破解来发现应用程序的有效用户名、再继续尝试发现相关联的密码。 
5.2 解决 
不论用户名或密码出现问题都提示同样的错误,且同时加上登陆失败次数达到规定次数,则执行帐户锁定功能。 
6 HTML注释敏感信息泄露 
6.1 原因 
页面源代码不正确的注释方式。 
6.2 解决 
将html中有关密码之类的敏感注释去掉或者用<%-- -%>隐式注释。 
7 应用程序错误 
7.1 原因 
未执行验证,可能输入参数数据类型不匹配。 
7.2 解决 
实行严格的数据类型验证。 
8 已解密的登录请求 
8.1 原因 
AppScan 的推理是“AppScan 识别了不是通过SSL 发送的密码参数。 
8.2 解决 
第一:采用基于SSL的HTTPS传输协议 
第二:对敏感信息加密并绕过扫描(只要不是采用SSL安全认证即使加密了但是AppScan还是会扫描出来) 
    

 <input type="text" id="password1"  style="width:195px; height:25px; line-height:25px;border: solid 1px #89B4D6;color:#999" value="10086密码" onfocus="javascript:clearPass();"  onkeyup="this.value=this.value.replace(/./g,\'*\');" onkeypress="javascript:hiddenPass()"/> 
     <input id="password" type="hidden" name="password"  class="inputs_dl" value="" /> 
将type为password的改为text,然后用JS做一个假的密码输入框,AppScan会扫描type为password的控件。 
   function hiddenPass(){ 
        var pass = document.getElementById("password1"); 
    var j_pass = document.getElementById("password"); 
      if(event.keyCode==13){ 
      check(); 
      } 
       var keycode=event.keyCode;                 
        var keychar=String.fromCharCode(keycode); 
      j_pass.value=j_pass.value+keychar; 
      j_pass.value=j_pass.value.substring(0,pass.length);    
  } 
    
   function clearPass(){ 
       $("#password1").val(""); 
       $("#password").val(""); 
  } 


然后传输密码的时候加个密: 

password = base64encode(password); 


9 启用了不安全的HTTP方法 
9.1 原因 
除标准的GET与POST方法外,HTTP请求还使用其他各种方法。许多这类方法主要用于完成不常见与特殊的任务。如果低权限用户可以访问这些方法,他们就能够以此向应用程序实施有效攻击。以下是一些值得注意的方法:
PUT,向指定的目录上传附加文件; 
DELETE,删除指定的资源; 
COPY,将指定的资源复制到Destination消息头指定的位置; 
MOVE,将指定的资源移动到Destination消息头指定的位置; 
SEARCH,在一个目录路径中搜索资源。 
PROPFIND,获取与指定资源有关的信息,如作者、大小与内容类型。 
TRACE,在响应中返回服务器收到的原始请求。可以使用这种方法避开阻止跨站点脚本的防御 
9.2 解决 
如何禁止DELETE、PUT、OPTIONS、TRACE、HEAD等协议访问应用程序应用程序呢? 
解决方法 
第一步:修改应用程序的web.xml文件的协议 

1.    <?xml version="1.0" encoding="UTF-8"?> 
2.    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" 
3.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
4.    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" 
5.    version="2.4"> 


第二步:在应用程序的web.xml中添加如下的代码即可 

1.    <security-constraint> 
2.    <web-resource-collection> 
3.    <url-pattern>/*</url-pattern> 
4.    <http-method>PUT</http-method> 
5.    <http-method>DELETE</http-method> 
6.    <http-method>HEAD</http-method> 
7.    <http-method>OPTIONS</http-method> 
8.    <http-method>TRACE</http-method> 
9.    </web-resource-collection> 
10.    <auth-constraint> 
11.    </auth-constraint> 
12.    </security-constraint> 
13.    <login-config> 
14.    <auth-method>BASIC</auth-method> 
15.    </login-config> 


10 禁止页面缓存 
10.1 原因 
能够访问到缓存的脱机数据导致泄密。 
10.2 解决 
建议在web管理后台程序的过滤器里增加如下代码: 
 

 response.setHeader("Cache-Control", "no-cache"); //只是请求或响应消息不缓存 
  response.setHeader("Cache-Control", "no-store"); //在请求消息中发送将使得请求和响应消息都不使用缓存                                                                                            
response.setDateHeader("Expires", 0); //缓存距离过期的时间为0毫秒,即缓存立即过期 
  response.setHeader("Pragma", "no-cache"); //页面不缓存 


11 数据库错误模式 
11.1 原因 
主要是一些数据连接错误信息,通过提交特殊构造的字符,程序会暴露一些数据库信息,也容易引起SQL注入攻击。 
现有平台发现的例子: 

<html><head><title>Apache Tomcat/5.5.28 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 500 - </h1><HR size="1" noshade="noshade"><p><b>type</b> Exception report</p><p><b>message</b> <u></u></p><p><b>description</b> <u>The server encountered an internal error () that prevented it from fulfilling this request.</u></p><p><b>exception</b> <pre>javax.servlet.ServletException: org.hibernate.HibernateException: java.sql.SQLException: ORA-12899: value too large for column &quot;QCSMS&quot;.&quot;T_SYS_LOGON_LOG&quot;.&quot;USER_CODE&quot; (actual: 24, maximum: 20)