From 2f8834c49f7223dee05bd4fefa50747a0ee3f98f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 18 Oct 2022 11:54:20 +0100 Subject: [PATCH] release latest version of all dependencies as v2.10.0 see: https://github.com/dwyl/fields/issues/116 --- README.md | 209 ++++++++++++++++++++++++++---------------------------- mix.exs | 7 +- mix.lock | 2 +- 3 files changed, 104 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 325e3e6..0970fa2 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ # Fields A **collection of frequently used fields** implemented as custom **`Ecto` types**
-with -[validation](https://hexdocs.pm/fields/Fields.Validate.html), -[sanitising](https://hexdocs.pm/fields/Fields.HtmlBody.html) -and +with +[validation](https://hexdocs.pm/fields/Fields.Validate.html), +[sanitising](https://hexdocs.pm/fields/Fields.HtmlBody.html) +and [encryption](https://hexdocs.pm/fields/Fields.AES.html) -/ +/ [hashing](https://hexdocs.pm/fields/Fields.Password.html) to **build `Phoenix` Apps _much_ faster!** 🚀
+ @@ -40,19 +42,18 @@ with built-in validation, sanitising and security. **`Fields`** makes defining Ecto Schemas faster and more precise. - # _What_? 💭 An Elixir package that helps you add popular custom types to your Phoenix/Ecto schemas so you can build apps faster! > **@dwyl** we are firm believers that personal data -(_Personally Identifiable Information_ (PII)) should be encrypted "at rest" -i.e. all "user" data should be encrypted _before_ being stored in the database. -This project makes hashing, encryption and _decryption_ for secure data storage -_much_ easier for everyone. +> (_Personally Identifiable Information_ (PII)) should be encrypted "at rest" +> i.e. all "user" data should be encrypted _before_ being stored in the database. +> This project makes hashing, encryption and _decryption_ for secure data storage +> _much_ easier for everyone. -> This package was born out of our research +> This package was born out of our research > into the best/easiest way to encrypt data in **`Phoenix`**: > [dwyl/phoenix-ecto-encryption-example](https://github.com/dwyl/phoenix-ecto-encryption-example) @@ -62,15 +63,14 @@ This module is for people building Elixir/Phoenix apps who want to ship _simpler_ and more maintainable code. > We've attempted to make **`Fields`** -as **beginner-friendly** as possible.
-If you get stuck using it or anything is unclear, please ask for -[help!](https://github.com/dwyl/fields/issues) +> as **beginner-friendly** as possible.
+> If you get stuck using it or anything is unclear, please ask for +> [help!](https://github.com/dwyl/fields/issues) # _How_? ✅ Start using **`Fields`** in your Phoenix App today with these 3 easy steps: - ## 1. Add the `fields` hex package to `deps` in `mix.exs` 📦 Add the `fields` package to your list of dependencies in your `mix.exs` file: @@ -78,7 +78,7 @@ Add the `fields` package to your list of dependencies in your `mix.exs` file: ```elixir def deps do [ - {:fields, "~> 2.9.1"} + {:fields, "~> 2.10.0"} ] end ``` @@ -86,7 +86,6 @@ end Once you have saved the `mix.exs` file, run **`mix deps.get`** in your terminal to download. - ## 2. Ensure you have the necessary environment variables 🔑 In order to use Encryption and Hashing, @@ -103,18 +102,16 @@ If you need to create a secure `SECRET_KEY_BASE` value, please see: And for `ENCRYPTION_KEYS`, see: [How to create encryption keys](https://github.com/dwyl/phoenix-ecto-encryption-example#how-to-generate-aes-encryption-keys) - > In our case we use a **`.env`** file -to manage our environment variables. -See: -[github.com/dwyl/**learn-environment-variables**](https://git.io/JeMLg)
-This allows us to securely manage our secret keys in dev -without the risk of accidentally publishing them on Github.
-When we _deploy_ our Apps, we use our service provider's -built-in key management service to securely store Environment Variables. -e.g: -[Environment Variables on Heroku](https://github.com/dwyl/learn-environment-variables#environment-variables-on-heroku) - +> to manage our environment variables. +> See: +> [github.com/dwyl/**learn-environment-variables**](https://git.io/JeMLg)
+> This allows us to securely manage our secret keys in dev +> without the risk of accidentally publishing them on Github.
+> When we _deploy_ our Apps, we use our service provider's +> built-in key management service to securely store Environment Variables. +> e.g: +> [Environment Variables on Heroku](https://github.com/dwyl/learn-environment-variables#environment-variables-on-heroku) ## 3. Apply the relevant field(s) to your schema 📝 @@ -183,39 +180,39 @@ e.g: ## Available `Fields` 📖 -+ [`Address`](lib/address.ex) - an address for a physical location. -Validated and stored as a (`plaintext`) `String`. -+ [`AddressEncrypted`](lib/address_encrypted.ex) - an address for a customer -or user which should be stored encrypted for data protection. -+ [`DescriptionPlaintextUnlimited`](lib/description_plaintext_unlimited.ex) - -filters any HTML/JS to avoid security issues. Perfect for blog post comments. -+ [`Encrypted`](lib/encrypted.ex) - a general purpose encrypted field. +- [`Address`](lib/address.ex) - an address for a physical location. + Validated and stored as a (`plaintext`) `String`. +- [`AddressEncrypted`](lib/address_encrypted.ex) - an address for a customer + or user which should be stored encrypted for data protection. +- [`DescriptionPlaintextUnlimited`](lib/description_plaintext_unlimited.ex) - + filters any HTML/JS to avoid security issues. Perfect for blog post comments. +- [`Encrypted`](lib/encrypted.ex) - a general purpose encrypted field. converts any type of data `to_string` and then encrypts it. -+ [`EmailEncrypted`](lib/email_encrypted.ex) - validate and strongly encrypt -email address to ensure they are kept private and secure. -+ [`EmailHash`](lib/email_hash.ex) - when an email needs to be looked up fast -without decrypting. Salted and hashed with `:sha256`. -+ [`EmailPlaintext`](lib/email_plaintext.ex) - when an email address is `public` -there's no advantage to encrypting it. e.g. a customer support email. -+ [`Hash`](lib/hash.ex) - a general-purpose hash field using `:sha256`, -useful if you need to store the hash of a value. (_one way_) -+ [`HtmlBody`](lib/html-body.ex) - useful for storing HTML data e.g in a CMS. -+ [`Name`](lib/html-body.ex) - used for personal names -that need to be kept private/secure. Max length 35 characters. AES Encrypted. -+ [`Password`](lib/password.ex) - passwords hashed using `argon2`. -+ [`PhoneNumberEncrypted`](lib/phone_number_encrypted.ex) - a phone number that should be kept private gets validated and encrypted. -+ [`PhoneNumber`](lib/phone_number.ex) - when a phone number is _not_ -sensitive information and can be stored in plaintext. -+ [`Postcode`](lib/postcode.ex) - validated postcode stored as `plaintext`. -+ [`PostcodeEncrypted`](lib/postcode_encrypted.ex) - validated and encrypted. -+ [`Url`](lib/url.ex) - validate a URL and store as `plaintext` -(_not encrypted_) `String` -+ [`UrlEncrypted`](lib/url_encrypted.ex) - validate a URL and store as AES _encrypted_ `Binary` -+ [`IpAddressPlaintext`](lib/ip_address_plaintext.ex) - validate an ipv4 and ipv6 address and store as `plaintext` -+ [`IpAddressHash`](lib/ip_address_hash.ex) - hash for ipv4 or ipv6 -+ [`IpAddressEncrypted`](lib/ip_address_encrypted.ex) - validate an ipv4 and ipv6 address and store as AES _encrypted_ `Binary` - -***Detailed documentation*** available on **HexDocs**: +- [`EmailEncrypted`](lib/email_encrypted.ex) - validate and strongly encrypt + email address to ensure they are kept private and secure. +- [`EmailHash`](lib/email_hash.ex) - when an email needs to be looked up fast + without decrypting. Salted and hashed with `:sha256`. +- [`EmailPlaintext`](lib/email_plaintext.ex) - when an email address is `public` + there's no advantage to encrypting it. e.g. a customer support email. +- [`Hash`](lib/hash.ex) - a general-purpose hash field using `:sha256`, + useful if you need to store the hash of a value. (_one way_) +- [`HtmlBody`](lib/html-body.ex) - useful for storing HTML data e.g in a CMS. +- [`Name`](lib/html-body.ex) - used for personal names + that need to be kept private/secure. Max length 35 characters. AES Encrypted. +- [`Password`](lib/password.ex) - passwords hashed using `argon2`. +- [`PhoneNumberEncrypted`](lib/phone_number_encrypted.ex) - a phone number that should be kept private gets validated and encrypted. +- [`PhoneNumber`](lib/phone_number.ex) - when a phone number is _not_ + sensitive information and can be stored in plaintext. +- [`Postcode`](lib/postcode.ex) - validated postcode stored as `plaintext`. +- [`PostcodeEncrypted`](lib/postcode_encrypted.ex) - validated and encrypted. +- [`Url`](lib/url.ex) - validate a URL and store as `plaintext` + (_not encrypted_) `String` +- [`UrlEncrypted`](lib/url_encrypted.ex) - validate a URL and store as AES _encrypted_ `Binary` +- [`IpAddressPlaintext`](lib/ip_address_plaintext.ex) - validate an ipv4 and ipv6 address and store as `plaintext` +- [`IpAddressHash`](lib/ip_address_hash.ex) - hash for ipv4 or ipv6 +- [`IpAddressEncrypted`](lib/ip_address_encrypted.ex) - validate an ipv4 and ipv6 address and store as AES _encrypted_ `Binary` + +**_Detailed documentation_** available on **HexDocs**: [hexdocs.pm/**fields**](https://hexdocs.pm/fields)
@@ -226,7 +223,6 @@ sensitive information and can be stored in plaintext. mix t ``` - ### _Coverage_ ``` @@ -242,7 +238,6 @@ please open an issue so we can add it!
-
## Background / Further Reading 🔗 @@ -255,7 +250,6 @@ If you are rusty/new on Binaries in Elixir, take a look at this post by @blackode:
https://medium.com/blackode/playing-with-elixir-binaries-strings-dd01a40039d5 - # Questions? If you have questions, please open an issue: @@ -270,23 +264,23 @@ and [`EmailHash`](https://github.com/dwyl/fields/blob/main/lib/email_hash.ex) serve very different purposes. Briefly: -with +with [**encryption**](https://en.wikipedia.org/wiki/Encryption) -the output is **_always_ different** +the output is **_always_ different** is meant for safely storing sensitive data that we want to **_decrypt_** later -whereas with +whereas with [**hash**](https://en.wikipedia.org/wiki/Hash_function) the output is **_always_ the same** -it cannot be "unhashed" but +it cannot be "unhashed" but can be used to -[***check***](https://en.wikipedia.org/wiki/Checksum) a value, +[**_check_**](https://en.wikipedia.org/wiki/Checksum) a value, i.e. you can lookup a _hashed_ value in a database. -The best way to understand how these work -is to see it for yourself. -Start an -[`IEx`](https://hexdocs.pm/iex/1.1.1/IEx.html) +The best way to understand how these work +is to see it for yourself. +Start an +[`IEx`](https://hexdocs.pm/iex/1.1.1/IEx.html) session in your terminal: ```sh @@ -302,6 +296,7 @@ Compiling 23 files (.ex) Generated fields app Interactive Elixir (1.12.3) - press Ctrl+C to exit (type h() ENTER for help) ``` + That confirms the `fields` module has compiled. #### Encryption @@ -321,18 +316,18 @@ iex(2)> encrypted = Fields.AES.encrypt(email) 80, 194, 241, 31, 28, 24, 155, 172, 208, 185, 142, 64, 65, 127>> ``` -> **Note**: the `Fields.EmailEncrypted` -uses the `AES.encrypt/1` behind the scenes, -that's why we are using it here directly. -You could just as easily have written: -`{:ok, encrypted} = Fields.EmailEncrypted.dump(email)` -this is just a shorthand. +> **Note**: the `Fields.EmailEncrypted` +> uses the `AES.encrypt/1` behind the scenes, +> that's why we are using it here directly. +> You could just as easily have written: +> `{:ok, encrypted} = Fields.EmailEncrypted.dump(email)` +> this is just a shorthand. -That output `<<48, 48, 48 ... 64, 65, 127>>` is a +That output `<<48, 48, 48 ... 64, 65, 127>>` is a [**bitstring**](https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#bitstrings) which is the sequence of bits in memory. -The encrypted data - usually called -["ciphertext"](https://en.wikipedia.org/wiki/Ciphertext) - +The encrypted data - usually called +["ciphertext"](https://en.wikipedia.org/wiki/Ciphertext) - is not human readable, that's a feature. But if you want to _decrypt_ it back to its human-readable form, simply run: @@ -346,13 +341,13 @@ iex(3)> decrypted = Fields.AES.decrypt(encrypted) So we know that an encrypted value can be decrypted. In the case of `EmailEncrypted` this is useful when we want to send someone an email message. -For security/privacy, +For security/privacy, we want their sensitive personal data to be stored _encrypted_ in the Database, but when we need to decrypt it to send them a message, it's easy enough. -If you run the `Fields.AES.encrypt/1` function +If you run the `Fields.AES.encrypt/1` function multiple times in your terminal, you will _always_ see different output: @@ -375,24 +370,22 @@ iex(7)> Fields.AES.encrypt(email) 235, 162, 186, 132, 23, 34, 174, 171, 157, 115, 54, 211, 124, 247, ...>> ``` -The first 4 bytes `<<48, 48, 48, 49,` are the same +The first 4 bytes `<<48, 48, 48, 49,` are the same because we are using the same encryption key. -But the rest is _always_ different. - +But the rest is _always_ different. #### Hashing -A `hash` function -can be used to map data of arbitrary size +A `hash` function +can be used to map data of arbitrary size to fixed-size values. -i.e. _any_ length of `plaintext` will +i.e. _any_ length of `plaintext` will result in the _same_ length `hash` _value_. A `hash` function is _one-way_, it cannot be reversed or "un-hashed". The `hash` _value_ is _always_ the same for a given string of plaintext. - Try it in `IEx`: ```elixir @@ -410,11 +403,11 @@ iex(4)> Fields.Helpers.hash(:sha256, email) 17, 97, 146, 103, 115, 3, 185, 230, 137, 218, 137, 209, 111, 48, 236>> ``` -The hash _value_ is identical for the given input text +The hash _value_ is identical for the given input text in this case the email address `"alex@gmail.com"`. If you use the `Fields.EmailHash.dump/1` function, -you will see the same hash value +you will see the same hash value (_because the same helper function is invoked_): ```elixir @@ -430,13 +423,12 @@ iex(6)> Fields.EmailHash.dump(email) When the `EmailHash` is stored in a database we can lookup an email address by hashing it -and comparing it to the list. +and comparing it to the list. -The best way of _visualizing_ this -is to convert the hash value (bitstring) +The best way of _visualizing_ this +is to convert the hash value (bitstring) to `base64` so that it is _human-readable_: - ```elixir iex(1)> email = "alex@gmail.com" "alex@gmail.com" @@ -450,14 +442,13 @@ iex(3)> Fields.Helpers.hash(:sha256, email) |> :base64.encode Imagine you have a database table called `people` that has just 3 columns: `id`, `email_hash` and `email_encrypted` +| `id` | `email_hash` | `email_encrypted` | +| :--: | -------------------------------------------- | -------------------------------------- | +| 1 | X/v7zLU77wTawSMU34PbZR4RYZJncwO55onaidFvMOw= | MDAwMc57Y1j0nhwOdw7EvNeUVEfYQoAr7aT6oX | +| 2 | +zXMhia/Z2I64nul6pqoDZTVM1q2K21Pby6GtPcm9iE= | MDAwMXnS1uwGN/cZRFkQgArm2Sbj9y+hnUJIS7 | +| 3 | maY4IxoRSOSqm6qyJDrnEN1JQssJRqRGhzwOown4DPU= | MDAwMa4v0FBko++zqfAkfisXOLosQfrDLAdPax | -| `id` | `email_hash` | `email_encrypted` | -|:-----:| ------------- | ----------------- | -| 1 | X/v7zLU77wTawSMU34PbZR4RYZJncwO55onaidFvMOw= | MDAwMc57Y1j0nhwOdw7EvNeUVEfYQoAr7aT6oX | -| 2 | +zXMhia/Z2I64nul6pqoDZTVM1q2K21Pby6GtPcm9iE= | MDAwMXnS1uwGN/cZRFkQgArm2Sbj9y+hnUJIS7 | -| 3 | maY4IxoRSOSqm6qyJDrnEN1JQssJRqRGhzwOown4DPU= | MDAwMa4v0FBko++zqfAkfisXOLosQfrDLAdPax | - -With this "database" table, +With this "database" table, we can now _lookup_ an email address to find out their `id`: ```elixir @@ -471,22 +462,22 @@ therefore **Alice's** `id` is `2` in the database. \ No newline at end of file +--> diff --git a/mix.exs b/mix.exs index f1a0a48..11498b9 100644 --- a/mix.exs +++ b/mix.exs @@ -5,7 +5,7 @@ defmodule Fields.MixProject do [ app: :fields, description: "A collection of useful fields for building Phoenix apps faster!", - version: "2.9.1", + version: "2.10.0", elixir: ">= 1.10.0", start_permanent: Mix.env() == :prod, deps: deps(), @@ -19,7 +19,7 @@ defmodule Fields.MixProject do coveralls: :test, "coveralls.json": :test, "coveralls.html": :test, - t: :test, + t: :test ] ] end @@ -49,7 +49,6 @@ defmodule Fields.MixProject do {:envar, "~> 1.0.8"}, # strip noise from html field {:html_sanitize_ex, "~> 1.4.2"}, - # stream_data for property based testing {:stream_data, "~> 0.5.0", only: :test}, @@ -76,7 +75,7 @@ defmodule Fields.MixProject do defp aliases do [ c: ["coveralls.html"], - t: ["test"], + t: ["test"] ] end end diff --git a/mix.lock b/mix.lock index 378e09c..306e90e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "argon2_elixir": {:hex, :argon2_elixir, "3.0.0", "fd4405f593e77b525a5c667282172dd32772d7c4fa58cdecdaae79d2713b6c5f", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "8b753b270af557d51ba13fcdebc0f0ab27a2a6792df72fd5a6cf9cfaffcedc57"}, - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, + "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},