What's the best way to defend against a path traversal attack?
Asked Answered
T

3

49

I have a Java server implementation (TFTP if it matters to you) and I'd like to ensure that it's not susceptible to path traversal attacks allowing access to files and locations that shouldn't be available.

My best attempt at defending so far is to reject any entries that match File.isAbsolute() and then rely on File.getCanonicalPath() to resolve any ../ and ./ components out of the path. Finally I ensure that the resulting path is still within the required root directory of my server:

public String sanitize(final File dir, final String entry) throws IOException {
    if (entry.length() == 0) {
        throw new PathTraversalException(entry);
    }

    if (new File(entry).isAbsolute()) {
        throw new PathTraversalException(entry);
    }

    final String canonicalDirPath = dir.getCanonicalPath() + File.separator;
    final String canonicalEntryPath = new File(dir, entry).getCanonicalPath();

    if (!canonicalEntryPath.startsWith(canonicalDirPath)) {
        throw new PathTraversalException(entry);
    }

    return canonicalEntryPath.substring(canonicalDirPath.length());
}

Are there security issues that this misses? Are there better / faster to achieve the same result reliably?

The code needs to work consistently across Windows and Linux.

Tautonym answered 3/3, 2010 at 23:40 Comment(5)
Don't forget to disallow access to the Windows special devices (NUL, COM1, etc.) which are present everywhere in the file system.Rousing
Good thought Heath. The following link appears to have a definitive list of Windows' reserved file names: msdn.microsoft.com/en-us/library/aa365247(VS.85).aspxTautonym
Why can't you configure this on your Apache/IIS server instead?Felike
I really suggest that you write some unit tests for your method and test it with various dangerous input until it returns what you expect it. That's the easiest way to achive piece of mind.Wheatworm
There is a nice answer (using the new Java file IO API) here: https://mcmap.net/q/270662/-filtering-upwards-path-traversal-in-java-or-scala-closedEffluence
P
16

NOTE: This answer to another similar question on StackOverflow may be the best approach.

Otherwise, the following may help. It compares the canonical and absolute paths, and if they differ, then it'll fail. Only tested on a mac/linux system (ie no windows).

This is for the case where you want to allow the user to supply a relative path, not an absolute path, and you don't allow any parent directory references.

public void failIfDirectoryTraversal(String relativePath)
{
    File file = new File(relativePath);

    if (file.isAbsolute())
    {
        throw new RuntimeException("Directory traversal attempt - absolute path not allowed");
    }

    String pathUsingCanonical;
    String pathUsingAbsolute;
    try
    {
        pathUsingCanonical = file.getCanonicalPath();
        pathUsingAbsolute = file.getAbsolutePath();
    }
    catch (IOException e)
    {
        throw new RuntimeException("Directory traversal attempt?", e);
    }


    // Require the absolute path and canonicalized path match.
    // This is done to avoid directory traversal 
    // attacks, e.g. "1/../2/" 
    if (! pathUsingCanonical.equals(pathUsingAbsolute))
    {
        throw new RuntimeException("Directory traversal attempt?");
    }
}
Ploughboy answered 7/1, 2016 at 15:0 Comment(0)
M
2

If you're running this on a unix machine (I'm not sure if windows has something similar, but it might) you'll want to look at chroot. Even if you think you hit all the ways for someone to refer up a few directories, it's nice to have the operating system there enforcing the fact.

(chroot causes '/' to refer to some other directory, so "/" might be "/home/me/project" and "/../../.." is still "/home/me/project".)

EDIT:

There's a chroot system call as well as a chroot command-line tool. I don't know if Java has a native method, but nothing would prevent you from running your server with the command-line tool. This should, of course, be in addition to doing your best to prevent other path manipulations.

Microcyte answered 3/3, 2010 at 23:48 Comment(2)
Actually that's not true, a chroot can be used to put a running process onto a completely indipendant directory tree with its own /home /var /etc... Also directory traversal within a chroot can still lead to nasty attacks, even remote code execution.Uncaused
Your first statement is true, though somewhat misleading. You would generate your own mirror of the root system paths in a subdirectory - everything you need to run, including Java and related libraries. The second statement is strong hyperbole, as it links chroot with the attack by implication. If you have a hole that could lead to any attack, it should be patched as a separate issue. chroot is there to minimize the impact of any potential security concern. In the end, you cannot prove any code to be secure, you can only reduce your attack surface and decrease the potential for damage.Microcyte
C
-3

You could check out the allowed characters in filenames (http://en.wikipedia.org/wiki/Filename) and filter out all non-allowed characters (white listing) and then you could be sure you've got a filename there.

Chaschase answered 4/3, 2010 at 9:55 Comment(1)
Filtering characters does not protect against directory traversal, unless it is OK with you that "." is banned as a character in file names.Danyelldanyelle

© 2022 - 2024 — McMap. All rights reserved.