Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solution for encrypted session data and silenced logs #150

Open
mediafinger opened this issue Nov 28, 2019 · 3 comments
Open

Solution for encrypted session data and silenced logs #150

mediafinger opened this issue Nov 28, 2019 · 3 comments

Comments

@mediafinger
Copy link

Encrypted database sessions

Hey, I would like to share my solution to how I use the gem activerecord-session_store to:

  • store sessions in the database
  • encrypt and decrypt the session data on the fly
  • silence the logs

When you need a very fast solution, you might want to stick to encrypted cookies in the user's browser. The particular use case for this solution is a small administration app that will never have thousands of users, but is used in a security and privacy aware context.

config/initializers/session_store.rb

ActiveRecord::SessionStore::Session.table_name = "sessions"
ActiveRecord::SessionStore::Session.primary_key = "session_id"
ActiveRecord::SessionStore::Session.data_column_name = "data"
ActiveRecord::SessionStore::Session.serializer = :json

ActionDispatch::Session::ActiveRecordStore.session_class = ::Session

Rails.application.config.session_store :active_record_store, key: "_encrypted_session"

app/models/session.rb

class Session < ApplicationRecord
  self.primary_key = :session_id

  around_save :silence_logs

  class << self
    def find_by_session_id(session_id)
      Session.find_or_initialize_by(session_id: session_id)
    end
  end

  def session_id=(sid)
    @session_id = sid || SecureRandom.hex(16)
    super(@session_id)
  end

  def session_id
    read_attribute(:session_id) || @session_id
  end

  def data=(json)
    super(EncryptionService.new(salt: "your salt").encrypt(json))
  end

  def data
    encrypted_data = read_attribute(:data)

    EncryptionService.new(salt: "your salt").decrypt(encrypted_data) unless session_id.nil? || encrypted_data.blank?
    
  # rescue in case the secret changed (no rollover implemented yet)
  # the salt is wrong
  # or some other issue prevented decryption
  # and delete the flawed session data
  rescue ActiveSupport::MessageEncryptor::InvalidMessage
    delete && nil
  end

  private

  # simple, reliable log silencing
  def silence_logs
    Rails.logger.silence do
      yield # saves / updates the session
    end
  end
end

The EncryptionService used in this example is a small class based on ActiveSupport::MessageEncryptor

database schema

The index on the updated_at column is there to delete sessions older than 30 days by running rake db:sessions:trim as a scheduled task.

  create_table "sessions", primary_key: "session_id", id: :string, force: :cascade do |t|
    t.text "data"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["session_id"], name: "index_sessions_on_session_id", unique: true
    t.index ["updated_at"], name: "index_sessions_on_updated_at"
  end

Hope it helps!

By going through the issues here and while trying to implement this solution, I've got the impression that the documentation of this gem is outdated and lacking. Maybe this implementation helps someone to achieve something similar faster than me.

@breim
Copy link

breim commented Jan 22, 2020

Buddy,
You might improve this example, posting this entire class EncryptionService

@mediafinger
Copy link
Author

@breim My implementation of the EncryptionService turned out to be a bit too CPU intense to use it with many concurrent sessions. So, I prefer to not add it, so it won't be blindly copied.

But if you look for examples using ActiveSupport::MessageEncryptor you will get the idea.

@breim
Copy link

breim commented Jan 24, 2020

Hmmm interesting.

Thanks buddy ! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants