Only follow symlinks within configured static file handler directory

This adds an additional check to only follow symlinks that are within
the configured public directory of a static file handler.

This ensures a malicious user cannot link to any files outside of the
public directory to prevent reading arbitrary files.
This commit is contained in:
Alexander Reelsen 2019-07-29 23:01:26 +02:00
parent 6e2714404d
commit b86d761242
2 changed files with 37 additions and 0 deletions

View file

@ -150,4 +150,34 @@ describe Kemal::StaticFileHandler do
response = handle HTTP::Request.new("GET", "/dir/index.html") response = handle HTTP::Request.new("GET", "/dir/index.html")
response.headers["Access-Control-Allow-Origin"].should eq("*") response.headers["Access-Control-Allow-Origin"].should eq("*")
end end
it "should not follow symlinks outside of the configured directory" do
tempfile = File.tempfile("symlink", "txt")
symlink_path = "#{__DIR__}/static/dir/symlink.txt"
File.write tempfile.path, "my_super_secret"
begin
File.symlink(tempfile.path, symlink_path)
response = handle HTTP::Request.new("GET", "/dir/symlink.txt")
response.body.should_not contain("my_super_secret")
response.status_code.should eq(404)
ensure
File.delete symlink_path
tempfile.delete
end
end
it "should follow symlinks inside of the configured directory" do
symlink_path = "#{__DIR__}/static/dir/symlink.txt"
begin
File.symlink("#{__DIR__}/static/dir/test.txt", symlink_path)
response = handle HTTP::Request.new("GET", "/dir/symlink.txt")
response.status_code.should eq(200)
response.body.should eq(File.read("#{__DIR__}/static/dir/test.txt"))
ensure
File.delete symlink_path
end
end
end end

View file

@ -39,6 +39,13 @@ module Kemal
end end
file_path = File.join(@public_dir, expanded_path) file_path = File.join(@public_dir, expanded_path)
# prevent symlinks out of the public dir
if File.symlink?(file_path) && !File.real_path(file_path).starts_with?(@public_dir)
call_next(context)
return
end
is_dir = Dir.exists? file_path is_dir = Dir.exists? file_path
if request_path != expanded_path if request_path != expanded_path