我在我的Rails应用程序中使用Sidekiq。
默认情况下,只要在URL后面添加“/sidekiq”,任何人都可以访问Sidekiq。
我想对/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'
很抱歉晚到了,但是Sidekiq的维基推荐以下方法来支持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
这篇维基文章还介绍了许多其他安全方案。
此代码已在Rails 5.1.3、Devise 4.3和Sidekiq 5.0上进行过测试。
Forbidden
错误。其中一个可能的情况是您的应用程序在反向代理后面工作,但没有传递重要的标头(X-Forwarded-For
,X-Forwarded-Proto
)。有关该情况及其解决方案,请参见此文章和问题#2560...如果你正在自行编写自定义身份验证,那么你可以使用文档中此处引用的以下示例。
# 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
sidekiq/stats
时进行额外的查询,因为目前这种情况经常发生。 - csalmeida如果您正在使用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
ActiveSupport::SecurityUtils.secure_compare
。&
(不要使用&&
),以避免短路。secure_compare
的默认值)。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
Active Support 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
这里复制了巫术维基的内容以备不时之需:
本教程展示了如何使用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
#config/initializers/sidekiq.rb
require "sidekiq/web"
Sidekiq::Web.use Rack::Auth::Basic do |username, password|
User.find_by(username: username)&.valid_password?(password)
end
#config/routes.rb
mount Sidekiq::Web => "/sidekiq"
mount
调用包装在authenticate
块中。 - stephen.hansonActiveSupport::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"]))
来比较用户名和密码。 - Capripot==
运算符会使应用程序容易受到时序攻击。攻击者可以根据请求处理时间逐个猜测每个字符,最终找出密码。请使用ActiveSupport::SecurityUtils.secure_compare
、Devise.secure_compare
或类似功能的函数。 - Nowakerauthenticate
区块。如果您没有使用 Devise,在 Rails 6 上会出现错误。 - The Code