lizongbo at 618119.com 工作,生活,Android,前端,Linode,Ubuntu,nginx,java,apache,tomcat,Resin,mina,Hessian,XMPP,RPC

2010年12月7日

使用java.net.URL解析校验检查url非法字符时撞上了bug

Filed under: Java — 标签:, , , — lizongbo @ 00:04

..URL对url格式的检查不严格,如果使用java..URL来进行url解析并判断url是否为指定域名时将产生漏洞。

目前一共发现两种情况会解析错误:
1.java.net.URL对url里存在回车符和换行符被认为是合法的:
<%
String goUrl=”http://618119.com/\r\nX-Location: http://www.lizongbo.com/”;
//goUrl=java.net.URLEncoder.encode(goUrl, “UTF-8″);
response.sendRedirect(goUrl);
%>
例如上面的代码即使使用java.net.URL进行解析,也能正常解析,而被认为是个合法的url。
加上reponse.setheader的时候没做参数检查,导致写入了非法的head,这样会导致XSS注入攻击。

2.”http://618119.com#www.lizongbo.com/”
这样的url被java.net.URL解析得到的host是618119.com#www.lizongbo.com,因此按域名后缀判断的话会被误放过,
在浏览器地址栏里实际请求会变成:http://618119.com/#www.lizongbo.com/

这样也会产生非法跳转漏洞。

使用java.net.URI进行解析则不会出现这样的问题。

JDK里的关于java.net.URL里引用的文章连接 为: http://www.socs.uts.edu.au/MosaicDocs-old/url-primer.html,但是这个链接已经失效了。

因此封装下面这个工具类来对url进行检查,避免URL 参数里出现非法字符导致的非法跳转的漏洞:

[code]

package com.lizongbo.net;

import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.;
/**
* 测试url检查是否完善的类
* @author lizongbo
*
*/
public class UrlTest {

/**
* @param args
* @throws MalformedURLException
* @throws UnsupportedEncodingException
* @throws URISyntaxException
*/
public static void main(String[] args) throws MalformedURLException,
UnsupportedEncodingException, URISyntaxException {
String urlStr = “http://618119.com/\r\nX-Location: http://www.lizongbo.com/”;
checkUrl(urlStr);
urlStr = “http://618119.com#www.lizongbo.com/”;
checkUrl(urlStr);
urlStr = “https://www.google.com/reader/view/#stream/feed%2Fhttp%3A%2F%2Fwww.lizongbo.com%2Ffeeds%2Fposts%2Fdefault”;
checkUrl(urlStr);
checkUrl(“file:C:/autoexec.bat”);
checkUrl(“file:/C:/autoexec.bat”);
checkUrl(“file://C:/autoexec.bat”);
checkUrl(“file:///C:/autoexec.bat”);
checkUrl(“/aa.jsp”);
}

private static void checkUrl(String urlStr) {
try {
java.net.URI uri = new URI(urlStr);
dump(uri);
} catch (Exception e) {
e.printStackTrace();
}
try {
java.net.URL url = new URL(urlStr);
dump(url);
} catch (Exception e) {
e.printStackTrace();
}
}

private static void dump(java.net.URL url) {
try {
System.out.println(“url=” + url + “,protocol=” + url.getProtocol()
+ “,host=” + url.getHost() + “,path=” + url.getPath()
+ “,query=” + url.getQuery() + “,ref=” + url.getRef()
+ “,url.toURI=” + url.toURI());
} catch (URISyntaxException e) {
e.printStackTrace();
}
}

private static void dump(java.net.URI uri) {
try {
System.out.println(“uri=” + uri + “,scheme=” + uri.getScheme()
+ “,host=” + uri.getHost() + “,path=” + uri.getPath()
+ “,query=” + uri.getQuery() + “,fragment=”
+ uri.getFragment() + “,uri.toURL=” + uri.toURL());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}

/**
* 使用java.net.URI判断指定的url是否为站内合法的目标地址,对url内容进行严格检查
*
* @param goUrl
* @return
*/
public static boolean verifyURL(String goUrl) {
if (goUrl == null) {
return false;
}
java.net.URI cgoUrl = null;
try {
cgoUrl = new java.net.URI(goUrl);// 不使用java.net.URL,而是改用URI进行校验
} catch (Exception e) {
return false;
}
if (!”http”.equalsIgnoreCase(cgoUrl.getScheme())
&& !”https”.equalsIgnoreCase(cgoUrl.getScheme())) {
return false;
}
String hostString = cgoUrl.getHost();
if (hostString == null) {
return false;
}
hostString = hostString.toLowerCase();
String allowDomains = “.618119.com;.lizongbo.com;.mqq.im;.seotijian.com”;
if (allowDomains.length() > 0) {
String[] domains = allowDomains.split(“;”);
for (int i = 0; i < domains.length; i++) {
String dmTmp = domains[i];
if (dmTmp != null && dmTmp.length() > 0
&& hostString.endsWith(dmTmp)) {
return true;
}
}
}
return false;
}

/**
* 使用判断java.net.URL指定的url是否为站内合法的目标地址,针对特殊url的判断存在bug<br/>
* “http://618119.com/\r\nX-Location: http://www.lizongbo.com/”<br/>
* “http://www.lizongbo.com#618119.com/” 这两种会绕过检查,导致安全漏洞<br/>
*
* @param goUrl
* @return
*/
public static boolean verifyURLOld(String goUrl) {
if (goUrl == null) {
return false;
}
java.net.URL cgoUrl = null;
try {
cgoUrl = new java.net.URL(goUrl);// 改用URI进行校验
} catch (Exception e) {
return false;
}
if (!”http”.equalsIgnoreCase(cgoUrl.getProtocol())
&& !”https”.equalsIgnoreCase(cgoUrl.getProtocol())) {
return false;
}
String hostString = cgoUrl.getHost().toLowerCase();
String allowDomains = “.618119.com;.lizongbo.com;.mqq.im”;
if (allowDomains.length() > 0) {
String[] domains = allowDomains.split(“;”);
for (int i = 0; i < domains.length; i++) {
String dmTmp = domains[i];
if (dmTmp != null && dmTmp.length() > 0
&& hostString.endsWith(dmTmp)) {
return true;
}
}
}
return false;
}
}

[code]

没有评论 »

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

Powered by WordPress