如何安全地下载文件

How to securely download files

以下方法用于根据用户 param/selection.

下载 YAML 文件

这肯定不安全,因为我可以下载层次结构中的其他 YAML 文件。

def download

    language_code = params[:code]
    send_file(
        "#{Rails.root}/config/locales/#{language_code}.yml",
        filename: "#{language_code}.yml",
        type: "application/yml"
    )

end

我无法控制 params[:code],它本质上是动态的。

如何保护此处易受攻击的 download 方法?

正如评论所暗示的那样,您的第一选择是完全 dis-allow 用户与 language_code 字符串。评论中有许多建议的选项:受限 列表、数据库实现等

另一种选择(尽管考虑到您的限制,这可能无效)是进行长度检查:language_code.length <= 4。根据 wikipedia's list of language codes.

,这假设您的语言代码不超过 4 个字符

作为最后的手段你也可以清理用户输入并清理它,这样文件路径就不能 操纵。 I've written a post about file sanitization functions here。您有两个选择:

  • 白名单,只接受一小部分字符:A-Z, a-z, 0-9
  • 黑名单并剔除危险字符:/ \ ? % * : | " < > . (and space)

在您的情况下(我假设您可以完全控制 config/locals)我将列入白名单。白名单功能是 易于创建:

def sanitize(file_name)
  # Remove any character that aren't 0-9, A-Z, or a-z
  filename.gsub(/[^0-9A-Z]/i, '_')
end

不知道您的语言文件是如何实现的,您可能需要使用下划线以外的字符 _ 用于更换。

为了额外预防,您还可以 pre-check 目录查看文件是否存在,这将防止 路径遍历攻击。像这样:

def valid_path?(filename)
  Dir["#{Rails.root}/config/locales/*"].include?("#{Rails.root}/config/locales/#{filename}")
end

这里的好处是您明确声明该文件必须存在于 config/locales 目录中 在你上菜之前。如果攻击者尝试目录遍历攻击,此函数将 return false。