From 315e51aafff051b0485b9df5c9c2e31adfcf02f2 Mon Sep 17 00:00:00 2001 From: Chris Gunther Date: Tue, 16 Feb 2021 16:54:09 -0500 Subject: [PATCH] Make encrypting secrets with symmetric-encryption optional Previously it was assumed you were using symmetric-encryption to encrypt the secrets in the database for extra security, however the gem itself wasn't actually required, leading to trouble setting things up for the first time. Now encryption is treated as an optional added layer of security with instructions on how to accomplish it. This should also open the door to using alternate encryption strategies. This should fix #21. --- .circleci/config.yml | 6 --- CHANGELOG.md | 2 + README.md | 44 +++++++++++++++++-- devise_2fa.gemspec | 1 - .../devise_two_factor_generator.rb | 29 ------------ .../mongoid/devise_two_factor_generator.rb | 4 +- spec/dummy/app/models/user.rb | 6 +-- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9086601..39e7cb6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,12 +18,6 @@ build_config: &build_config - ./vendor/bundle key: v1-dependencies-{{ checksum "devise_2fa.gemspec" }}-{{ checksum "Appraisals" }} - - run: - name: Setup Symmetric Encryption Config - command: | - cd spec/dummy/ - symmetric-encryption -g - - run: name: Run Tests command: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 924d1ab..b94a2d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- *Possibly Breaking*: Remove assumption of using symmetric-encryption gem and replace with optional instructions for how to encrypt secrets. For existing applications, you'll likely need to add `gem 'symmetric-encryption'` to your Gemfile if it's not already there. ## [0.4.1] - 2019-10-31 ### Changed diff --git a/README.md b/README.md index c35bb9b..8f2b20c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ Devise TwoFactor implements two-factor authentication for Devise, using an rfc6238 compatible Time-Based One-Time Password Algorithm. * Uses [rotp](https://github.com/mdp/rotp) for the generation and verification of codes. * Uses [RQRCode](https://github.com/whomwah/rqrcode) to generate QR Code PNGs. -* Uses [SymmetricEncryption](https://github.com/rocketjob/symmetric-encryption) to encrypt secrets and recovery codes. It currently has the following features: @@ -22,8 +21,6 @@ Compatible token devices are: ## Installation -Setup the symmetric-encryption gem by following the steps on the (configuration page)[http://rocketjob.github.io/symmetric-encryption/configuration.html] - Add this line to your application's Gemfile: gem 'devise' @@ -103,7 +100,46 @@ The install generator adds some options to the end of your Devise config file (c * `config.otp_trust_persistence` - The user is allowed to set his browser as "trusted", no more OTP challenges will be asked for that browser, for a limited time. (default: `1.month`, set to false to disable setting the browser as trusted) * `config.otp_issuer` - The name of the token issuer, to be added to the provisioning url. Display will vary based on token application. (defaults to the Rails application class) -### Development +## Encrypting Secrets + +It's best practice to encrypt the secrets stored in the database for authentication and recovery so they can't be exploted should your database be breached. You can use a gem like [SymmetricEncryption](https://github.com/rocketjob/symmetric-encryption) to seamlessly handle the encryption. + +The following instructions assume you're starting from scratch and don't have any existing two-factor authentication data. + +Add this line to your application's Gemfile: + + gem 'symmetric-encryption' + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install symmetric-encryption + +Next, setup the symmetric-encryption gem by following the steps on the (configuration page)[http://rocketjob.github.io/symmetric-encryption/configuration.html] + +Finally, modify your model to specify which attributes to encrypt. For Rails 5+, add the following to your model: + + attribute :otp_auth_secret, :encrypted + attribute :otp_recovery_secret, :encrypted + +For Rails 3/4, add the following to your model: + + attr_encrypted :otp_auth_secret + attr_encrypted :otp_recovery_secret + validates :otp_auth_secret, symmetric_encryption: true + validates :otp_recovery_secret, symmetric_encryption: true + +And rename the `otp_auth_secret` and `otp_recovery_secret` columns in your database to `encrypted_otp_auth_secret` and `encrypted_otp_recovery_secret`, respectively. + +For Mongoid, replace your `otp_auth_secret` and `otp_recovery_secret` fields with the following: + + field :encrypted_otp_auth_secret, type: String, encrypted: true + field :encrypted_otp_recovery_secret, type: String, encrypted: true + +## Development Set up the gem for development with `bin/setup`. diff --git a/devise_2fa.gemspec b/devise_2fa.gemspec index 51ea539..31c8cf2 100644 --- a/devise_2fa.gemspec +++ b/devise_2fa.gemspec @@ -23,7 +23,6 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency 'devise', '~> 4.6' gem.add_runtime_dependency 'rotp', '~> 5.1' gem.add_runtime_dependency 'rqrcode', '~> 0.10.1' - gem.add_runtime_dependency 'symmetric-encryption', '~> 4.3.0' gem.add_development_dependency 'appraisal' gem.add_development_dependency 'capybara' diff --git a/lib/generators/active_record/devise_two_factor_generator.rb b/lib/generators/active_record/devise_two_factor_generator.rb index 70e4b7f..bbcbebc 100755 --- a/lib/generators/active_record/devise_two_factor_generator.rb +++ b/lib/generators/active_record/devise_two_factor_generator.rb @@ -8,35 +8,6 @@ class DeviseTwoFactorGenerator < ActiveRecord::Generators::Base def copy_devise_migration migration_template 'migration.rb', "db/migrate/devise_two_factor_add_to_#{table_name}.rb" end - - def inject_field_types - class_path = if namespaced? - class_name.to_s.split("::") - else - [class_name] - end - - inject_into_class(model_path, class_path.last, content) if model_exists? - end - - def content -<