Tuesday, May 14, 2013

Encrypting Database Passwords In Ruby

Most web applications connect to a database, which means that they need to store the database password in a file. While that file should only be readable by the user running the application server, security can be further improved by encrypting the password in the file. The encryption passphrase is then stored in the code of your application. This means that an attacker would not only have to get access to the password file, but also to the application code to decrypt the password. Here's a simple implementation of AES 256 encryption/decryption in Ruby. I've run it in JRuby using Java 1.7, and it requires you to install "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" for your JRE. On Mac OS, after extracting the zip file I had to copy US_export_policy.jar and local_policy.jar to /Library/Java/JavaVirtualMachines/jdk1.7.0_21.jdk/Contents/Home/jre/lib/security/.

# file_crypto.rb

require 'openssl'

class FileCrypto
  def initialize(passphrase, iv_passphrase, salt)
    @key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(passphrase, salt, 2000, 32)
    @iv = OpenSSL::PKCS5.pbkdf2_hmac_sha1(iv_passphrase, salt, 2000, 16)
  end

  def read(filepath)
    decrypt(IO.read(filepath))
  end

  def write(filepath, plaintext)
    IO.write(filepath, encrypt(plaintext))
  end

  def encrypt(data)
    cipher = OpenSSL::Cipher::AES256.new(:CBC)
    cipher.encrypt
    cipher.key = @key
    cipher.iv = @iv
    cipher.update(data) + cipher.final
  end

  def decrypt(data)
    cipher = OpenSSL::Cipher::AES256.new(:CBC)
    cipher.decrypt
    cipher.key = @key
    cipher.iv = @iv
    cipher.update(data) + cipher.final
  end
end

Then, you can encrypt test password like this:
require_relative 'file_crypto'

fc = FileCrypto.new(
  'j7U2 k92M%qc mw}f;T@6cAlH',
  'cZd !W7Xf2PMQY#Xprt)n',
  '28Ca kDl^vYuO'
)

fc.write('/tmp/encpassword', 'test password')

And decrypt it back like this:
require_relative 'file_crypto'

fc = FileCrypto.new(
  'j7U2 k92M%qc mw}f;T@6cAlH',
  'cZd !W7Xf2PMQY#Xprt)n',
  '28Ca kDl^vYuO'
)

puts fc.read('/tmp/encpassword')

Concurrent Tasks In JRuby

Sometimes you need to execute long running tasks (like database queries on multiple shards) concurrently. JRuby can easily make use of Java's built-in Executor class for this purpose.

# Since java.util.concurrent.ThreadPoolExecutor.submit has multiple signatures,
# we force JRuby to use the one signature we want, and stop a warning from being emitted.
java.util.concurrent.ThreadPoolExecutor.class_eval do
  java_alias :submit, :submit, [java.util.concurrent.Callable.java_class]
end

# Start the executor service.  Note that this won't create any threads right away,
# but will create new threads as the jobs are submitted, until it gets to 5 threads.
# Then it will reuse the threads.
executor = java.util.concurrent.Executors.new_fixed_thread_pool 5

# Submit jobs, get back java.util.concurrent.FutureTask objects.
tasks = [1, 3, 4, 6].map do |s|
  executor.submit do
    # run database query on shard s
    # return query result
  end
end

# We get control back immediately, while the queries are being executed by the thread pool.
# We can do some other things here while the queries run.

# Get the results of all queries and combine them into an array.  FutureTask.get will wait
# for the task to complete if it's not completed yet, so it's safe to call this right away.
# If for some reason we wanted to check if a task is complete, we can call t.done? .
results = tasks.map do |t|
  begin
    t.get
  rescue java.util.concurrent.ExecutionException => e
    # Convert the exception to one actually thrown inside the task.
    raise e.cause.exception
  end
end

# Shut down the thread pool.
executor.shutdown_now