{"id":13878488,"url":"https://github.com/mrrooijen/attr_encrypter","last_synced_at":"2025-10-10T20:52:03.933Z","repository":{"id":62553703,"uuid":"275633024","full_name":"mrrooijen/attr_encrypter","owner":"mrrooijen","description":"Encrypts/Decrypts, with key rotation, attributes on classes, using RbNaCl (libsodium).","archived":false,"fork":false,"pushed_at":"2020-06-28T23:03:41.000Z","size":11,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-24T19:49:18.543Z","etag":null,"topics":["activerecord","encryption","ruby","standalone"],"latest_commit_sha":null,"homepage":"https://github.com/mrrooijen/attr_encrypter","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mrrooijen.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-28T17:17:35.000Z","updated_at":"2022-06-03T11:04:41.000Z","dependencies_parsed_at":"2022-11-03T04:45:34.788Z","dependency_job_id":null,"html_url":"https://github.com/mrrooijen/attr_encrypter","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrrooijen%2Fattr_encrypter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrrooijen%2Fattr_encrypter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrrooijen%2Fattr_encrypter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrrooijen%2Fattr_encrypter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrrooijen","download_url":"https://codeload.github.com/mrrooijen/attr_encrypter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226138849,"owners_count":17579496,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activerecord","encryption","ruby","standalone"],"created_at":"2024-08-06T08:01:51.350Z","updated_at":"2025-10-10T20:51:58.900Z","avatar_url":"https://github.com/mrrooijen.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# AttrEncrypter\n\n[![Gem Version](https://badge.fury.io/rb/attr_encrypter.svg)](https://badge.fury.io/rb/attr_encrypter)\n[![Test Status](https://github.com/mrrooijen/attr_encrypter/workflows/Test/badge.svg)](https://github.com/mrrooijen/attr_encrypter/actions)\n\nEncrypts/Decrypts, with key rotation, attributes on classes, using [RbNaCl] ([libsodium]).\n\nThis library was developed for- and extracted from [HireFire].\n\nThe documentation can be found on [RubyDoc].\n\n\n### Compatibility\n\n- Ruby 2.5+\n- RbNaCl 7.0+\n\n\n### Installation\n\nEnsure that you've installed [libsodium], then add the gem to your Gemfile and run `bundle`.\n\n```ruby\ngem \"attr_encrypter\", \"~\u003e 1\"\n```\n\n\n### Example\n\n```ruby\nclass Account\n  include AttrEncrypter::Accessors\n\n  # Defines the token accessor that seamlessly encrypts and decrypts\n  # data stored in @token_digest.\n  #\n  # Note: This won't define a @token instance variable, as state is\n  #       managed by the token_digest accessor using @token_digest.\n  #\n  # Note: You can add multiple attributes in a single attr_encrypter call.\n  #       e.g. `attr_encrypter ENV[\"KEYCHAIN\"], :attr_a, :attr_b, :attr_c`\n  #\n  attr_encrypter ENV[\"KEYCHAIN\"], :token\n\n  # Defines the token_digest accessor for storing and retrieving encrypted data.\n  # You don't directly use this accessor. Both read and write operations should\n  # always go through the token/token= methods provided by the attr_encrypter method.\n  #\n  # Consider making this accessor private. We'll leave it public for this demo.\n  #\n  attr_accessor :token_digest\nend\n```\n\nThe `KEYCHAIN` environment variable must contain one or more keys. To generate a key, use the following function.\n\n```ruby\nAttrEncrypter::Generator.generate_key\n```\n\nPlace the generated key in the `KEYCHAIN` environment variable.\n\nThe key consists of two segments, `\u003cversion\u003e.\u003csecret\u003e`. The initial key has a version of 1. Versions determine which secrets will be used to encrypt and decrypt data.\n\nWith the key in place you'll be able to store and retrieve data using the dynamically defined `token` accessor. Data assigned with `token=` will automatically be encrypted and stored with `token_digest=` in `@token_digest`.\n\n```ruby\naccount                 = Account.new\naccount.token           = \"my_secret_token\"\naccount.token_digest # =\u003e \"1.yCZGH0QEk8+OPT0ad29wVmCkvr/7NXZjZtxu23v2j9HKfehndH0qv9MsF4ME\\n\"\naccount.token        # =\u003e \"my_secret_token\"\n\naccount.instance_variable_get(\"@token\")        # =\u003e nil\naccount.instance_variable_get(\"@token_digest\") # =\u003e \"1.yCZGH0QEk8+OPT0ad29wVmCkvr/7NXZjZtxu23v2j9HKfehndH0qv9MsF4ME\\n\"\n```\n\nThe `@token_digest` value, like the keychain, also consists of two segments, `\u003cversion\u003e.\u003cdata\u003e`. The version matches the key version used to encrypt the data. This way, whenever data is accessed via `token`, it knows which secret to use to decrypt the data.\n\nTo clear the token, simply assign `nil` to it.\n\n```ruby\naccount.token           = nil\naccount.token_digest # =\u003e nil\n```\n\n\n### Active Record\n\nThis is a general purpose library, and while its only hard dependency is rbnacl (libsodium), it was designed to work seamlessly with Active Record.\n\n```ruby\nclass User \u003c ActiveRecord::Base\n  include AttrEncrypter::Accessors\n  attr_encrypter ENV[\"KEYCHAIN\"], :token\nend\n```\n\nThis assumes that you have a users table in the database, containing a column named `token_digest` of type text.\n\n\n### Key Rotation\n\nYou'll want to rotate your keys at some point.\n\nFirst, add a second key to the keychain (`ENV[\"KEYCHAIN\"]` in this case).\n\nTo generate version 2 you can again use the following function, but this time using the version argument.\n\n```ruby\nAttrEncrypter::Generator.generate_key 2 # or 3, 4, 5...\n```\n\nThe keychain format allows any kind of whitespace to delimit keys.\n\n```ruby\nENV[\"KEYCHAIN\"] = \"\u003cversion\u003e.\u003csecret\u003e \u003cversion\u003e.\u003csecret\u003e \u003cversion\u003e.\u003csecret\u003e\"\n```\n\nFor example.\n\n```ruby\nENV[\"KEYCHAIN\"] = \u003c\u003c-EOS\n  1.c1dbb0dd094d4f40916c9cc0d8c974151949f105c499366b2acd9da76de7e5e9\n  2.3054563b2d80ae13c6e115405d6263e70be004f81eb3982f4a61ff9e9821e7d4\nEOS\n```\n\nThen simply reassign the token to re-encrypt the token with key version 2.\n\n```ruby\nUser.find_each do |user|\n  user.token = user.token\n  user.save\nend\n```\n\nThe `user.token` (reader) decrypts the existing encrypted token from `token_digest` back to its unencrypted form using key version 1. It then uses the highest key version in the keychain, version 2, to re-encrypt the token with `user.token=` which overwrites the key version 1-encrypted token in `token_digest` with the key version 2-encrypted token. We then persist the key version 2-encrypted token to the database.\n\n\n#### Quick Facts\n\n* The order of the keys in the keychain is irrelevant.\n* The key with the highest version in the keychain is always used for encryption.\n* The key version used to encrypt data is stored along with the encrypted data (`\u003cversion\u003e.\u003csecret\u003e`).\n* The version of the encrypted data determines which key's secret to use in order to decrypt data.\n* Keys can be safely removed from the keychain when none of the encrypted data requires it for decryption.\n* It's recommended that you increment key versions by 1 when generating a new key.\n\n\n### Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/mrrooijen/attr_encrypter.\n\nFirst, install [libsodium] on your machine, and then install the remaining development dependencies:\n\n```\n$ bundle\n```\n\nTo open an interactive console:\n\n```\n$ bundle console\n```\n\nTo run the tests:\n\n```\n$ bundle exec rake\n```\n\nTo view the code coverage (generated after each test run):\n\n```\n$ open coverage/index.html\n```\n\nTo run the local documentation server:\n\n```\n$ bundle exec rake doc\n```\n\nTo build a gem:\n\n```\n$ bundle exec rake build\n```\n\nTo build and install a gem on your local machine:\n\n```\n$ bundle exec rake install\n```\n\nFor a list of available tasks:\n\n```\n$ bundle exec rake --tasks\n```\n\n\n### Author / License\n\nReleased under the [MIT License] by [Michael van Rooijen].\n\n[Michael van Rooijen]: https://michael.vanrooijen.io\n[HireFire]: https://www.hirefire.io\n[RubyDoc]: https://rubydoc.info/github/mrrooijen/attr_encrypter/master\n[MIT License]: https://github.com/mrrooijen/attr_encrypter/blob/master/LICENSE.txt\n[RbNaCl]: https://github.com/RubyCrypto/rbnacl\n[libsodium]: https://doc.libsodium.org/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrrooijen%2Fattr_encrypter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrrooijen%2Fattr_encrypter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrrooijen%2Fattr_encrypter/lists"}