discourse/db/migrate/20191211170000_add_hashed_api_key.rb
David Taylor 4c9ca24ccf
FEATURE: Hash API keys in the database (#8438)
API keys are now only visible when first created. After that, only the first four characters are stored in the database for identification, along with an sha256 hash of the full key. This makes key usage easier to audit, and ensures attackers would not have access to the live site in the event of a database leak.

This makes the merge lower risk, because we have some time to revert if needed. Once the change is confirmed to be working, we will add a second commit to drop the `key` column.
2019-12-12 11:45:00 +00:00

59 lines
1.4 KiB
Ruby

# frozen_string_literal: true
class AddHashedApiKey < ActiveRecord::Migration[6.0]
def up
add_column(:api_keys, :key_hash, :string)
add_column(:api_keys, :truncated_key, :string)
execute(
<<~SQL
UPDATE api_keys
SET truncated_key = LEFT(key, 4)
SQL
)
batch_size = 500
begin
batch = DB.query <<-SQL
SELECT id, key
FROM api_keys
WHERE key_hash IS NULL
LIMIT #{batch_size}
SQL
to_update = []
for row in batch
hashed = Digest::SHA256.hexdigest row.key
to_update << { id: row.id, key_hash: hashed }
end
if to_update.size > 0
data_string = to_update.map { |r| "(#{r[:id]}, '#{r[:key_hash]}')" }.join(",")
DB.exec <<~SQL
UPDATE api_keys
SET key_hash = data.key_hash
FROM (values
#{data_string}
) as data(id, key_hash)
WHERE api_keys.id = data.id
SQL
end
end until batch.length < batch_size
change_column_null :api_keys, :key_hash, false
change_column_null :api_keys, :truncated_key, false
add_index :api_keys, :key_hash
# The key column will be dropped in a post_deploy migration
# But allow it to be null in the meantime
Migration::SafeMigrate.disable!
change_column_null :api_keys, :key, true
Migration::SafeMigrate.enable!
end
def down
raise ActiveRecord::IrreversibleMigration
end
end