一、 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介绍,一般有的话,都有类似这种参考修复:
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 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("");
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
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" ) );