Don't encrypt the OTP secret
It doesn't add any security to have this encrypted, but it adds complexity for podmins, because they need to backup the key. closes #8014
This commit is contained in:
parent
2d23a2601e
commit
165b8f4f6e
7 changed files with 61 additions and 53 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,7 +11,6 @@ app/assets/images/custom/
|
||||||
# Configuration files
|
# Configuration files
|
||||||
config/diaspora.yml
|
config/diaspora.yml
|
||||||
config/initializers/secret_token.rb
|
config/initializers/secret_token.rb
|
||||||
config/initializers/twofa_encryption_key.rb
|
|
||||||
.bundle
|
.bundle
|
||||||
vendor/bundle/
|
vendor/bundle/
|
||||||
vendor/cache/
|
vendor/cache/
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,10 @@ class User < ApplicationRecord
|
||||||
scope :halfyear_actives, ->(time = Time.now) { logged_in_since(time - 6.month) }
|
scope :halfyear_actives, ->(time = Time.now) { logged_in_since(time - 6.month) }
|
||||||
scope :active, -> { joins(:person).where(people: {closed_account: false}) }
|
scope :active, -> { joins(:person).where(people: {closed_account: false}) }
|
||||||
|
|
||||||
attribute :otp_secret
|
attr_encrypted :otp_secret, if: false, prefix: "plain_"
|
||||||
|
|
||||||
devise :two_factor_authenticatable,
|
devise :two_factor_authenticatable,
|
||||||
:two_factor_backupable,
|
:two_factor_backupable,
|
||||||
otp_secret_encryption_key: AppConfig.twofa_encryption_key,
|
|
||||||
otp_backup_code_length: 16,
|
otp_backup_code_length: 16,
|
||||||
otp_number_of_backup_codes: 10
|
otp_number_of_backup_codes: 10
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
class AddDeviseTwoFactorToUsers < ActiveRecord::Migration[5.1]
|
class AddDeviseTwoFactorToUsers < ActiveRecord::Migration[5.1]
|
||||||
def change
|
def change
|
||||||
add_column :users, :encrypted_otp_secret, :string
|
change_table :users, bulk: true do |t|
|
||||||
add_column :users, :encrypted_otp_secret_iv, :string
|
t.string :encrypted_otp_secret
|
||||||
add_column :users, :encrypted_otp_secret_salt, :string
|
t.string :encrypted_otp_secret_iv
|
||||||
add_column :users, :consumed_timestep, :integer
|
t.string :encrypted_otp_secret_salt
|
||||||
add_column :users, :otp_required_for_login, :boolean
|
t.integer :consumed_timestep
|
||||||
|
t.boolean :otp_required_for_login
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
52
db/migrate/20190511150503_decrypt_two_factor_secret.rb
Normal file
52
db/migrate/20190511150503_decrypt_two_factor_secret.rb
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class DecryptTwoFactorSecret < ActiveRecord::Migration[5.1]
|
||||||
|
class User < ApplicationRecord
|
||||||
|
end
|
||||||
|
|
||||||
|
def up
|
||||||
|
add_column :users, :plain_otp_secret, :string
|
||||||
|
|
||||||
|
key = twofa_encryption_key
|
||||||
|
decrypt_existing_secrets(key) if key
|
||||||
|
|
||||||
|
change_table :users, bulk: true do |t|
|
||||||
|
t.remove :encrypted_otp_secret
|
||||||
|
t.remove :encrypted_otp_secret_iv
|
||||||
|
t.remove :encrypted_otp_secret_salt
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
raise ActiveRecord::IrreversibleMigration
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def twofa_encryption_key
|
||||||
|
if AppConfig.heroku?
|
||||||
|
ENV["TWOFA_ENCRYPTION_KEY"]
|
||||||
|
else
|
||||||
|
key_file = File.expand_path("../../config/initializers/twofa_encryption_key.rb", File.dirname(__FILE__))
|
||||||
|
|
||||||
|
if File.exist? key_file
|
||||||
|
require key_file
|
||||||
|
File.delete(key_file)
|
||||||
|
|
||||||
|
return Diaspora::Application.config.twofa_encryption_key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def decrypt_existing_secrets(key)
|
||||||
|
User.where.not(encrypted_otp_secret: nil).each do |user|
|
||||||
|
user.plain_otp_secret = Encryptor.decrypt(
|
||||||
|
value: user.encrypted_otp_secret.unpack("m").first,
|
||||||
|
key: key,
|
||||||
|
iv: user.encrypted_otp_secret_iv.unpack("m").first,
|
||||||
|
salt: user.encrypted_otp_secret_salt.slice(1..-1).unpack("m").first
|
||||||
|
)
|
||||||
|
user.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -68,24 +68,6 @@ module Configuration
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def twofa_encryption_key
|
|
||||||
if heroku?
|
|
||||||
return ENV["TWOFA_ENCRYPTION_KEY"] if ENV["TWOFA_ENCRYPTION_KEY"]
|
|
||||||
|
|
||||||
warn "FATAL: Running on Heroku with TWOFA_ENCRYPTION_KEY unset"
|
|
||||||
warn " Run heroku config:add TWOFA_ENCRYPTION_KEY=#{SecureRandom.hex(32)}"
|
|
||||||
abort
|
|
||||||
else
|
|
||||||
key_file = File.expand_path(
|
|
||||||
"../config/initializers/twofa_encryption_key.rb",
|
|
||||||
File.dirname(__FILE__)
|
|
||||||
)
|
|
||||||
system "DISABLE_SPRING=1 bin/rake generate:twofa_key" unless File.exist? key_file
|
|
||||||
require key_file
|
|
||||||
Diaspora::Application.config.twofa_encryption_key
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def version_string
|
def version_string
|
||||||
return @version_string unless @version_string.nil?
|
return @version_string unless @version_string.nil?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
namespace :generate do
|
|
||||||
desc "Generates a key for encrypting 2fa tokens"
|
|
||||||
task :twofa_key do
|
|
||||||
path = Rails.root.join("config", "initializers", "twofa_encryption_key.rb")
|
|
||||||
key = SecureRandom.hex(32)
|
|
||||||
File.open(path, "w") do |f|
|
|
||||||
f.write <<~CONF
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
# The 2fa encryption key is used to encrypt users' OTP tokens in the database.
|
|
||||||
|
|
||||||
# You can regenerate this key by running `rake generate:twofa_key`
|
|
||||||
|
|
||||||
# If you change this key after a user has set up 2fa
|
|
||||||
# the users' tokens cannot be recovered
|
|
||||||
# and they will not be able to log in again!
|
|
||||||
|
|
||||||
Diaspora::Application.config.twofa_encryption_key = "#{key}"
|
|
||||||
CONF
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -984,9 +984,7 @@ describe User, :type => :model do
|
||||||
exported_at
|
exported_at
|
||||||
exported_photos_at
|
exported_photos_at
|
||||||
consumed_timestep
|
consumed_timestep
|
||||||
encrypted_otp_secret
|
plain_otp_secret
|
||||||
encrypted_otp_secret_iv
|
|
||||||
encrypted_otp_secret_salt
|
|
||||||
otp_backup_codes
|
otp_backup_codes
|
||||||
otp_required_for_login
|
otp_required_for_login
|
||||||
otp_secret
|
otp_secret
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue