How to prevent directory traversal when joining paths in node.js?

Here's is one approach I've used in this situation:

  1. path.normalize() handles all . and .., so you can be sure that if either one is present, it will be at the front of the path.
  2. Remove any ../../ from the front of your path.

So:

var safeSuffix = path.normalize(unsafeSuffix).replace(/^(\.\.(\/|\\|$))+/, '');
var safeJoin = path.join(basePath, safeSuffix);

About your approach: checking the prefix seems like a pretty good idea to me. There are a couple of problems I see with your implementation:

  • You've checked for a prefix without a trailing slash: ../html-other will resolve to public/html-other, which I guess is not what you want.
  • You would run into trouble on Windows systems, where .normalize() would convert / to \, meaning no paths would work.

When I've done prefix-checking (for slightly different situations), here's what I ended up with:

function checkPrefix(prefix, candidate) {
    // .resolve() removes trailing slashes
    var absPrefix = path.resolve(prefix) + path.sep;
    var absCandidate = path.resolve(candidate) + path.sep;
    return absCandidate.substring(0, absPrefix.length) === absPrefix;
}

(Yes, I added path.sep to both, so that the prefix dir itself passes the test.)