From 3794d2728b48df851e6627ec37b38af9caef8297 Mon Sep 17 00:00:00 2001 From: Tom Levy Date: Thu, 21 Dec 2023 02:40:29 +1300 Subject: [PATCH 1/3] Add config.secret_key_base (#195) secret_key_base was added in Rails 4 and replaces secret_token. It is used by CookieStore to encrypt cookies (we are not currently using CookieStore, but we will switch to it in #162). The Rails 4 convention for setting secret_key_base is to use config/secrets.yml and the environment variable SECRET_KEY_BASE (see [1]). We don't use this approach, because secrets.yml will be deprecated in favour of credentials.yml.enc in Rails 5.2 [2] and because setting the environment variable is awkward. Instead, we generate a random secret the first time the app runs and store it in config/secret_key_base.#{env}.txt. We also support the environment variable SECRET_KEY_BASE to match the Rails convention; we don't currently use this environment variable, but it may be useful (e.g. in situations such as build steps where we don't want to generate the file, or in environments such as Docker where environment variables are often more convenient than files). We don't store the secret in the database (which is how the existing secret_token is implemented) because it may be useful to start the app without a database connection. Also, it's probably more secure to keep the secret out of the database (and its backups). This commit also adds a rule to .gitignore to make sure the secrets aren't accidentally committed. [1]: https://guides.rubyonrails.org/v4.1.8/upgrading_ruby_on_rails.html#config-secrets-yml [2]: https://www.github.com/rails/rails/pull/30067 --- .gitignore | 1 + config/initializers/secret_token.rb | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/.gitignore b/.gitignore index 221ad475..8b5ec3b4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ /config/unicorn.yml /config/isolate.yml /config/redis.yml +/config/secret_key_base.*.txt /script/install.cfg /script/update.time /db/backup/ diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 3b514e27..7b86afb2 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -12,3 +12,23 @@ token = Setting.find_by_key("sessions/secret_token").value NZTrain::Application.config.secret_token = token if token && !(token.empty?) end + +# The secret_key_base is used as the input secret to the +# application's key generator, which is used to sign and encrypt +# cookies. +# +# Once we upgrade to Rails 5.2 we might remove this code and instead +# set secret_key_base using config/credentials.yml.enc or using the +# environment variable SECRET_KEY_BASE (which is natively supported +# by the Rails 5.2 method Rails::Application.secret_key_base). +NZTrain::Application.config.secret_key_base ||= ENV["SECRET_KEY_BASE"] +if NZTrain::Application.config.secret_key_base.nil? + # Randomly generate a secret and store it in config/secret_key_base.#{env}.txt + # Code inspired by Rails.application.generate_local_secret + # (https://github.com/rails/rails/blob/v7.1.2/railties/lib/rails/application.rb#L665) + file = Rails.root.join("config/secret_key_base.#{Rails.env}.txt") + if !File.exist?(file) + File.binwrite(file, SecureRandom.hex(64)) + end + NZTrain::Application.config.secret_key_base = File.binread(file).strip +end From 1f71cc811a5f2b5c823fbc56a27555a5058a6fb3 Mon Sep 17 00:00:00 2001 From: Tom Levy Date: Thu, 21 Dec 2023 02:43:52 +1300 Subject: [PATCH 2/3] Remove config.secret_token (#195) It has been replaced by secret_key_base, and doesn't appear to be used anywhere because we are not using CookieStore yet. Also rename the file because it is no longer related to secret_token. The setting sessions/secret_token should eventually be deleted manually using the admin web interface, but note that this setting is still used in config/initializers/devise.rb at the moment. --- .../{secret_token.rb => secret_key_base.rb} | 13 ------------- 1 file changed, 13 deletions(-) rename config/initializers/{secret_token.rb => secret_key_base.rb} (52%) diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_key_base.rb similarity index 52% rename from config/initializers/secret_token.rb rename to config/initializers/secret_key_base.rb index 7b86afb2..14f86ca0 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_key_base.rb @@ -1,18 +1,5 @@ # Be sure to restart your server when you modify this file. -# Your secret key for verifying the integrity of signed cookies. -# If you change this key, all old signed cookies will become invalid! -# Make sure the secret is at least 30 characters and all random, -# no regular words or you'll be exposed to dictionary attacks. -NZTrain::Application.config.secret_token = '9c912a91a69a2b65a0de03c7c96f83c38b90510360c68d98b5997241f19c7fa3529c13b94ca8b01af31e973bc078c1cd6d1514971359b908789a49f28c74946b' - -# if a setting is stored in database, use that secret token instead (because that token is not checked in to the git repository) -# only appears to affect cookie stored sessions (no apparent effect on database stored sessions) -if ActiveRecord::Base.connection.table_exists?(Setting.table_name) && Setting.exists?(:key => "sessions/secret_token") - token = Setting.find_by_key("sessions/secret_token").value - NZTrain::Application.config.secret_token = token if token && !(token.empty?) -end - # The secret_key_base is used as the input secret to the # application's key generator, which is used to sign and encrypt # cookies. From 1b8b2382721f7cef49f80ea37c2cf1c665229960 Mon Sep 17 00:00:00 2001 From: Tom Levy Date: Thu, 21 Dec 2023 18:14:11 +1300 Subject: [PATCH 3/3] Revert "Remove config.secret_token (#195)" I was wrong, we *are* using CookieStore (just not for the session store). We need to keep secret_token for now, to allow automatic migration from the legacy signed-but-not-encrypted cookies to the new store encrypted using secret_key_base. The cookies are used for the "remember me" functionality for example. --- .../{secret_key_base.rb => secret_token.rb} | 13 +++++++++++++ 1 file changed, 13 insertions(+) rename config/initializers/{secret_key_base.rb => secret_token.rb} (52%) diff --git a/config/initializers/secret_key_base.rb b/config/initializers/secret_token.rb similarity index 52% rename from config/initializers/secret_key_base.rb rename to config/initializers/secret_token.rb index 14f86ca0..7b86afb2 100644 --- a/config/initializers/secret_key_base.rb +++ b/config/initializers/secret_token.rb @@ -1,5 +1,18 @@ # Be sure to restart your server when you modify this file. +# Your secret key for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +NZTrain::Application.config.secret_token = '9c912a91a69a2b65a0de03c7c96f83c38b90510360c68d98b5997241f19c7fa3529c13b94ca8b01af31e973bc078c1cd6d1514971359b908789a49f28c74946b' + +# if a setting is stored in database, use that secret token instead (because that token is not checked in to the git repository) +# only appears to affect cookie stored sessions (no apparent effect on database stored sessions) +if ActiveRecord::Base.connection.table_exists?(Setting.table_name) && Setting.exists?(:key => "sessions/secret_token") + token = Setting.find_by_key("sessions/secret_token").value + NZTrain::Application.config.secret_token = token if token && !(token.empty?) +end + # The secret_key_base is used as the input secret to the # application's key generator, which is used to sign and encrypt # cookies.