在 Rails 6 上定义 secret_key_base 的正确方法是啥?

Posted

技术标签:

【中文标题】在 Rails 6 上定义 secret_key_base 的正确方法是啥?【英文标题】:What's the correct way of defining secret_key_base on Rails 6?在 Rails 6 上定义 secret_key_base 的正确方法是什么? 【发布时间】:2020-06-27 08:17:53 【问题描述】:

既然我们拥有每个环境的凭据,那么在 Rails 6 上定义 secret_key_base 的正确方法是什么?

我的环境有变量 SECRET_KEY_BASE 但 Rails 没有选择它。我尝试在config\credentials\production.yml.enc 中定义secret_key_base,但它对Rails.application.credentials.secret_key_base 没有影响

我知道config/secrets.yml

staging:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

有效,但是,这是 Rails 6 的方式吗?

【问题讨论】:

使用 ENV 变量首先会破坏使用加密密钥的所有安全优势。加密的秘密不会像 secrets.yml 那样通过 ERB 运行。 “Rails 6 方式”是首先不使用 ENV var,因为我们知道它们很容易被破坏。 【参考方案1】:

在 Rails 6 中访问和检查secret_key_base 的正确方法不再是:~

Rails.application.credentials.secret_key_base

现在是:

Rails.application.secret_key_base

我不确定这是 Rails 6 还是一直如此。在查看此方法及其实现时,这一点变得非常清楚:

https://github.com/rails/rails/blob/09a2979f75c51afb797dd60261a8930f84144af8/railties/lib/rails/application.rb#L410-L427

# The secret_key_base is used as the input secret to the application's key generator, which in turn
# is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
#
# In development and test, this is randomly generated and stored in a
# temporary file in <tt>tmp/development_secret.txt</tt>.
#
# In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
# then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
# the correct place to store it is in the encrypted credentials file.
def secret_key_base
  if Rails.env.development? || Rails.env.test?
    secrets.secret_key_base ||= generate_development_secret
  else
    validate_secret_key_base(
      ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
    )
  end
end

开发和测试模式都有自己的生成和存储密钥库的方式。对于其他所有内容,它会按顺序从环境、凭据或机密中提取出来。

【讨论】:

此文档令人困惑,因为它说它确实适用于凭据。怎么不是 Rails.application.credentials.secret_key_base?【参考方案2】:

开发中的 Docker 用户可能会在他们的 entrypoint.sh 中考虑这一点:

if [ "$RAILS_ENV" = "development" ]; then
   printf $SECRET_KEY_BASE > ./tmp/development_secret.txt
fi

【讨论】:

我们为什么需要这个? Rails 不是为我们随机生成 ./tmp/development_secret.txt 吗?【参考方案3】:

几天前我已经尝试解决这个问题。

我学到了什么:

第一次尝试

我尝试在每个环境中使用凭据

$ EDITOR=nano rails credentials:edit --environment development

$ EDITOR=nano rails credentials:edit --environment staging

$ EDITOR=nano rails credentials:edit --environment production

我的 creds 文件和密钥放在config/credentials

我直接在那里设置了必要的变量。这是一个可用的解决方案,但是当我们的 devopses 想要使用 helm 配置时,我们在 Kubernetes 集群中的部署遇到了问题。因此,预定义的凭据不适用于这种情况。


第二次尝试

之后,我尝试在凭据文件中使用 ENV 变量。 不幸的是,它也不起作用:

secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>

最后的尝试

最后,我使用默认配置优雅地降级到 gem config,当您将每个环境设置放在那里时:

config/settings.yml
config/settings/development.yml
config/settings/production.yml
config/settings/test.yml

而我的settings.yml 文件仅包含 ENV 变量,如下所示:

secret_key_base: <%= ENV['SECRET_KEY_BASE'] %>

db:
  host: <%= ENV['DB_HOST'] %>
  port: <%= ENV['DB_PORT'] %>
  pool: <%= ENV['DB_POOL'] %>
  user: <%= ENV['DB_USER'] %>
  password: <%= ENV['DB_PASSWORD'] %>
  database: <%= ENV['DB_DATABASE'] %>

...

这是可行的解决方案,但似乎是退步了。

据我所知,我们不能以任何简单方式在凭据中使用 ENV-vars。

【讨论】:

使用 ENV 变量是使用加密密钥的倒退。就像在 secrets.yml 中以明文形式放置凭据一样,是从 ENV vars 退后一步。系统上任何泄露 ENV 的 gem 都会危及它们。 @max 我几乎同意你的观点,但我还不知道,当你需要在服务器端更改(或设置)加密凭证中的某些内容时如何解决任务,如何分离基础设施来自代码。 @cnnr 我认为您可以完全忽略配置/凭据并使用外部工具部署它们。加密应该很容易应用。

以上是关于在 Rails 6 上定义 secret_key_base 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Flask进阶

在 Rails 6 上定义 secret_key_base 的正确方法是啥?

Rails 4:在本地主机上重新验证“无效的站点密钥”

Rails:在 rails 6 上安装和配置引导程序

Flask之session

在标准“生产”或“开发”之外的不同数据库上使用Rails迁移