我在目录app/assets/downloadables/
中有很多文件,我希望经过身份验证的用户能够在该目录或任何子目录中的params中按名称下载任意文件。
如何清理send_file
的输入以防止用户访问服务器上的任意文件?
目前我正在考虑做这样的事情,但我不相信它是安全的:
DOWNLOADS_ROOT = File.join(Rails.root, "app", "assets", "downloadables")
# e.g. request.path = "/downloads/subdir/file1.pdf"
file = request.path.gsub(/^\/downloads\//,'').gsub('..','').split('/')
# send file /app/assets/downloadables/subdir/file1.pdf
send_file File.join(DOWNLOADS_ROOT, file)
这是否可以充分防止应用程序范围内的任意文件访问,或者是否有更好的改进或不同的方法?
答案 0 :(得分:0)
需要根据链接创建此文件:
应用/控制器/共享/ send_file_inside_trait.rb 强>
module ApplicationController::SendFileInsideTrait
as_trait do
private
def send_file_inside(allowed_path, filename, options = {})
path = File.expand_path(File.join(allowed_path, filename))
if path.match Regexp.new('^' + Regexp.escape(allowed_path))
send_file path, options
else
raise 'Disallowed file requested'
end
end
end
end
在控制器中使用如下:
send_file_inside File.join(Rails.root, 'app', 'assets', 'downloadables'), request.path.gsub(/^\/downloads\//,'').split('/')
魔术发生在将计算出的allowed_path + file_name路径输入expand_path
的情况下,它将删除任何特殊目录浏览字符串并返回绝对路径。然后将已解析的路径与allowed_path进行比较,以确保所请求的文件位于允许的路径和/或子目录中。
请注意,此解决方案需要Modularity gem v2 https://github.com/makandra/modularity