README.md in symmetric-encryption-0.9.1 vs README.md in symmetric-encryption-1.0.0

- old
+ new

@@ -25,21 +25,30 @@ its source code, or even stored in its source control system. These files can be created, managed and further secured by System Administrators. This prevents developers having or needing to have access to the symmetric encryption keys * The Operating System security features limit access to the Symmetric Encryption key files to System Administrators and the userid under which the Rails application runs. -* The files in which the Symmetric Encryption keys are stored are futher +* The files in which the Symmetric Encryption keys are stored are further encrypted using RSA 2048 bit encryption In order for anyone to decrypt the data being encrypted in the database, they would need access to ALL of the following: * A copy of the files containing the Symmetric Encryption Keys which are secured by the Operating System * The application source code containing the RSA private key to decrypt the above files * The userid and password for the database to copy the encrypted data itself, or an unsecured copy or export of the database contents +A major feature of symmetric encryption is that it makes the encryption and decryption +automatically available when the Rails application is started. This includes all +rake tasks and the Rails console. In this way data can be encrypted or decrypted as +part of any rake task. + +From a security perspective it is important then to properly secure the system so that +no hacker can switch to and run as the rails user and thereby gain access to the +encryption and decryption capabilities + ## Features * Encryption of passwords in configuration files * Encryption of ActiveRecord model attributes by prefixing attributes / column names with encrypted_ @@ -57,107 +66,189 @@ ## Examples ### Encryption Example - SymmetricEncryption.encrypt "Sensitive data" +```ruby +SymmetricEncryption.encrypt "Sensitive data" +``` ### Decryption Example - SymmetricEncryption.decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n" +```ruby +SymmetricEncryption.decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n" +``` -### Validation Example +### ActiveRecord Example - class MyModel < ActiveRecord::Base - validates :encrypted_ssn, :symmetric_encryption => true - end +```ruby +class User < ActiveRecord::Base + # Requires table users to have a column called encrypted_bank_account_number + attr_encrypted :bank_account_number - m = MyModel.new - m.valid? - # => false - m.encrypted_ssn = SymmetricEncryption.encrypt('123456789') - m.valid? - # => true + # Requires table users to have a column called encrypted_social_security_number + attr_encrypted :social_security_number + validates :encrypted_bank_account_number, :symmetric_encryption => true + validates :encrypted_social_security_number, :symmetric_encryption => true +end + +# Create a new user instance assigning a bank account number +user = User.new +user.bank_account_number = '12345' + +# Saves the bank_account_number in the column encrypted_bank_account_number in +# encrypted form +user.save! + +# Short example using create +User.create(:bank_account_number => '12345') +``` + +### Mongoid Example + +To encrypt a field in a Mongoid document, just add ":encrypted => true" at the end +of the field specifier. The field name must currently begin with "encrypted_" + +```ruby +# User model in Mongoid +class User + include Mongoid::Document + + field :name, :type => String + field :encrypted_bank_account_number, :type => String, :encrypted => true + field :encrypted_social_security_number, :type => String, :encrypted => true +end + +# Create a new user document +User.create(:bank_account_number => '12345') + +# When finding a document, always use the encrypted form of the field name +user = User.where(:encrypted_bank_account_number => SymmetricEncryption.encrypt('12345')).first + +# Fields can be accessed using their unencrypted names +puts user.bank_account_number +``` + +### Validation Example + +```ruby +class MyModel < ActiveRecord::Base + validates :encrypted_ssn, :symmetric_encryption => true +end + +m = MyModel.new +m.valid? +# => false +m.encrypted_ssn = SymmetricEncryption.encrypt('123456789') +m.valid? +# => true +``` ### Encrypting Passwords in configuration files Passwords can be encrypted in any YAML configuration file. For example config/database.yml - production: - adapter: mysql - host: db1w - database: myapp_production - username: admin - password: <%= SymmetricEncryption.try_decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n" %> +```yaml +--- +production: + adapter: mysql + host: db1w + database: myapp_production + username: admin + password: <%= SymmetricEncryption.try_decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n" %> +``` Note: Use SymmetricEncryption.try_decrypt method which will return nil if it fails to decrypt the value, which is essential when the encryption keys differ between environments Note: In order for the above technique to work in other YAML configuration files the YAML file must be processed using ERB prior to passing to YAML. For example +```ruby config_file = Rails.root.join('config', 'redis.yml') raise "redis config not found. Create a config file at: config/redis.yml" unless config_file.file? cfg = YAML.load(ERB.new(File.new(config_file).read).result)[Rails.env] raise("Environment #{Rails.env} not defined in redis.yml") unless cfg +``` ### Large File Encryption Example: Read and decrypt a line at a time from a file - SymmetricEncryption::Reader.open('encrypted_file') do |file| - file.each_line do |line| - puts line - end - end +```ruby +SymmetricEncryption::Reader.open('encrypted_file') do |file| + file.each_line do |line| + puts line + end +end +``` Example: Encrypt and write data to a file - SymmetricEncryption::Writer.open('encrypted_file') do |file| - file.write "Hello World\n" - file.write "Keep this secret" - end +```ruby +SymmetricEncryption::Writer.open('encrypted_file') do |file| + file.write "Hello World\n" + file.write "Keep this secret" +end +``` Example: Compress, Encrypt and write data to a file - SymmetricEncryption::Writer.open('encrypted_compressed.zip', :compress => true) do |file| - file.write "Hello World\n" - file.write "Compress this\n" - file.write "Keep this safe and secure\n" - end +```ruby +SymmetricEncryption::Writer.open('encrypted_compressed.zip', :compress => true) do |file| + file.write "Hello World\n" + file.write "Compress this\n" + file.write "Keep this safe and secure\n" +end +``` ### Standalone test Before generating keys we can use SymmetricEncryption in a standalone test environment: - # Use test encryption keys - SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new( - :key => '1234567890ABCDEF1234567890ABCDEF', - :iv => '1234567890ABCDEF', - :cipher => 'aes-128-cbc' - ) - encrypted = SymmetricEncryption.encrypt('hello world') - puts SymmetricEncryption.decrypt(encrypted) +```ruby +# Use test encryption keys +SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new( + :key => '1234567890ABCDEF1234567890ABCDEF', + :iv => '1234567890ABCDEF', + :cipher => 'aes-128-cbc' +) +encrypted = SymmetricEncryption.encrypt('hello world') +puts SymmetricEncryption.decrypt(encrypted) +``` -### Generating encrypted passwords +### Rake Tasks -The following rake task can be used to generate encrypted passwords for the -specified environment +For PCI compliance developers should not be the ones creating or encrypting +passwords. The following rake tasks can be used by system administrators to +generate and encrypt passwords for databases, or external web calls. +It is safe to pass the encrypted password for say MySQL to the developers +who can then put it in the config files which are kept in source control. +Generate a random password and display its encrypted form: + + rake symmetric_encryption:random_password + +Encrypt a known value, such as a password: + + rake symmetric_encryption:encrypt + Note: Passwords must be encrypted in the environment in which they will be used. Since each environment should have its own symmetric encryption keys ## Installation ### Add to an existing Rails project Add the following line to Gemfile - gem 'symmetric-encryption' +```ruby +gem 'symmetric-encryption' +``` Install the Gem with bundler bundle install @@ -242,22 +333,26 @@ one supplied in examples/symmetric-encryption.yml. At application startup, run the code below to initialize symmetric-encryption prior to attempting to encrypt or decrypt any data - require 'symmetric-encryption' - SymmetricEncryption.load!('config/symmetric-encryption.yml', 'production') +```ruby +require 'symmetric-encryption' +SymmetricEncryption.load!('config/symmetric-encryption.yml', 'production') +``` Parameters: * Filename of the configuration file created above * Name of the environment to load the configuration for To manually generate the symmetric encryption keys, run the code below - require 'symmetric-encryption' - SymmetricEncryption.generate_symmetric_key_files('config/symmetric-encryption.yml', 'production') +```ruby +require 'symmetric-encryption' +SymmetricEncryption.generate_symmetric_key_files('config/symmetric-encryption.yml', 'production') +``` Parameters: * Filename of the configuration file created above * Name of the environment to load the configuration for @@ -278,90 +373,92 @@ ### Configuring multiple Symmetric Encryption keys Create a configuration file in config/symmetric-encryption.yml per the following example: - # - # Symmetric Encryption for Ruby - # - --- - # For the development and test environments the test symmetric encryption keys - # can be placed directly in the source code. - # And therefore no RSA private key is required - development: &development_defaults - key: 1234567890ABCDEF1234567890ABCDEF - iv: 1234567890ABCDEF - cipher: aes-128-cbc +```yaml +# +# Symmetric Encryption for Ruby +# +--- +# For the development and test environments the test symmetric encryption keys +# can be placed directly in the source code. +# And therefore no RSA private key is required +development: &development_defaults + key: 1234567890ABCDEF1234567890ABCDEF + iv: 1234567890ABCDEF + cipher: aes-128-cbc - test: - <<: *development_defaults +test: + <<: *development_defaults - production: - # Since the key to encrypt and decrypt with must NOT be stored along with the - # source code, we only hold a RSA key that is used to unlock the file - # containing the actual symmetric encryption key - # - # Sample RSA Key, DO NOT use this RSA key, generate a new one using - # openssl genrsa 2048 - private_rsa_key: | - -----BEGIN RSA PRIVATE KEY----- - MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH - 6DcuFTFcNSSSxG9n4y7tKi755be8N0uwCCuOzvXqfWmXYjbLwK3Ib2vm0btpHyvA - qxgqeJOOCxKdW/cUFLWn0tACUcEjVCNfWEGaFyvkOUuR7Ub9KfhbW9cZO3BxZMUf - IPGlHl/gWyf484sXygd+S7cpDTRRzo9RjG74DwfE0MFGf9a1fTkxnSgeOJ6asTOy - fp9tEToUlbglKaYGpOGHYQ9TV5ZsyJ9jRUyb4SP5wK2eK6dHTxTcHvT03kD90Hv4 - WeKIXv3WOjkwNEyMdpnJJfSDb5oquQvCNi7ZSQIDAQABAoIBAQCbzR7TUoBugU+e - ICLvpC2wOYOh9kRoFLwlyv3QnH7WZFWRZzFJszYeJ1xr5etXQtyjCnmOkGAg+WOI - k8GlOKOpAuA/PpB/leJFiYL4lBwU/PmDdTT0cdx6bMKZlNCeMW8CXGQKiFDOcMqJ - 0uGtH5YD+RChPIEeFsJxnC8SyZ9/t2ra7XnMGiCZvRXIUDSEIIsRx/mOymJ7bL+h - Lbp46IfXf6ZuIzwzoIk0JReV/r+wdmkAVDkrrMkCmVS4/X1wN/Tiik9/yvbsh/CL - ztC55eSIEjATkWxnXfPASZN6oUfQPEveGH3HzNjdncjH/Ho8FaNMIAfFpBhhLPi9 - nG5sbH+BAoGBAOdoUyVoAA/QUa3/FkQaa7Ajjehe5MR5k6VtaGtcxrLiBjrNR7x+ - nqlZlGvWDMiCz49dgj+G1Qk1bbYrZLRX/Hjeqy5dZOGLMfgf9eKUmS1rDwAzBMcj - M9jnnJEBx8HIlNzaR6wzp3GMd0rrccs660A8URvzkgo9qNbvMLq9vyUtAoGBANll - SY1Iv9uaIz8klTXU9YzYtsfUmgXzw7K8StPdbEbo8F1J3JPJB4D7QHF0ObIaSWuf - suZqLsvWlYGuJeyX2ntlBN82ORfvUdOrdrbDlmPyj4PfFVl0AK3U3Ai374DNrjKR - hF6YFm4TLDaJhUjeV5C43kbE1N2FAMS9LYtPJ44NAoGAFDGHZ/E+aCLerddfwwun - MBS6MnftcLPHTZ1RimTrNfsBXipBw1ItWEvn5s0kCm9X24PmdNK4TnhqHYaF4DL5 - ZjbQK1idEA2Mi8GGPIKJJ2x7P6I0HYiV4qy7fe/w1ZlCXE90B7PuPbtrQY9wO7Ll - ipJ45X6I1PnyfOcckn8yafUCgYACtPAlgjJhWZn2v03cTbqA9nHQKyV/zXkyUIXd - /XPLrjrP7ouAi5A8WuSChR/yx8ECRgrEM65Be3qBEtoGCB4AS1G0NcigM6qhKBFi - VS0aMXr3+V8argcUIwJaWW/x+p2go48yXlJpLHPweeXe8mXEt4iM+QZte6p2yKQ4 - h9PGQQKBgQCqSydmXBnXGIVTp2sH/2GnpxLYnDBpcJE0tM8bJ42HEQQgRThIChsn - PnGA91G9MVikYapgI0VYBHQOTsz8rTIUzsKwXG+TIaK+W84nxH5y6jUkjqwxZmAz - r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg== - -----END RSA PRIVATE KEY----- +production: + # Since the key to encrypt and decrypt with must NOT be stored along with the + # source code, we only hold a RSA key that is used to unlock the file + # containing the actual symmetric encryption key + # + # Sample RSA Key, DO NOT use this RSA key, generate a new one using + # openssl genrsa 2048 + private_rsa_key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH + 6DcuFTFcNSSSxG9n4y7tKi755be8N0uwCCuOzvXqfWmXYjbLwK3Ib2vm0btpHyvA + qxgqeJOOCxKdW/cUFLWn0tACUcEjVCNfWEGaFyvkOUuR7Ub9KfhbW9cZO3BxZMUf + IPGlHl/gWyf484sXygd+S7cpDTRRzo9RjG74DwfE0MFGf9a1fTkxnSgeOJ6asTOy + fp9tEToUlbglKaYGpOGHYQ9TV5ZsyJ9jRUyb4SP5wK2eK6dHTxTcHvT03kD90Hv4 + WeKIXv3WOjkwNEyMdpnJJfSDb5oquQvCNi7ZSQIDAQABAoIBAQCbzR7TUoBugU+e + ICLvpC2wOYOh9kRoFLwlyv3QnH7WZFWRZzFJszYeJ1xr5etXQtyjCnmOkGAg+WOI + k8GlOKOpAuA/PpB/leJFiYL4lBwU/PmDdTT0cdx6bMKZlNCeMW8CXGQKiFDOcMqJ + 0uGtH5YD+RChPIEeFsJxnC8SyZ9/t2ra7XnMGiCZvRXIUDSEIIsRx/mOymJ7bL+h + Lbp46IfXf6ZuIzwzoIk0JReV/r+wdmkAVDkrrMkCmVS4/X1wN/Tiik9/yvbsh/CL + ztC55eSIEjATkWxnXfPASZN6oUfQPEveGH3HzNjdncjH/Ho8FaNMIAfFpBhhLPi9 + nG5sbH+BAoGBAOdoUyVoAA/QUa3/FkQaa7Ajjehe5MR5k6VtaGtcxrLiBjrNR7x+ + nqlZlGvWDMiCz49dgj+G1Qk1bbYrZLRX/Hjeqy5dZOGLMfgf9eKUmS1rDwAzBMcj + M9jnnJEBx8HIlNzaR6wzp3GMd0rrccs660A8URvzkgo9qNbvMLq9vyUtAoGBANll + SY1Iv9uaIz8klTXU9YzYtsfUmgXzw7K8StPdbEbo8F1J3JPJB4D7QHF0ObIaSWuf + suZqLsvWlYGuJeyX2ntlBN82ORfvUdOrdrbDlmPyj4PfFVl0AK3U3Ai374DNrjKR + hF6YFm4TLDaJhUjeV5C43kbE1N2FAMS9LYtPJ44NAoGAFDGHZ/E+aCLerddfwwun + MBS6MnftcLPHTZ1RimTrNfsBXipBw1ItWEvn5s0kCm9X24PmdNK4TnhqHYaF4DL5 + ZjbQK1idEA2Mi8GGPIKJJ2x7P6I0HYiV4qy7fe/w1ZlCXE90B7PuPbtrQY9wO7Ll + ipJ45X6I1PnyfOcckn8yafUCgYACtPAlgjJhWZn2v03cTbqA9nHQKyV/zXkyUIXd + /XPLrjrP7ouAi5A8WuSChR/yx8ECRgrEM65Be3qBEtoGCB4AS1G0NcigM6qhKBFi + VS0aMXr3+V8argcUIwJaWW/x+p2go48yXlJpLHPweeXe8mXEt4iM+QZte6p2yKQ4 + h9PGQQKBgQCqSydmXBnXGIVTp2sH/2GnpxLYnDBpcJE0tM8bJ42HEQQgRThIChsn + PnGA91G9MVikYapgI0VYBHQOTsz8rTIUzsKwXG+TIaK+W84nxH5y6jUkjqwxZmAz + r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg== + -----END RSA PRIVATE KEY----- - # List Symmetric Key files in the order of current / latest first - ciphers: - - - # Filename containing Symmetric Encryption Key encrypted using the - # RSA public key derived from the private key above - key_filename: /etc/rails/.rails.key - iv_filename: /etc/rails/.rails.iv + # List Symmetric Key files in the order of current / latest first + ciphers: + - + # Filename containing Symmetric Encryption Key encrypted using the + # RSA public key derived from the private key above + key_filename: /etc/rails/.rails.key + iv_filename: /etc/rails/.rails.iv - # Encryption cipher - # Recommended values: - # aes-256-cbc - # 256 AES CBC Algorithm. Very strong - # Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second - # JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second - # aes-128-cbc - # 128 AES CBC Algorithm. Less strong. - # Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second - # JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second - cipher: aes-256-cbc + # Encryption cipher + # Recommended values: + # aes-256-cbc + # 256 AES CBC Algorithm. Very strong + # Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second + # JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second + # aes-128-cbc + # 128 AES CBC Algorithm. Less strong. + # Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second + # JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second + cipher: aes-256-cbc - - - # OPTIONAL: - # - # Any previous Symmetric Encryption Keys - # - # Only used when old data still exists that requires old decryption keys - # to be used - key_filename: /etc/rails/.rails_old.key - iv_filename: /etc/rails/.rails_old.iv - cipher: aes-256-cbc + - + # OPTIONAL: + # + # Any previous Symmetric Encryption Keys + # + # Only used when old data still exists that requires old decryption keys + # to be used + key_filename: /etc/rails/.rails_old.key + iv_filename: /etc/rails/.rails_old.iv + cipher: aes-256-cbc +``` ## Future Enhancements * Ability to randomly generate a new initialization vector (iv) with every encryption and put the iv in the encrypted data as its header