AppScan安全检测工具,检测出的问题解决

一、跨站点脚本请求编制

问题体现:网页中存在类似

<script>tagJson = decodeURI('${tagJson}')的内容,则容易被注入类似下面内容进行攻击

<script>tagJson = decodeURI('${tagJson}');</script>

如果您有这样的问题存在,那只能从您自己的代码中进行解决了,最好不要再页面中存在el表达式;

除去上面提到的这种情况外,其他情况的解决方法:可以对一些特殊的字符进行拦截处理:

类似:http://blog.csdn.net/mym43210/article/details/41082999

我的解决办法,具体代码实现

1、在web.xml文件中配置过滤器

<!--配置过滤器 通过过滤器过滤SQL注入特殊字符  -->
    <filter>
        <filter-name>InjectFilter</filter-name>
        <filter-class>com.filter.InjectFilter</filter-class>
    </filter>
    <!--映射过滤器-->
    <filter-mapping>
      <filter-name>InjectFilter</filter-name>
        <!--“/*”表示拦截所有的请求 -->
       <url-pattern>/*</url-pattern>
    </filter-mapping>
2、 过滤器过滤代码:
package com.filter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Enumeration;
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.Filter;
import javax.servlet.http.HttpServlet;
 
/**
 *  通过过滤器过滤注入的特殊字符 
 * */
public  class InjectFilter extends HttpServlet implements Filter {
   
    private static final long serialVersionUID = 5286703103846683570L;
    private String failPage = "/error.jsp";//发生注入时,跳转页面
 
    public void doFilter(ServletRequest request,ServletResponse response,
            FilterChain filterchain)throws IOException, ServletException {
        //判断是否有注入攻击字符
        HttpServletRequest req = (HttpServletRequest) request;
        String inj = injectInput(req);
        if (!inj.equals("")) {
             request.getRequestDispatcher(failPage).forward(request, response);
             return;
        } else {
            // 传递控制到下一个过滤器
            filterchain.doFilter(request, response);
        }
    }
     
    /**
     * 判断request中是否含有注入攻击字符
     * @param request
     * @return
     */
    public String injectInput(ServletRequest request) {
         
        Enumeration e = request.getParameterNames();
        String attributeName;
        String attributeValues[];
        String inj = "";
        String injdb = "";
        while (e.hasMoreElements()) {
            attributeName = (String)e.nextElement();
            //不对密码信息进行过滤,一般密码中可以包含特殊字符
            if(attributeName.equals("userPassword")||attributeName.equals("confirmPassword")||attributeName.equals("PASSWORD")
                    ||attributeName.equals("password")||attributeName.equals("PASSWORD2")||attributeName.equals("valiPassword")){
                continue;
            }
             
            attributeValues = request.getParameterValues(attributeName);
            for (int i = 0; i < attributeValues.length; i++) {
                 
    if(attributeValues[i]==null||attributeValues[i].equals(""))
                    continue;
                inj = injectChar(attributeValues[i]);
                 
                if (!inj.equals(""))
                {
                    return inj;
                }       
            }
        }  
        return inj;
    }
     
    /**
     * 判断字符串中是否含有注入攻击字符
     * @param str
     * @return
     */
    public String injectChar(String str) {
         
       String inj_str = "\" ) \' * % < > &";
       String inj_stra[] = inj_str.split(" ");
         
       for (int i = 0 ; i < inj_stra.length ; i++ )
       {
           if (str.indexOf(inj_stra[i])>=0)
           {
               return inj_stra[i];
           }
       }
       return "";
    }
         
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
           // System.out.println("----注入过滤器初始化----"); 
    } 
     
}

二、跨站点请求伪造(CSRF)

CSRF(Cross Site Request Forgery),中文是跨站点请求伪造。CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
下面简单的陈述了csrf的攻击思想:

从上图可以看出,要完成一次CSRF攻击, 受害者必须依次完成两个步骤
  1. 登录受信任网站A,并在本地生成Cookie
  2. 在不登出A的情况下,访问危险网站B
  这里,也许会说:“ 如果不满足以上两个条件中的一个,就不会受到CSRF的攻击 ”。是的,确实如此,但不能保证以下情况不会发生:
  1.不能保证登录了一个网站后,不再打开一个tab页面并访问另外的网站。
  2.不能保证关闭浏览器了后,本地的Cookie立刻过期,上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
  3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。

目前防御csrf攻击的主要有以下四种策略:
1、验证 HTTP Referer 字段;
2、在请求地址中添加 token 并验证;
3、在 HTTP 头中自定义属性并验证;
4、Chrome 浏览器端启用 SameSite cookie

下面主要说明我是怎么完成的:
package com.common.util;

import javax.servlet.http.HttpServletRequest;

public class CSRFTool {
    public static String getToken(HttpServletRequest request) {
        return CSRFTokenManager.getTokenForSession(request.getSession());
    }
}

生成token代码
package com.common.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.UUID;

/**
 * {@link #getTokenForSession(HttpSession)} 得到当前会话的token(token获得的唯一方法)
 */
public final class CSRFTokenManager {

    /**
     * token参数名
     */
    static final String CSRF_PARAM_NAME = "xToken";

    /**
     * 将token保存在session中
     */
    public final static String CSRF_TOKEN_FOR_SESSION_ATTR_NAME = CSRFTokenManager.class.getName() + ".tokenval";

    public static String getTokenForSession(HttpSession session) {
        String token = null;
        DESUtil des = new DESUtil("jrkj123");
        synchronized (session) {
            token = (String) session.getAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
//            token = des.encryptStr(tokens1);
            if (null == token) {
                String tokens = UUID.randomUUID().toString();
                token = des.encryptStr(tokens);
                session.setAttribute(CSRF_TOKEN_FOR_SESSION_ATTR_NAME, token);
            }
        }
        return token;
    }

    /**
     * 从会话中提取token值
     *
     * @param request
     * @return
     */
    public static String getTokenFromRequest(HttpServletRequest request) {
        String token = request.getParameter(CSRF_PARAM_NAME);
        if (token == null || "".equals(token)) {
            token = request.getHeader(CSRF_PARAM_NAME);
        }
        return token;
    }

    private CSRFTokenManager() {
    }
}

加密方法:
package com.common.util;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import java.security.Key;
import java.security.SecureRandom;

public class DESUtil {
    Key key ;
    public DESUtil() {

    }
    public DESUtil(String str) {
        setKey(str); // 生成密匙
    }
    public Key getKey() {
        return key ;
    }
    public void setKey(Key key) {
        this . key = key;
    }
    public void setKey(String strKey) {
        try {
            KeyGenerator _generator = KeyGenerator.getInstance ( "DES" );
            _generator.init( new SecureRandom(strKey.getBytes("utf-8")));
            SecureRandom secureRandom= SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(strKey.getBytes());
            _generator.init(56, secureRandom);
            this.key = _generator.generateKey();
            _generator = null ;
        } catch (Exception e) {
            throw new RuntimeException("Error initializing class. Cause: " + e);
        }
    }

    /**
     * 加密 String 明文输入 ,String 密文输出
     */
    public String encryptStr(String strMing) {
        byte [] byteMi = null ;
        byte [] byteMing = null ;
        String strMi = "" ;
        BASE64Encoder base64en = new BASE64Encoder();
        try {
            byteMing = strMing.getBytes( "UTF-8" );
            byteMi = this.encryptByte(byteMing);
            strMi = base64en.encode(byteMi);
        } catch (Exception e) {
            throw new RuntimeException("Error initializing class. Cause: " + e);
        } finally {
            base64en = null ;
            byteMing = null ;
            byteMi = null ;
        }
        return strMi;
    }

    /**
     * 解密 以 String 密文输入 ,String 明文输出
     *
     * @param strMi
     * @return
     */
    public String decryptStr(String strMi) {
        BASE64Decoder base64De = new BASE64Decoder();
        byte [] byteMing = null ;
        byte [] byteMi = null ;
        String strMing = "" ;
        try {
            byteMi = base64De.decodeBuffer(strMi);
            byteMing = this.decryptByte(byteMi);
            strMing = new String(byteMing, "UTF-8" );
        } catch (Exception e) {
            throw new RuntimeException("Error initializing class. Cause: " + e);
        } finally {
            base64De = null ;
            byteMing = null ;
            byteMi = null ;
        }
        return strMing;
    }

    /**
     * 加密以 byte[] 明文输入 ,byte[] 密文输出
     *
     * @param byteS
     * @return
     */
    private byte [] encryptByte( byte [] byteS) {
        byte [] byteFina = null ;
        Cipher cipher;
        try {
            cipher = Cipher.getInstance ( "DES" );
            cipher.init(Cipher.ENCRYPT_MODE , key );
            byteFina = cipher.doFinal(byteS);
        } catch (Exception e) {
            throw new RuntimeException("Error initializing class. Cause: " + e);
        } finally {
            cipher = null ;
        }
        return byteFina;
    }

    /**
     * 解密以 byte[] 密文输入 , 以 byte[] 明文输出
     *
     * @param byteD
     * @return
     */
    private byte [] decryptByte( byte [] byteD) {
        Cipher cipher;
        byte [] byteFina = null ;
        try {
            cipher = Cipher.getInstance ( "DES" );
            cipher.init(Cipher.DECRYPT_MODE , key );
            byteFina = cipher.doFinal(byteD);
        } catch (Exception e) {
            throw new RuntimeException("Error initializing class. Cause: " + e);
        } finally {
            cipher = null ;
        }
        return byteFina;
    }
    public static void main(String[] args) throws Exception {
        DESUtil des = new DESUtil("jrkj123");
        String str1 = "4cdbc040657a4847b2667e31d9e2c3d9" ;
        // DES 加密字符串
        String str2 = des.encryptStr(str1);
        // DES 解密字符串
        String deStr = des.decryptStr(str2);
        System. out .println( " 加密前: " + str1);
        System. out .println( " 加密后: " + str2);
        System. out .println( " 解密后: " + deStr);
    }
}

拦截器:
package com.common.interceptors;

import com.common.util.CSRFTool;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TokenAjaxInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setContentType("multipart/form-data");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html");
        //从前端获取到的token
        String token = request.getHeader("token");
        response.setHeader("token", token);//将token放到response中
        //后台生成的token
        String req_token = CSRFTool.getToken(request);

        //判断是不是ajax请求
        String requestUri = request.getRequestURI(); //请求完整路径,可用于登陆后跳转
        String contextPath = request.getContextPath();  //项目下完整路径
        String url = requestUri.substring(contextPath.length()); //请求页面

//        System.out.println("发生拦截...");
//        System.out.println("来自:"+requestUri+"的请求");
        System.out.println("来自url:"+url);
        /**
         * 拦截目录下请求,是否为ajax请求
         *   是:需要验证token是否正常;正常-->通过,不正常-->不通过
         *   否:全部拦截
         */
        if (request.getHeader("x-requested-with") != null && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")){
            //如果是ajax请求响应头会有,x-requested-with
            System.out.println("发生ajax请求...");
            if(req_token != null){
                //验证token是否合理
                if(token.equals(req_token) || token == req_token){
                    System.out.println("token验证正确,请求成功");
                    return true;
                }else{
                    System.out.println("token验证不正确,请求失败");
                    return false;
                }
            }else {
                System.out.println("没有token信息,请求失败");
                return false;
            }
        }else{
            System.out.println("非ajax请求,拦截...");
            return false;
        }
    }
}

配置文件中添加:
<!--该拦截器主要作用是:对ajax请求进行拦截,同时判断token-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <mvc:exclude-mapping path="/login/help"/>
            <bean class="com.common.interceptors.TokenAjaxInterceptor"/>
        </mvc:interceptor>

三、HTTP动词篡改的认证旁路
AppScan 检测的问题:
原始:

测试:


请求方法改变了,但是结果,居然请求成功了,是不是很奇怪……
修改方法也挺简单,改下tomcat的web.xml就好。
<security-constraint>  
    <web-resource-collection>  
        <http-method>HEAD</http-method>  
        <http-method>PUT</http-method>  
        <http-method>DELETE</http-method>  
        <http-method>OPTIONS</http-method>  
        <http-method>TRACE</http-method>  
        <url-pattern>/*</url-pattern>  
    </web-resource-collection>  
    <auth-constraint>  
        <role-name></role-name>  
    </auth-constraint>  
</security-constraint>  

这种方法可能的弊端就是需要修改服务器环境中的toncat配置,有一些公司可能不太好实现,需要找到一种比较好的解决方法, 请各位大神指教

四、缺少  X-XSS-Protection
web.xml中添加代码如下:
<add name="X-XSS-Protection" value="1;mode=block"/>

五、缺少“ X-Content-Type-Options ” 头
web.xml中添加代码如下:
<add name="X-Content-Type-Options" value="nosniff"/>

六、缺少“ Content-Security-Policy” 头
web.xml中添加代码如下:
<system.webServer>
  <httpProtocol> 
    <customHeaders>
      <add name="X-Content-Type-Options" value="nosniff"/>
      <add name="X-XSS-Protection" value="1;mode=block"/>
      <add name="X-Frame-Options" value="SAMEORIGIN"/>
      <add name="Content-Security-Policy" value="default-src 'self'"/>
    </customHeaders>
  </httpProtocol>
</system.webServer>

上述四五六这三个问题在网上查到的代码说在web里添加这几行的头就行,结果不行; 请各路大神指教!




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值