Skip to content

Compare path elements when checking that a served file is below the base directory - #1218

Open
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:path-safety
Open

Compare path elements when checking that a served file is below the base directory#1218
pjfanning wants to merge 1 commit into
apache:mainfrom
pjfanning:path-safety

Conversation

@pjfanning

Copy link
Copy Markdown
Member

Motivation

checkIsSafeDescendant in FileAndResourceDirectives decides whether a requested file is really below the served directory by comparing the two canonical paths as plain strings:

if (!canonicalFinalPath.startsWith(baseFile.getCanonicalPath))

A string prefix is not a path prefix. With /var/www served, the location /var/www-private/secret satisfies startsWith without being contained in the served directory. The segment filter in safeJoinPaths does not catch this, since no path segment is suspicious — the path only moves out of the directory when getCanonicalPath resolves a symbolic link that points at such a sibling. The file is then served.

Modification

Compare the canonical paths element by element through java.nio.file.Path rather than as strings. The base directory itself still compares as contained, which the directory listing in getFromBrowseableDirectory relies on (its root listing resolves to exactly the base path).

Result

Only files that are genuinely below the served directory are served. A symbolic link into a sibling directory is rejected with the warning that was already there, whatever the sibling is called. Requests that were previously served are unaffected, and the existing symlink and traversal tests still pass.

Tests

  • sbt "http-tests/testOnly org.apache.pekko.http.scaladsl.server.directives.FileAndResourceDirectivesSymlinkSpec" - pass; new test serves through a symlink pointing at a sibling directory named after the served one, and asserts the request is rejected. Verified that it fails without the change (the file is served, handled is true).
  • sbt http-tests/test - pass (1484 tests)
  • sbt http/mimaReportBinaryIssues - pass
  • sbt http/scalafmt http-tests/Test/scalafmt - clean

References

None - tightens the containment check for file and resource directives

…irectory

Motivation:
`checkIsSafeDescendant` compared the canonical location of the requested
file with the canonical path of the served directory as a plain string
prefix. A path such as `/var/www-private/secret` has `/var/www` as a string
prefix without being contained in it, so a symbolic link inside the served
directory that resolves to such a sibling directory passed the check and the
file was served. The segment filter in `safeJoinPaths` does not catch this,
because no path segment is suspicious; only canonicalization moves the
location out of the directory.

Modification:
Compare the two canonical paths element by element via `java.nio.file.Path`
instead of as strings. That keeps the base directory itself accepted, which
the directory listing of `getFromBrowseableDirectory` relies on.

Result:
Only files that are really below the served directory are served, and a
symbolic link to a sibling directory is rejected with the existing warning
regardless of how the sibling is named.

Tests:
- sbt "http-tests/testOnly org.apache.pekko.http.scaladsl.server.directives.FileAndResourceDirectivesSymlinkSpec" - pass, 1 new test that serves a symlink to a sibling directory named after the served one; it fails without the change
- sbt http-tests/test - pass
- sbt http/mimaReportBinaryIssues - pass
- sbt http/scalafmt http-tests/Test/scalafmt - clean

References:
None - tightens the containment check for file and resource directives
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant