Skip to content

Commit 7f5ccda

Browse files
tenderloveeileencodesjhawthorn
committed
Fix possible dev mode RCE
If the secret_key_base is nil in dev or test generate a key from random bytes and store it in a tmp file. This prevents the app developers from having to share / checkin the secret key for dev / test but also maintains a key between app restarts in dev/test. [CVE-2019-5420] Co-Authored-By: eileencodes <eileencodes@gmail.com> Co-Authored-By: John Hawthorn <john@hawthorn.email>
1 parent d7fac9c commit 7f5ccda

File tree

4 files changed

+43
-6
lines changed

4 files changed

+43
-6
lines changed

actionpack/lib/action_dispatch/middleware/session/cookie_store.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ module Session
2929
#
3030
# Rails.application.config.session_store :cookie_store, key: '_your_app_session'
3131
#
32-
# By default, your secret key base is derived from your application name in
33-
# the test and development environments. In all other environments, it is stored
34-
# encrypted in the <tt>config/credentials.yml.enc</tt> file.
32+
# In the development and test environments your application's secret key base is
33+
# generated by Rails and stored in a temporary file in <tt>tmp/development_secret.txt</tt>.
34+
# In all other environments, it is stored encrypted in the
35+
# <tt>config/credentials.yml.enc</tt> file.
3536
#
3637
# If your application was not updated to Rails 5.2 defaults, the secret_key_base
3738
# will be found in the old <tt>config/secrets.yml</tt> file.

railties/lib/rails/application.rb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,8 @@ def secrets=(secrets) #:nodoc:
426426
# then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
427427
# the correct place to store it is in the encrypted credentials file.
428428
def secret_key_base
429-
if Rails.env.test? || Rails.env.development?
430-
secrets.secret_key_base || Digest::MD5.hexdigest(self.class.name)
429+
if Rails.env.development? || Rails.env.test?
430+
secrets.secret_key_base ||= generate_development_secret
431431
else
432432
validate_secret_key_base(
433433
ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
@@ -588,6 +588,21 @@ def validate_secret_key_base(secret_key_base)
588588

589589
private
590590

591+
def generate_development_secret
592+
if secrets.secret_key_base.nil?
593+
key_file = Rails.root.join("tmp/development_secret.txt")
594+
595+
if !File.exist?(key_file)
596+
random_key = SecureRandom.hex(64)
597+
File.binwrite(key_file, random_key)
598+
end
599+
600+
secrets.secret_key_base = File.binread(key_file)
601+
end
602+
603+
secrets.secret_key_base
604+
end
605+
591606
def build_request(env)
592607
req = super
593608
env["ORIGINAL_FULLPATH"] = req.fullpath

railties/test/application/configuration_test.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,27 @@ def index
513513
end
514514

515515

516+
test "application will generate secret_key_base in tmp file if blank in development" do
517+
app_file "config/initializers/secret_token.rb", <<-RUBY
518+
Rails.application.credentials.secret_key_base = nil
519+
RUBY
520+
521+
app "development"
522+
523+
assert_not_nil app.secrets.secret_key_base
524+
assert File.exist?(app_path("tmp/development_secret.txt"))
525+
end
526+
527+
test "application will not generate secret_key_base in tmp file if blank in production" do
528+
app_file "config/initializers/secret_token.rb", <<-RUBY
529+
Rails.application.credentials.secret_key_base = nil
530+
RUBY
531+
532+
assert_raises ArgumentError do
533+
app "production"
534+
end
535+
end
536+
516537
test "raises when secret_key_base is blank" do
517538
app_file "config/initializers/secret_token.rb", <<-RUBY
518539
Rails.application.credentials.secret_key_base = nil
@@ -550,7 +571,6 @@ def index
550571

551572
test "application verifier can build different verifiers" do
552573
make_basic_app do |application|
553-
application.credentials.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
554574
application.config.session_store :disabled
555575
end
556576

railties/test/isolation/abstract_unit.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def self.name; "RailtiesTestApp"; end
155155
@app.config.active_support.deprecation = :log
156156
@app.config.active_support.test_order = :random
157157
@app.config.log_level = :info
158+
@app.secrets.secret_key_base = "b3c631c314c0bbca50c1b2843150fe33"
158159

159160
yield @app if block_given?
160161
@app.initialize!

0 commit comments

Comments
 (0)