【问题标题】:Is it possible to ensure that requests come from a specific domain?是否可以确保请求来自特定域?
【发布时间】:2024-05-18 06:25:02
【问题描述】:

我正在创建一个 Rails 投票站点,它的结果应该非常准确。用户使用 POST 链接投票。我努力确保用户只投票一次,并且确切地知道他们投票的目的。

但我想到,对结果感兴趣的第三方可以在他们自己的网站上发布 POST 链接,指向我的投票路径。他们可能会以这种方式扭曲我的结果,例如添加误导性描述。

有什么方法可以确保请求只能来自我的域?因此,来自不同域的链接不会运行我的控制器中的任何代码。

【问题讨论】:

    标签: ruby-on-rails http url controller request


    【解决方案1】:

    您需要检查各种事项。第一个是request.referer,它将告诉您将链接指向您网站的页面。如果它不是您的网站,您应该拒绝它。

    if URI(request.referer).host != my_host
        raise ArgumentError.new, "Invalid request from external domain"
    end
    

    但是,这只会保护您免受准确填充 HTTP referer 标头的 Web 客户端(浏览器)的影响。这是假设它完全来自网页。例如,有人可以通过电子邮件发送链接,而电子邮件客户端根本不可能提供推荐人。

    在没有referer的情况下,你也可以检查一下:

    if request.referer.blank?
      raise ArgumentError.new, "Invalid request from unknown domain"
    elsif URI(request.referer).host != my_host
      raise ArgumentError.new, "Invalid request from external domain"
    end
    

    使用简单的脚本来欺骗 HTTP 'referer' 也很容易,因此即使您确实获得了一个有效的域,您也需要其他检查以确保它是一个合法的 POST。脚本小子一直在做这种事情,并且使用十几行 Ruby、python、perl、curl 甚至 VBA,您可以模拟“真实用户”的交互。

    您可能想要使用请求/响应密钥机制之类的东西。在这种方法中,从您的网站提供的链接包含一个唯一的密钥(您跟踪该密钥),用于每次访问该页面,并且只有拥有该密钥的人才能投票。

    您如何识别选民也很重要。被动识别技术适用于非关键活动,例如提供广告或提出建议。然而,当在一般人群中使用时,这种方法通常会在相当大的时间内失败。当您还考虑到人们实际上想要 破坏投票活动这一事实时,很容易突然成为每个拥有“击败系统”的好概念和一些空闲时间的人的目标。

    尽早建立尽可能多的安全性,因为您需要的远远超出您的预期。在 2012 年总统选举期间,我被要求预先测试 41 个在线投票网站,并在最初的 24 小时内破解了其中的 39 个(其中 6 个在 1 小时内)。过于谨慎。了解攻击者如何进入,而不仅仅是使用“正常”机制。不要发布有关您正在使用的技术的信息,即使在代码中也是如此。在 HTML 或 Javascript 代码(甚至是 URL 路径名)的任何地方看到“Rails-isms”将立即使攻击者在击败您的安全机制方面获得巨大优势。使用默默无闻的优势,并尽可能使用安全性。

    注意:检查request.referer 就像在银行金库上放了一把挂锁:它会阻止那些容易被劝阻的人,但甚至不会放慢坚定的人的脚步。

    【讨论】:

    • 感谢您的精彩回答!你知道我可以用来了解更多关于你提到的请求/响应密钥机制的来源吗?
    • 正如@DaSourcerer 所指出的,Rails 包含 CSRF 保护,这将有助于防止一个网站发布到另一个网站。 (请为他的答案投票以获取这些重要信息!)。然而,这将阻止随意的跨站点黑客,这对于能够编写脚本来驱动您的站点的攻击者没有多大帮助。我会看看我是否能找到一些好的参考资料。同时,您可以看看mechanize gem 可以做什么——它代表了一种严重的网站黑客工具,人们实际上可能会使用它来自动投票。
    【解决方案2】:

    您在这里试图阻止的基本上是cross-site request forgery。正如迈克尔正确指出的那样,检查Referer 标头不会给您带来任何好处。

    一种流行的对策是为每个用户提供一个单独的一次性令牌,该令牌随每个表单一起发送并存储在用户的会话中。如果在提交时提交的值和存储的值不匹配,则该请求被忽略。幸运的是,RoR 似乎是ship such a feature。看起来确实是单线。

    【讨论】: