273011 XSS in directory listing
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@166 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
17250b81c0
commit
59a80ea017
|
@ -5,7 +5,7 @@ jetty-7.0.0.M1-SNAPSHOT
|
|||
+ JETTY-695 Handler dump
|
||||
+ Reworked authentication for deferred authentication
|
||||
+ JETTY-983 DefaultServlet generates accept-ranges for cached/gzip content
|
||||
|
||||
+ 273011 JETTY-980 JETTY-992 Security / Directory Listing XSS present
|
||||
|
||||
jetty-7.0.0.M0
|
||||
+ JETTY-496 Support inetd/xinetd through use of System.inheritedChannel()
|
||||
|
|
|
@ -30,8 +30,11 @@ public interface IdentityService
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* Scope the {@link UserIdentity} to a {@link UserIdentity.Scope}.
|
||||
* @param user The current user or null for no user associated.
|
||||
* Associate a user identity with the current thread.
|
||||
* This is called with as a thread enters the
|
||||
* {@link SecurityHandler#handle(String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
|
||||
* method and then again with a null argument as that call exits.
|
||||
* @param user The current user or null for no user to associated.
|
||||
*/
|
||||
void associate(UserIdentity user);
|
||||
|
||||
|
|
|
@ -312,10 +312,8 @@ public class ServletHandler extends AbstractHandler
|
|||
|
||||
// Get the base requests
|
||||
final Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
|
||||
final String old_servlet_name=base_request.getServletName();
|
||||
final String old_servlet_path=base_request.getServletPath();
|
||||
final String old_path_info=base_request.getPathInfo();
|
||||
UserIdentity scoped_identity = null;
|
||||
|
||||
DispatcherType type = base_request.getDispatcherType();
|
||||
Object request_listeners=null;
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
||||
public class DefaultServletTest extends TestCase
|
||||
{
|
||||
private Server server;
|
||||
private LocalConnector connector;
|
||||
private ServletContextHandler context;
|
||||
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
super.setUp();
|
||||
|
||||
server = new Server();
|
||||
server.setSendServerVersion(false);
|
||||
|
||||
connector = new LocalConnector();
|
||||
|
||||
context = new ServletContextHandler();
|
||||
context.setContextPath("/context");
|
||||
context.setWelcomeFiles(new String[] {}); // no welcome files
|
||||
|
||||
server.setHandler(context);
|
||||
server.addConnector(connector);
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception
|
||||
{
|
||||
super.tearDown();
|
||||
|
||||
if (server != null)
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void testListingXSS() throws Exception
|
||||
{
|
||||
ServletHolder defholder = context.addServlet(DefaultServlet.class,"/*");
|
||||
defholder.setInitParameter("dirAllowed","true");
|
||||
defholder.setInitParameter("redirectWelcome","false");
|
||||
defholder.setInitParameter("gzip","false");
|
||||
|
||||
File resBase = new File("src/test/resources");
|
||||
|
||||
assertTrue("resBase.exists",resBase.exists());
|
||||
assertTrue("resBase.isDirectory",resBase.isDirectory());
|
||||
|
||||
String resBasePath = resBase.getAbsolutePath();
|
||||
defholder.setInitParameter("resourceBase",resBasePath);
|
||||
|
||||
StringBuffer req1 = new StringBuffer();
|
||||
req1.append("GET /context/org/mortbay/resource/;<script>window.alert(\"hi\");</script> HTTP/1.1\n");
|
||||
req1.append("Host: localhost\n");
|
||||
req1.append("\n");
|
||||
|
||||
String response = connector.getResponses(req1.toString());
|
||||
|
||||
assertResponseContains("org/mortbay/resource/one/",response);
|
||||
assertResponseContains("org/mortbay/resource/two/",response);
|
||||
assertResponseContains("org/mortbay/resource/three/",response);
|
||||
|
||||
assertResponseNotContains("<script>",response);
|
||||
}
|
||||
|
||||
private void assertResponseNotContains(String forbidden, String response)
|
||||
{
|
||||
int idx = response.indexOf(forbidden);
|
||||
if (idx != (-1))
|
||||
{
|
||||
// Found (when should not have)
|
||||
StringBuffer err = new StringBuffer();
|
||||
err.append("Response contain forbidden string \"").append(forbidden).append("\"");
|
||||
err.append("\n").append(response);
|
||||
|
||||
System.err.println(err);
|
||||
throw new AssertionFailedError(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertResponseContains(String expected, String response)
|
||||
{
|
||||
int idx = response.indexOf(expected);
|
||||
if (idx == (-1))
|
||||
{
|
||||
// Not found
|
||||
StringBuffer err = new StringBuffer();
|
||||
err.append("Response does not contain expected string \"").append(expected).append("\"");
|
||||
err.append("\n").append(response);
|
||||
|
||||
System.err.println(err);
|
||||
throw new AssertionFailedError(err.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -79,6 +79,10 @@ public class URIUtil
|
|||
case '?':
|
||||
case ';':
|
||||
case '#':
|
||||
case '\'':
|
||||
case '"':
|
||||
case '<':
|
||||
case '>':
|
||||
case ' ':
|
||||
buf=new StringBuilder(path.length()<<1);
|
||||
break loop;
|
||||
|
@ -107,6 +111,18 @@ public class URIUtil
|
|||
case '#':
|
||||
buf.append("%23");
|
||||
continue;
|
||||
case '"':
|
||||
buf.append("%22");
|
||||
continue;
|
||||
case '\'':
|
||||
buf.append("%27");
|
||||
continue;
|
||||
case '<':
|
||||
buf.append("%3C");
|
||||
continue;
|
||||
case '>':
|
||||
buf.append("%3E");
|
||||
continue;
|
||||
case ' ':
|
||||
buf.append("%20");
|
||||
continue;
|
||||
|
|
|
@ -429,7 +429,7 @@ public abstract class Resource implements Serializable
|
|||
Arrays.sort(ls);
|
||||
|
||||
String decodedBase = URIUtil.decodePath(base);
|
||||
String title = "Directory: "+decodedBase;
|
||||
String title = "Directory: "+deTag(decodedBase);
|
||||
|
||||
StringBuilder buf=new StringBuilder(4096);
|
||||
buf.append("<HTML><HEAD><TITLE>");
|
||||
|
@ -440,9 +440,9 @@ public abstract class Resource implements Serializable
|
|||
|
||||
if (parent)
|
||||
{
|
||||
buf.append("<TR><TD><A HREF=");
|
||||
buf.append(URIUtil.addPaths(base,"../"));
|
||||
buf.append(">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
|
||||
buf.append("<TR><TD><A HREF=\"");
|
||||
URIUtil.encodePath(buf,URIUtil.addPaths(base,"../"));
|
||||
buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
|
||||
}
|
||||
|
||||
DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||
|
@ -457,9 +457,9 @@ public abstract class Resource implements Serializable
|
|||
|
||||
if (item.isDirectory() && !path.endsWith("/"))
|
||||
path=URIUtil.addPaths(path,URIUtil.SLASH);
|
||||
buf.append(path);
|
||||
URIUtil.encodePath(buf,path);
|
||||
buf.append("\">");
|
||||
buf.append(StringUtil.replace(StringUtil.replace(ls[i],"<","<"),">",">"));
|
||||
buf.append(deTag(ls[i]));
|
||||
buf.append(" ");
|
||||
buf.append("</TD><TD ALIGN=right>");
|
||||
buf.append(item.length());
|
||||
|
@ -473,6 +473,10 @@ public abstract class Resource implements Serializable
|
|||
return buf.toString();
|
||||
}
|
||||
|
||||
private static String deTag(String raw) {
|
||||
return StringUtil.replace( StringUtil.replace(raw,"<","<"), ">", ">");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param out
|
||||
|
|
|
@ -57,6 +57,9 @@ public class URITest extends junit.framework.TestCase
|
|||
URIUtil.encodeString(buf,"foo%23;,:=b a r",";,= ");
|
||||
assertEquals("foo%2523%3b%2c:%3db%20a%20r",buf.toString());
|
||||
|
||||
buf.setLength(0);
|
||||
URIUtil.encodePath(buf,"/context/'list'/\"me\"/;<script>window.alert('xss');</script>");
|
||||
assertEquals("/context/%27list%27/%22me%22/%3B%3Cscript%3Ewindow.alert(%27xss%27)%3B%3C/script%3E", buf.toString());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
Loading…
Reference in New Issue