首页
社区
课程
招聘
[原创]J2EE漏洞与OWASP ESAPI修复方案之(URL_Redirection)
发表于: 2016-8-11 15:54 4963

[原创]J2EE漏洞与OWASP ESAPI修复方案之(URL_Redirection)

2016-8-11 15:54
4963

一、  URL_Redirection漏洞介绍
这类漏洞,一般称之为url重定向或url跳转漏洞,利用这类漏洞可以用于网页钓鱼,设想下这种http://www.cmbchina.com/example.jsp?forward=http://40069911.jp/121.html发过来,保不准只关注到www.cmbchina.com,url重定向跳转类漏洞owasp官方定义为Unvalidated Redirects and Forwards,翻译成中文是“未经验证的重定向和转发”,漏洞介绍:https://www.owasp.org/index.php/Unvalidated_Redirects_and_Forwards_Cheat_Sheet
    J2EE这类漏洞产生,引用owasp官方阐述,一般是由于采用了不安全的URL Redirects, 比如response.sendRedirect(url);或者采用了不安全的Forward,比如request.getRequestDispatcher(url).forward(request, response)。分别简单分析下:
ν  Dangerous URL Redirects
1)、Dangerous URL Redirect Example 1
response.sendRedirect(request.getParameter("url"));
2)、Dangerous URL Redirect Example 2:
某些MVC框架,可能直接封装了跳转方法:
[HttpPost]
 public ActionResult LogOn(LogOnModel model, string returnUrl)
 {
   if (ModelState.IsValid)
   {
     if (MembershipService.ValidateUser(model.UserName, model.Password))
     {
       FormsService.SignIn(model.UserName, model.RememberMe);
       if (!String.IsNullOrEmpty(returnUrl))
       {
         return Redirect(returnUrl);
       }
       else
       {
         return RedirectToAction("Index", "Home");
       }
     }
     else
     {
       ModelState.AddModelError("", "The user name or password provided is incorrect.");
     }
   }
   return View(model);
 }
ν  Dangerous Forward Example
public class ForwardServlet extends HttpServlet 
{
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String query = request.getQueryString();
    if (query.contains("url")) 
    {
      String url= request.getParameter("url");
      try 
      {
        request.getRequestDispatcher(url).forward(request, response);
      } 
      catch (ServletException e) 
      {
        e.printStackTrace();
      }
    }
  }
}
讨论下:request.getRequestDispatcher(url).forward(request, response)与response.sendRedirect(url)都可以跳转,是否都存在url重定向漏洞,需要对比分析一下,
OWASP官网介绍危险的forward样例时,提到了下面这段话,不知道大家有没有注意:

Dangerous Forward Example
When applications allow user input to forward requests between different parts of the site, the application must check that the user is authorized to access the url,
意思简单说就是forward一般用于应用中网站不同资源间转发请求,例如url=English.html表示英文页面,url=Chinese.html表示中文页面,admin.jsp表示后台页面,通过forward可以有效网站资源间跳转,
http://www.example.com/function.jsp?url=english.html
http://www.example.com/function.jsp?url=chinese.html http://www.example.com/function.jsp?url=admin.jsp 
说的很清楚forward()方法只能重定向到同一个Web应用程序中的某个资源,在资源跳转间需要考虑授权,防止非授权访问,forward()方法只能重定向到同一个Web应用程序中的某个资源,sendRedirect()方法可以让你重定向到任何URL。

二、安全修复方案
Preventing Unvalidated Redirects and Forwards
Safe use of redirects and forwards can be done in a number of ways: 
•  Simply avoid using redirects and forwards(1、尽量避免使用这两类)
•  If used, do not allow the url as user input for the destination. This can usually be done. In this case, you should have a method to validate URL(2、万一要用也可以,需要有个方法验证客户端传入的url参数有效性). 
•  If user input can’t be avoided, ensure that the supplied value is valid, appropriate for the application, and is authorized for the user. 
•  It is recommended that any such destination input be mapped to a value, rather than the actual URL or portion of the URL, and that server side code translate this value to the target URL. (3、需要考虑授权、映射表,不在讨论范畴)
•  Sanitize input by creating a list of trusted URL's (lists of hosts or a regex)(4、创建信任url白名单,主机列表、正则表达式). 
•  Force all redirects to first go through a page notifying users that they are going off of your site, and have them click a link to confirm(4、强制所有重定向先弹出通知,他们将关闭您的网站的用户页面,并让他们点击一个链接,确认、简单而言就是跳转前有个notice,告诉你前往xx站点,你点不点?). 
不知道是因为这类漏洞没代表性,还是没查到,查了下OWASP ESAPI的确没有针对类似漏洞的修复esapi介绍,一般有的话,都有类似这种参考修复:


自己动手吧,修复考虑两类情况:
1、  跳转只发生在同一网站内
修复方案1、直接建议使用request.getRequestDispatcher(url).forward(request, response)
这样接受进来的参数,也没法构造跳转到不可信网站链接(本网站某页面被挂马除外)
     String url= request.getParameter("url");
          try {
        request.getRequestDispatcher(url).forward(request, response);
      } catch (Exception e) {
        e.printStackTrace();
      }  
修复方案2-把传入的http|https过滤掉,没了http、https头跳转会404 error
String url=request.getParameter("url").toLowerCase();
    try {
      String safe=url.replaceAll("(http|https)", "");//没了http、https头跳转不了
      response.sendRedirect(safe);
    } catch (Exception e) {
      e.printStackTrace();
    }
2、  业务需要,需在不同网站间跳转,设置可信域名白名单,超出不让跳转
HashMap<String, String> extMap = new HashMap<String, String>();
    extMap.put("white_url", "www.sohu.com,www.baidu.com,sina.com,59.123.11.80:8080");//信任域名列表
    String url=request.getParameter("url")+"/".toLowerCase();
    int start = url.lastIndexOf("://");
    String input = url.substring(start+3).substring(0, url.substring(start+3).indexOf("/"));
    try {
      if (!Arrays.<String> asList(extMap.get("white_url").split(",")).contains(input)){
        response.getWriter().println("Invalid url_redirection...");
      }
      else{    
        response.sendRedirect(url);
      }
    } catch (Exception e) {
      e.printStackTrace();
    }


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 0
支持
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  axiuno   +1.00 2017/05/04
最新回复 (2)
雪    币: 250
活跃值: (70)
能力值: (RANK:140 )
在线值:
发帖
回帖
粉丝
3
2.1、XSS跨站脚本漏洞介绍
XSS跨站脚本漏洞网上资料很多,就不具体名词介绍了,主要产生原因就是一些可输入的位置(多见用户交互功能页面,如留言板、论坛等等)插入了不可信的数据(html、js)内容,比如这些地方:
<script>...NEVER PUT UNTRUSTED DATA HERE...</script>   directly in a script
<!--...NEVER PUT UNTRUSTED DATA HERE...-->     inside an HTML comment
<div ...NEVER PUT UNTRUSTED DATA HERE...=test />       in an attribute name
<a NEVER PUT UNTRUSTED DATA HERE... href="/test" />   in a tag name
<style>...NEVER PUT UNTRUSTED DATA HERE...</style>   directly in CSS
{"Message":"dev.net.ie/api/pay/.html?HouseNumber=9&AddressLine
=The+Gardens"&NEVER PUT UNTRUSTED DATA H&AddressLine2=foxlodge+woods&TownName=Meath'.","MessageDetail":"No type was found that matches the controller named 'pay'."}   in JSON,
2.2、安全修复方案
2.2.1、HTTP请求时进行专义
请求时进行转义处理下面介绍使用filter的编写:
a)、重写Filter类doFilter方法
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 {

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }
  @Override
  public void destroy() {
  }
  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain chain) throws IOException, ServletException {
    chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request),
        response);
  }
}
b)、Wrapper编写
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.owasp.esapi.ESAPI;

public class XSSRequestWrapper extends HttpServletRequestWrapper {
       
        public XSSRequestWrapper(HttpServletRequest servletRequest) {  
        super(servletRequest);  
    }  

        @Override
        public String[] getParameterValues(String parameter) {
                String[] values = super.getParameterValues(parameter);

                if (values == null) {
                        return null;
                }

                int count = values.length;
                String[] encodedValues = new String[count];
                for (int i = 0; i < count; i++) {
                        encodedValues[i] = stripXSS(values[i]);
                }

                return encodedValues;
        }

        @Override
        public String getParameter(String parameter) {
                String value = super.getParameter(parameter);

                return stripXSS(value);
        }

        @Override
        public String getHeader(String name) {
                String value = super.getHeader(name);
                return stripXSS(value);
        }

        private String stripXSS(String value) {
                if (value != null) {
                        // 强烈推荐使用esapi进行专义,不要通过正则去过滤非法标签
                        value = ESAPI.encoder().encodeForHTML(value);

                        // Avoid null characters
                        /**
                        value = value.replaceAll("", "");

                        // Avoid anything between script tags
                        Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>",Pattern.CASE_INSENSITIVE);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Avoid anything in a src='...' type of expression
                        scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Remove any lonesome </script> tag
                        scriptPattern = Pattern.compile("</script>",Pattern.CASE_INSENSITIVE);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Remove any lonesome <script ...> tag
                        scriptPattern = Pattern.compile("<script(.*?)>",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");
                       
                        // Remove any lonesome <input ...> tag
                        scriptPattern = Pattern.compile("<input(.*?)>",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");                       
                       
                        // Remove any lonesome <iframe ...> tag
                        scriptPattern = Pattern.compile("<iframe(.*?)>",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Avoid eval(...) expressions
                        scriptPattern = Pattern.compile("eval\\((.*?)\\)",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Avoid expression(...) expressions
                        scriptPattern = Pattern.compile("expression\\((.*?)\\)",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Avoid javascript:... expressions
                        scriptPattern = Pattern.compile("javascript:",Pattern.CASE_INSENSITIVE);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Avoid vbscript:... expressions
                        scriptPattern = Pattern.compile("vbscript:",Pattern.CASE_INSENSITIVE);
                        value = scriptPattern.matcher(value).replaceAll("");

                        // Avoid onload,onmouseover,onmouseout expressions
                        scriptPattern = Pattern.compile("on(load|mouseover|mouseout|error)(.*?)=",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");
                       
                        // Avoid alert,confirm,prompt
                        scriptPattern = Pattern.compile("(alert|confirm|prompt)(.*?)=",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE| Pattern.DOTALL);
                        value = scriptPattern.matcher(value).replaceAll("");
                        **/
                }
                return value;
        }
}
c)、配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>enterprise_security_esapi</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <filter>
    <filter-name>demo1</filter-name>
    <filter-class>com.enterprise.security.XSSFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>demo1</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

2.2.2、HTTP响应输出时转义
Http响应时可能输出在不同位置,分别调用不同ESAPI专义方法:

a)、HTML Escape Before Inserting Untrusted Data into HTML Element Content
输出内容在<body></div>等html元素范围:
<body>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</body>
<div>...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...</div>
对应ESAPI修复方案:
String safe = ESAPI.encoder().encodeForHTML( request.getParameter( "input" ) );

b)、Attribute Escape Before Inserting Untrusted Data into HTML Common Attributes
输出内容在HTML Attributes属性值范围:
<div attr=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...>content</div>     inside UNquoted attribute
<div attr='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'>content</div>   inside single quoted attribute
<div attr="...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">content</div>   inside double quoted attribute
对应ESAPI修复方案:
String safe = ESAPI.encoder().encodeForHTMLAttribute( request.getParameter( "input" ) );

c)、JavaScript Escape Before Inserting Untrusted Data into JavaScript Data Values
输出内容在javascript data value范围:
<script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>  inside a quoted string
<script>x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'</script> one side of a quoted expression
<div onmouseover="x='...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...'"</div>  inside quoted event handler
对应ESAPI修复方案:
String safe = ESAPI.encoder().encodeForJavaScript( request.getParameter( "input" ) );

d)、HTML escape JSON values in an HTML context and read the data with JSON.parse
输出内容为json模式:
{"Message":"No HTTP resource was found that matches the request URI 'dev.net.ie/api/pay/.html?HouseNumber=9&AddressLine
=The+Gardens<script>alert(1)</script>&AddressLine2=foxlodge+woods&TownName=Meath'.","MessageDetail":"No type was foundthat matches the controller named 'pay'."}   <-- this script will pop!!
对应修复建议:
确保返回的Content-Type类型为application /JSON,而不是text / html的。从而确保不执行脚本注入。
  response.setCharacterEncoding("UTF-8");
    response.setContentType("application/json; charset=utf-8");
    //response.setContentType("text/html; charset=utf-8");
    String json = "{\"name\":\"piaox\",\"age\":\"30<script>alert(1)</script>\"}";
  response.getWriter().println(json);

e)、CSS Escape And Strictly Validate Before Inserting Untrusted Data into HTML Style 
输出内容在html style部分:
{ background-url : "javascript:alert(1)"; }  // and all other URLs
 { text-size: "expression(alert('XSS'))"; }   // only in IE

对应修复建议:
String safe = ESAPI.encoder().encodeForCSS( request.getParameter( "input" ) );

f)、URL Escape Before Inserting Untrusted Data into HTML URL Parameter Values
输出内容在html url标签属性部分:
<a href="http://www.somesite.com?test=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">link</a >       
对应修复建议:
String safe = ESAPI.encoder().encodeForURL( request.getParameter( "input" ) );
上传的附件:
2016-8-12 16:08
0
雪    币: 35
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
2017-2-28 17:06
0
游客
登录 | 注册 方可回帖
返回
//