From a6afecfba5f3b05155040bb69dc435ff172ab460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Cobucci?= Date: Sat, 4 Nov 2023 22:51:46 +0100 Subject: [PATCH] Add docs on key rotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Luís Cobucci --- .readthedocs.yaml | 9 ++ docs/rotating-keys.md | 211 ++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 221 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 docs/rotating-keys.md diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..c1545635d --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,9 @@ +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: 3 + +mkdocs: + configuration: mkdocs.yml diff --git a/docs/rotating-keys.md b/docs/rotating-keys.md new file mode 100644 index 000000000..dafe912b1 --- /dev/null +++ b/docs/rotating-keys.md @@ -0,0 +1,211 @@ +# Rotating Keys + +Key rotation consists in retiring and replacing cryptographic keys with new ones. +Performing that operation on a regular basis is an industry standard. + +## Why should I rotate my keys? + +Rotating keys allows us to: + +1. Limit the number of tokens signed with the same key, helping the prevention of attacks enabled by cryptanalysis +2. Adopt other algorithms or stronger keys +3. Limit the impact of eventual compromised keys + +## The challenges + +After rotating keys, apps will likely receive requests with tokens issues with the previous key. +If the key rotation of an app is done with a "hard cut", requests with non-expired tokens issued with the old key **will fail**! + +Imagine if you were the user who logged in just before a key rotation on that kind of app, you'd probably have to log in again! + +That's rather frustrating, right!? + +## Preventing issues + +It's possible to handle key rotation in a smoother way by leveraging the `SignedWithOneInSet` validation constraint! + +Say your application uses the symmetric algorithm `HS256` with a not so secure key to issue tokens: + +```php +issue( + new Signer\Hmac\Sha256(), + InMemory::plainText( + 'a-very-long-and-secure-key-that-should-actually-be-something-else' + ), + static fn (Builder $builder): Builder => $builder + ->issuedBy('https://api.my-awesome-app.io') + ->permittedFor('https://client-app.io') +); +``` + +!!! Sample + Here's a token issued with the code above, if you want to test the script locally: + +
+ Sample token + + // line breaks added for readability + eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 + .eyJpYXQiOjE2OTkxMzE5NjEsIm5iZiI6MTY5OTEzMTk2MSwiZXhwIjoxNjk5MTMyMjYxLCJpc3MiOiJ + odHRwczovL2FwaS5teS1hd2Vzb21lLWFwcC5pbyIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LWFwcC5pbyJ9 + .IA9S0n8Q2O97lyR8KczVE8g-hxbbH6_TfJS-JWTQR4c +
+ +Your parsing logic (with validations) look like: + +```php +parse($jwt, ...$validationConstraints); +``` + +### Performing a backwards compatible rotation + +Now Imagine that you want to adopt the new `BLAKE2B` symmetric algorithm. + +These are the changes to your issuing logic: + +```diff + issue( +- new Signer\Hmac\Sha256(), ++ new Signer\Blake2b(), +- InMemory::plainText( +- 'a-very-long-and-secure-key-that-should-actually-be-something-else' ++ InMemory::base64Encoded( ++ 'GOu4rLyVCBxmxP+sbniU68ojAja5PkRdvv7vNvBCqDQ=' + ), + static fn (Builder $builder): Builder => $builder + ->issuedBy('https://api.my-awesome-app.io') + ->permittedFor('https://client-app.io') + ); +``` + +!!! Sample + Here's a token issued with the code above, if you want to test the script locally: + +
+ Sample token + + // line breaks added for readability + eyJ0eXAiOiJKV1QiLCJhbGciOiJCTEFLRTJCIn0 + .eyJpYXQiOjE2OTkxMzE5NjEsIm5iZiI6MTY5OTEzMTk2MSwiZXhwIjoxNjk5MTMyMjYxLCJpc3Mi + OiJodHRwczovL2FwaS5teS1hd2Vzb21lLWFwcC5pbyIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LWFwc + C5pbyJ9.bD67s8IXpAJiBTIZn1et_M5WSS7kfmuNiacNRz5lArQ +
+ +So far, nothing different that a normal rotation. + +Now check the changes on the parsing and validation logic: + +```diff + parse($jwt, ...$validationConstraints); +``` + +Now the application is able to accept non-expired tokens issued with either old and new keys! + +!!! Important + The order of `SignedWith` constraints given to `SignedWithOneInSet` does matter, and it's recommended to leave older keys at the end of the list. diff --git a/mkdocs.yml b/mkdocs.yml index 8dbba334f..1a3ef8581 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,6 +14,7 @@ nav: - 'configuration.md' - Guides: - 'extending-the-library.md' + - 'rotating-keys.md' - 'upgrading.md' markdown_extensions: