如何使用密码保护我的 /sidekiq 路由(即需要对 Sidekiq::Web 工具进行身份验证)?

Posted

技术标签:

【中文标题】如何使用密码保护我的 /sidekiq 路由(即需要对 Sidekiq::Web 工具进行身份验证)?【英文标题】:How can I password-protect my /sidekiq route (i.e. require authentication for the Sidekiq::Web tool)? 【发布时间】:2012-08-29 05:27:35 【问题描述】:

我在我的 rails 应用程序中使用 sidekiq。 默认情况下,任何人都可以通过在 URL 后附加“/sidekiq”来访问 Sidekiq。 我只想对 sidekiq 部分进行密码保护/身份验证。我该怎么做?

【问题讨论】:

【参考方案1】:

将以下内容放入您的 sidekiq 初始化程序中

require 'sidekiq'
require 'sidekiq/web'

Sidekiq::Web.use(Rack::Auth::Basic) do |user, password|
  # Protect against timing attacks:
  # - See https://codahale.com/a-lesson-in-timing-attacks/
  # - See https://thisdata.com/blog/timing-attacks-against-string-comparison/
  # - Use & (do not use &&) so that it doesn't short circuit.
  # - Use digests to stop length information leaking
  Rack::Utils.secure_compare(::Digest::SHA256.hexdigest(user), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_USER"])) &
  Rack::Utils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_PASSWORD"]))
end

在路由文件中:

mount Sidekiq::Web => '/sidekiq'

【讨论】:

顺便说一句-我将以下内容放在我的 Gemfile 中,这样我就不必在初始化程序和 routes.rb 文件中手动要求 sidekiq 和 sidekiq/web:“gem 'sidekiq', require: ['sidekiq', 'sidekiq/web']" 至少在我运行 Rails 5 的应用程序中,我没有必要用 authenticate 块包装 mount 调用。 如 Sidekiq 项目中所述,您可能希望使用 ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(user), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_USER"])) & ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password), ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_PASSWORD"])) 来比较用户名和密码。 警告!使用== 运算符会使应用程序暴露于定时攻击。任何攻击者最终都可以通过根据请求处理时间逐个猜测每个字符来找出密码是什么。使用ActiveSupport::SecurityUtils.secure_compareDevise.secure_compare 或类似名称。 你不需要authenticate 块。如果您没有使用设计,它会在 rails 6 上出现错误【参考方案2】:

很抱歉迟到了,但Sidekiq's wiki 为 Devise 推荐以下内容:

允许任何经过身份验证的User

# config/routes.rb
authenticate :user do
  mount Sidekiq::Web => '/sidekiq'
end

限制对User.admin?的访问

# config/routes.rb
authenticate :user, lambda  |u| u.admin?  do
  mount Sidekiq::Web => '/sidekiq'
end

This wiki post 还有很多其他的安全方案。

这是使用 Rails 5.1.3、Devise 4.3 和 Sidekiq 5.0 测试的

【讨论】:

感谢汤姆,Sidekiq wiki 上的良好实践链接! 是否有任何文档显示如何对 RSpec 进行测试? @tomAranda 你们都知道如何进行 rspec 测试了吗? 我已经为此编写了一个 rspec,但我不打算在不久的将来。【参考方案3】:

参见https://github.com/mperham/sidekiq/wiki/Monitoring下的“安全”

Sidekiq::Web 使用Rack::Protection 保护您的应用程序免受典型的网络攻击(例如CSRF、XSS 等)。如果发现您的请求不满足安全要求,Rack::Protection 将使您的会话无效并引发Forbidden 错误。一种可能的情况是让您的应用程序在反向代理后面工作,而不是将重要的标头传递给它(X-Forwarded-ForX-Forwarded-Proto)。这种情况和解决办法可以在in this article和issue #2560找到...

【讨论】:

【参考方案4】:

如果您使用的是 Devise(或其他基于 Warden 的身份验证),则可以这样做,假设您的应用中有一个 AdminUser 模型。

# config/routes.rb
# This defines the authentication constraint
constraint = lambda do |request|
               request.env['warden'].authenticate!( scope: :admin_user )
             end

# This mounts the route using the constraint.
# You could use any other path to make it less obvious
constraints constraint do
  mount Sidekiq::Web => '/sidekiq'
end

【讨论】:

我可以把这个约束放在任何其他地方吗?我的意思是不要把它放在 routes.rb【参考方案5】:

如果您正在滚动自己的自定义身份验证,则可以使用文档here 中引用的以下示例。

# lib/admin_constraint.rb
class AdminConstraint
  def matches?(request)
    return false unless request.session[:user_id]
    user = User.find request.session[:user_id]
    user && user.admin?
  end
end

# config/routes.rb
require 'sidekiq/web'
require 'admin_constraint'
mount Sidekiq::Web => '/sidekiq', :constraints => AdminConstraint.new

【讨论】:

【参考方案6】:

接受的答案很好,但我认为它可以更安全地实施,如Sidekiq documentation mentions(在我发布后对其进行了编辑以展示正确的解决方案)。

要保护您的应用免受计时攻击,请使用ActiveSupport::SecurityUtils.secure_compare

见https://codahale.com/a-lesson-in-timing-attacks/ 见https://thisdata.com/blog/timing-attacks-against-string-comparison/

另外,使用&(不要使用&&)以免短路。

最后,使用摘要来阻止长度信息泄露(Active Support 5 中默认为 secure_compare)。

因此,在初始化文件中,通常在 Rails 项目中的 config/initializers/sidekiq.rb 中,根据您的 Active Support/Rails 版本,编写以下内容。

Active Support 5+:感谢Rails PR #24510,传递给secure_compare的参数默认通过Digest::SHA256.hexdigest

require 'active_support/security_utils'
require 'sidekiq'
require 'sidekiq/web'

Sidekiq::Web.use(Rack::Auth::Basic) do |user, password|
  # Protect against timing attacks:
  # - See https://codahale.com/a-lesson-in-timing-attacks/
  # - See https://thisdata.com/blog/timing-attacks-against-string-comparison/
  # - Use & (do not use &&) so that it doesn't short circuit.
  # - Use digests to stop length information leaking
  ActiveSupport::SecurityUtils.secure_compare(user, ENV["SIDEKIQ_ADMIN_USER"]) &
    ActiveSupport::SecurityUtils.secure_compare(password, ENV["SIDEKIQ_ADMIN_PASSWORD"])
end

主动支持 4

require 'active_support/security_utils'
require 'sidekiq'
require 'sidekiq/web'

Sidekiq::Web.use(Rack::Auth::Basic) do |user, password|
  # Protect against timing attacks:
  # - See https://codahale.com/a-lesson-in-timing-attacks/
  # - See https://thisdata.com/blog/timing-attacks-against-string-comparison/
  # - Use & (do not use &&) so that it doesn't short circuit.
  # - Use digests to stop length information leaking
  ActiveSupport::SecurityUtils.secure_compare(
    ::Digest::SHA256.hexdigest(user),
    ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_ADMIN_USER"])
  ) &
    ActiveSupport::SecurityUtils.secure_compare(
      ::Digest::SHA256.hexdigest(password),
      ::Digest::SHA256.hexdigest(ENV["SIDEKIQ_ADMIN_PASSWORD"])
    )
end

【讨论】:

【参考方案7】:

如果您使用Sorcery 进行身份验证,这里是how to use Rails routes constraints 来保护某些路由。


为了冗余,从巫术维基复制到这里:

本教程展示了如何通过 Sorcery gem 使用 Rails 路由约束。感谢@anthonator 编写!

首先,定义将用于所有约束的UserConstraint 模块:

module RouteConstraints::UserConstraint
  def current_user(request)
    User.find_by_id(request.session[:user_id])
  end
end

然后,在定义了该模块后,您可以指定特定的约束类。在这些示例中,第一个路由仅在没有用户登录时才有效,第二个路由仅适用于已登录的管理员用户:

class RouteConstraints::NoUserRequiredConstraint
  include RouteConstraints::UserConstraint

  def matches?(request)
    !current_user(request).present?
  end
end

class RouteConstraints::AdminRequiredConstraint
  include RouteConstraints::UserConstraint

  def matches?(request)
    user = current_user(request)
    user.present? && user.is_admin?
  end
end

最后,您可以将约束添加到config/routes.rb

MyApp::Application.routes.draw do

  # other routes …

  root :to => 'admin#dashboard', :constraints => RouteConstraints::AdminRequiredConstraint.new
  root :to => 'home#welcome', :constraints => RouteConstraints::NoUserRequiredConstraint.new

end

【讨论】:

【参考方案8】:

另一种选择是添加 CanCan 之类的内容和基于角色的特殊访问权限。

【讨论】:

以上是关于如何使用密码保护我的 /sidekiq 路由(即需要对 Sidekiq::Web 工具进行身份验证)?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Sidekiq 运行连续的后台作业?

Rails:如何重启sidekiq?

如何在 Sidekiq 上为 Redis 6 启用 TLS?

将 ActiveJob 与 Sidekiq 一起使用与单独使用 Sidekiq 相比的优势

Rails 6.1:Heroku 上的作业将使用 Async 而不是 Sidekiq

如何将 ApplicationMailer(或 ActionMailer)设置为默认使用 Activejob/sidekiq