You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Hans Bergsten <ha...@gefionsoftware.com> on 1999/11/06 23:52:57 UTC

DefaultServlet path checks

I believe I've found a solution for checking the path in the DefaultServlet
that works with both 8.3 and long file paths on Windows and symbolic links
on Unix, and still catches all attempts to get to the source of JSP files
or other extension mapped files. I have only tested this on Windows but I
don't see why it shouldn't work on other platforms.

The solution is to always check for ".." and then only compare the file 
extension part of the URI with with the file extension from the canonical 
path. As far as I can tell, that's the only part we need to worry about
since that's where extra characters or mixed case can fool the container
to dispatch to DefaultServlet instead of a servlet mapped to the file
extension. Mixed case in the path itself doesn't really matter; on a case
sensitive platform the file will not be found, on a case insensitive it
will be found but that's okay as long as we catch tricks with the extension
part.

Please take a look at this version of the serveFile() method. If everyone
is happy with it, I will commit it to both the J2EE branch and the main
branch.

    private void serveFile(File file, HttpServletRequest request,
        HttpServletResponse response) throws IOException {

    	String absPath = file.getAbsolutePath();
	    String canPath = file.getCanonicalPath();
		if(absPath.indexOf("..") != -1) {
			// We have .. in the path...
	    	response.sendError(response.SC_NOT_FOUND,
	    	    "File Not Found:<br>" + absPath);
	    	return;
		}

        // Compare the file extension part of the requested name
        // with the "real" extension, to catch attempts to read
        // shtml or jsp source by adding characters or using mixed
        // case extension on Windows platforms
        String reqExt = absPath;
        int dot = absPath.lastIndexOf('.');
        if (dot != -1 && dot < absPath.length() - 1) {
            reqExt = absPath.substring(dot + 1);
        }
        String canExt = canPath;
        dot = canPath.lastIndexOf('.');
        if (dot != -1 && dot < canPath.length() - 1) {
            canExt = canPath.substring(dot + 1);
        }
        if (!canExt.equals(reqExt)) {
	    	response.sendError(response.SC_NOT_FOUND,
	    	    "File Not Found:<br>" + absPath);
	    	return;
        }

    	String mimeType = mimeTypes.getContentTypeFor(file.getName());

    	if (mimeType == null) {
    	    mimeType = "text/plain";
    	}

    	response.setContentType(mimeType);
    	response.setContentLength((int)file.length());
    	response.setDateHeader("Last-Modified", file.lastModified());

    	FileInputStream in = new FileInputStream(file);

    	try {
    	    serveStream(in, request, response);
    	} catch (FileNotFoundException e) {
    	    // Figure out what we're serving

    	    String requestURI = (String)request.getAttribute(
    		Constants.Attribute.RequestURI);

       	    if (requestURI == null) {
    	    	requestURI = request.getRequestURI();
    	    }

    	    response.sendError(response.SC_NOT_FOUND,
                    "File Not Found<br>" + requestURI);
    	} catch (SocketException e) {
    	    return;  // munch
    	} finally {
    	    if (in != null) {
    		in.close();
    	    }
    	}
    }


-- 
Hans Bergsten		hans@gefionsoftware.com
Gefion Software		http://www.gefionsoftware.com

Re: DefaultServlet path checks

Posted by Hans Bergsten <ha...@gefionsoftware.com>.
Harish Prabandham wrote:
> 
> Hi,
> 
> Your fix sounds good... Does it address the case of a file that
> is named:
> 
> foo.Bar.goo and Foo.Bar.GOO & such similar variations....
> 
> .html and .htm variations etc....
> 
> If It does, please commit the changes to the "trunk" only..

I'm not sure I understand what problem you refer to with the above
examples. It does address the case where someone tries to fool the
server to use the DefaultServlet to reveal the source of a JSP page
(or other extension based processing) by using extra characters or
using mixed case in the extension part. I assume that's what you
refer to in the first example.

But what do you mean by ".html and .htm variations etc."? How is
that supposed to be addressed? 

Hans
-- 
Hans Bergsten		hans@gefionsoftware.com
Gefion Software		http://www.gefionsoftware.com

Re: DefaultServlet path checks

Posted by Harish Prabandham <Ha...@eng.sun.com>.
Hi,


Your fix sounds good... Does it address the case of a file that
is named:

foo.Bar.goo and Foo.Bar.GOO & such similar variations....


.html and .htm variations etc....


If It does, please commit the changes to the "trunk" only..


Thanx


Harish


Hans Bergsten wrote:

> Harish Prabandham wrote:
> >
> > Hi Hans,
> >
> > A fix similar to this exists on the J2EE branch. Please do not commit
> > any changes to the branch.
> >
> > Costin has merged the J2EE branch with the trunk, so this change
> > should be available there.... Could you check this out before you
> > commit your changes.
>
> The fix I proposed is not on the J2EE branch. I actually used the J2EE
> branch version as the source for the fix. The current fix tries to
> figure out if we're on a Windows or Unix platform using the file path
> separator character, and then checks the path according to different
> rules depending on platform. Since it uses the canPath != absPath check
> it has problems with non-8.3 paths on a Windows platform.
>
> My proposal is a check that works the same on all platform by only
> checking the critical piece of the path with "abs != can"; the file
> extension part.
>
> Hans
> --
> Hans Bergsten           hans@gefionsoftware.com
> Gefion Software         http://www.gefionsoftware.com
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org


Re: DefaultServlet path checks

Posted by Hans Bergsten <ha...@gefionsoftware.com>.
Harish Prabandham wrote:
> 
> Hi Hans,
> 
> A fix similar to this exists on the J2EE branch. Please do not commit
> any changes to the branch.
> 
> Costin has merged the J2EE branch with the trunk, so this change
> should be available there.... Could you check this out before you
> commit your changes.

The fix I proposed is not on the J2EE branch. I actually used the J2EE
branch version as the source for the fix. The current fix tries to 
figure out if we're on a Windows or Unix platform using the file path
separator character, and then checks the path according to different
rules depending on platform. Since it uses the canPath != absPath check
it has problems with non-8.3 paths on a Windows platform.

My proposal is a check that works the same on all platform by only
checking the critical piece of the path with "abs != can"; the file
extension part.

Hans
-- 
Hans Bergsten		hans@gefionsoftware.com
Gefion Software		http://www.gefionsoftware.com

Re: DefaultServlet path checks

Posted by Harish Prabandham <Ha...@eng.sun.com>.
Hi Hans,


A fix similar to this exists on the J2EE branch. Please do not commit
any changes to the branch.

Costin has merged the J2EE branch with the trunk, so this change
should be available there.... Could you check this out before you
commit your changes.


Thanx


Harish

Hans Bergsten wrote:

> I believe I've found a solution for checking the path in the DefaultServlet
> that works with both 8.3 and long file paths on Windows and symbolic links
> on Unix, and still catches all attempts to get to the source of JSP files
> or other extension mapped files. I have only tested this on Windows but I
> don't see why it shouldn't work on other platforms.
>
> The solution is to always check for ".." and then only compare the file
> extension part of the URI with with the file extension from the canonical
> path. As far as I can tell, that's the only part we need to worry about
> since that's where extra characters or mixed case can fool the container
> to dispatch to DefaultServlet instead of a servlet mapped to the file
> extension. Mixed case in the path itself doesn't really matter; on a case
> sensitive platform the file will not be found, on a case insensitive it
> will be found but that's okay as long as we catch tricks with the extension
> part.
>
> Please take a look at this version of the serveFile() method. If everyone
> is happy with it, I will commit it to both the J2EE branch and the main
> branch.
>
>     private void serveFile(File file, HttpServletRequest request,
>         HttpServletResponse response) throws IOException {
>
>         String absPath = file.getAbsolutePath();
>             String canPath = file.getCanonicalPath();
>                 if(absPath.indexOf("..") != -1) {
>                         // We have .. in the path...
>                 response.sendError(response.SC_NOT_FOUND,
>                     "File Not Found:<br>" + absPath);
>                 return;
>                 }
>
>         // Compare the file extension part of the requested name
>         // with the "real" extension, to catch attempts to read
>         // shtml or jsp source by adding characters or using mixed
>         // case extension on Windows platforms
>         String reqExt = absPath;
>         int dot = absPath.lastIndexOf('.');
>         if (dot != -1 && dot < absPath.length() - 1) {
>             reqExt = absPath.substring(dot + 1);
>         }
>         String canExt = canPath;
>         dot = canPath.lastIndexOf('.');
>         if (dot != -1 && dot < canPath.length() - 1) {
>             canExt = canPath.substring(dot + 1);
>         }
>         if (!canExt.equals(reqExt)) {
>                 response.sendError(response.SC_NOT_FOUND,
>                     "File Not Found:<br>" + absPath);
>                 return;
>         }
>
>         String mimeType = mimeTypes.getContentTypeFor(file.getName());
>
>         if (mimeType == null) {
>             mimeType = "text/plain";
>         }
>
>         response.setContentType(mimeType);
>         response.setContentLength((int)file.length());
>         response.setDateHeader("Last-Modified", file.lastModified());
>
>         FileInputStream in = new FileInputStream(file);
>
>         try {
>             serveStream(in, request, response);
>         } catch (FileNotFoundException e) {
>             // Figure out what we're serving
>
>             String requestURI = (String)request.getAttribute(
>                 Constants.Attribute.RequestURI);
>
>             if (requestURI == null) {
>                 requestURI = request.getRequestURI();
>             }
>
>             response.sendError(response.SC_NOT_FOUND,
>                     "File Not Found<br>" + requestURI);
>         } catch (SocketException e) {
>             return;  // munch
>         } finally {
>             if (in != null) {
>                 in.close();
>             }
>         }
>     }
>
> --
> Hans Bergsten           hans@gefionsoftware.com
> Gefion Software         http://www.gefionsoftware.com
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
> For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org