-
Notifications
You must be signed in to change notification settings - Fork 388
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
feat: encrypt sensitive columns #1593
Conversation
81eb808
to
8a97e9d
Compare
Pull Request Test Coverage Report for Build 9366388885Details
💛 - Coveralls |
4e050a0
to
243cceb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for putting this together
@hf Some questions:
just to clarify my understanding, the encryption key of an object is derived from the symmetric key + the object id?
The old key will already be in the decryption keys map because we had to use it previously to decrypt the objects right? So we actually just need to update the encryption key and encryption key id?
This gives me the impression that |
Yes, the
Yeah, I'll change the wording I see how it's confusing.
Yes, it's just one key. |
243cceb
to
92afa8c
Compare
92afa8c
to
3362518
Compare
🤖 I have created a release *beep* *boop* --- ## [2.154.0](v2.153.0...v2.154.0) (2024-06-12) ### Features * add max length check for email ([#1508](#1508)) ([f9c13c0](f9c13c0)) * add support for Slack OAuth V2 ([#1591](#1591)) ([bb99251](bb99251)) * encrypt sensitive columns ([#1593](#1593)) ([e4a4758](e4a4758)) * upgrade otel to v1.26 ([#1585](#1585)) ([cdd13ad](cdd13ad)) * use largest avatar from spotify instead ([#1210](#1210)) ([4f9994b](4f9994b)), closes [#1209](#1209) ### Bug Fixes * define search path in auth functions ([#1616](#1616)) ([357bda2](357bda2)) * enable rls & update grants for auth tables ([#1617](#1617)) ([28967aa](28967aa)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Adds support for encrypting sensitive columns like the MFA secret and password hash. The goal with this encryption mechanism is to add yet another layer of security on top of the database permissions provided by Postgres. In the event that the database leaks or is accessed by malicious users or the database permissions are incorrectly defined, the encryption key would also be required to inspect this sensitive data. Encryption is done using AES-GCM-256. Strings that are encrypted are converted into a JSON string with this shape: ```json { "key_id": "key identifier used for encryption", "alg": "aes-gcm-hkdf", "nonce": "GCM 12 byte nonce", "data": "Base64 standard encoding of the ciphertext" } ``` As AES-GCM must not be used more than 2^32 times with a single symmetric key, and this is not that much -- imagine serving 100m users -- then this means that all users can only add 42 passwords or MFA verification factors before running into this hard limit. To fix this, a symmetric key is derived using [HKDF](https://datatracker.ietf.org/doc/html/rfc5869) with SHA256 such that the symmetric key is used together with the object ID (for passwords - the user ID, for TOTP secrets - the factor ID). This way there's a separate AES-GCM key per object, and additionally gives the security property that a malicious actor with write permissions to the database cannot swap passwords / TOTP secrets from Malice's account to Target's account. They would need to also change the UUIDs of these objects, which is likely to be hard. To turn on encryption the following configs need to be added: `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true` -- that turns on encryption for new objects. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=key-id` -- ID of the encryption key, allowing to rotate keys easily. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=key` -- Base64 URL encoding of a 256 bit AES key Once encryption has been turned on, in order to have the rows be readable **for ever** this config must be provided with all past and future keys: `GOTRUE_SECURITY_DB_ENCRYPTION_DECRYPTION_KEYS=key-id:key` -- A map of key IDs and Base64 URL key encodings of the keys. To retire keys, you should just move the old key to the decryption keys map, and advertise the new encryption key ID. On each successful sign in with password, or any MFA verification attempt, the latest key will be used to re-encrypt the column. This also applies for the non-encrypted-to-encrypted case.
🤖 I have created a release *beep* *boop* --- ## [2.154.0](supabase/auth@v2.153.0...v2.154.0) (2024-06-12) ### Features * add max length check for email ([supabase#1508](supabase#1508)) ([f9c13c0](supabase@f9c13c0)) * add support for Slack OAuth V2 ([supabase#1591](supabase#1591)) ([bb99251](supabase@bb99251)) * encrypt sensitive columns ([supabase#1593](supabase#1593)) ([e4a4758](supabase@e4a4758)) * upgrade otel to v1.26 ([supabase#1585](supabase#1585)) ([cdd13ad](supabase@cdd13ad)) * use largest avatar from spotify instead ([supabase#1210](supabase#1210)) ([4f9994b](supabase@4f9994b)), closes [supabase#1209](supabase#1209) ### Bug Fixes * define search path in auth functions ([supabase#1616](supabase#1616)) ([357bda2](supabase@357bda2)) * enable rls & update grants for auth tables ([supabase#1617](supabase#1617)) ([28967aa](supabase@28967aa)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Adds support for encrypting sensitive columns like the MFA secret and password hash. The goal with this encryption mechanism is to add yet another layer of security on top of the database permissions provided by Postgres. In the event that the database leaks or is accessed by malicious users or the database permissions are incorrectly defined, the encryption key would also be required to inspect this sensitive data. Encryption is done using AES-GCM-256. Strings that are encrypted are converted into a JSON string with this shape: ```json { "key_id": "key identifier used for encryption", "alg": "aes-gcm-hkdf", "nonce": "GCM 12 byte nonce", "data": "Base64 standard encoding of the ciphertext" } ``` As AES-GCM must not be used more than 2^32 times with a single symmetric key, and this is not that much -- imagine serving 100m users -- then this means that all users can only add 42 passwords or MFA verification factors before running into this hard limit. To fix this, a symmetric key is derived using [HKDF](https://datatracker.ietf.org/doc/html/rfc5869) with SHA256 such that the symmetric key is used together with the object ID (for passwords - the user ID, for TOTP secrets - the factor ID). This way there's a separate AES-GCM key per object, and additionally gives the security property that a malicious actor with write permissions to the database cannot swap passwords / TOTP secrets from Malice's account to Target's account. They would need to also change the UUIDs of these objects, which is likely to be hard. To turn on encryption the following configs need to be added: `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true` -- that turns on encryption for new objects. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=key-id` -- ID of the encryption key, allowing to rotate keys easily. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=key` -- Base64 URL encoding of a 256 bit AES key Once encryption has been turned on, in order to have the rows be readable **for ever** this config must be provided with all past and future keys: `GOTRUE_SECURITY_DB_ENCRYPTION_DECRYPTION_KEYS=key-id:key` -- A map of key IDs and Base64 URL key encodings of the keys. To retire keys, you should just move the old key to the decryption keys map, and advertise the new encryption key ID. On each successful sign in with password, or any MFA verification attempt, the latest key will be used to re-encrypt the column. This also applies for the non-encrypted-to-encrypted case.
🤖 I have created a release *beep* *boop* --- ## [2.154.0](supabase/auth@v2.153.0...v2.154.0) (2024-06-12) ### Features * add max length check for email ([supabase#1508](supabase#1508)) ([f9c13c0](supabase@f9c13c0)) * add support for Slack OAuth V2 ([supabase#1591](supabase#1591)) ([bb99251](supabase@bb99251)) * encrypt sensitive columns ([supabase#1593](supabase#1593)) ([e4a4758](supabase@e4a4758)) * upgrade otel to v1.26 ([supabase#1585](supabase#1585)) ([cdd13ad](supabase@cdd13ad)) * use largest avatar from spotify instead ([supabase#1210](supabase#1210)) ([4f9994b](supabase@4f9994b)), closes [supabase#1209](supabase#1209) ### Bug Fixes * define search path in auth functions ([supabase#1616](supabase#1616)) ([357bda2](supabase@357bda2)) * enable rls & update grants for auth tables ([supabase#1617](supabase#1617)) ([28967aa](supabase@28967aa)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Adds support for encrypting sensitive columns like the MFA secret and password hash. The goal with this encryption mechanism is to add yet another layer of security on top of the database permissions provided by Postgres. In the event that the database leaks or is accessed by malicious users or the database permissions are incorrectly defined, the encryption key would also be required to inspect this sensitive data. Encryption is done using AES-GCM-256. Strings that are encrypted are converted into a JSON string with this shape: ```json { "key_id": "key identifier used for encryption", "alg": "aes-gcm-hkdf", "nonce": "GCM 12 byte nonce", "data": "Base64 standard encoding of the ciphertext" } ``` As AES-GCM must not be used more than 2^32 times with a single symmetric key, and this is not that much -- imagine serving 100m users -- then this means that all users can only add 42 passwords or MFA verification factors before running into this hard limit. To fix this, a symmetric key is derived using [HKDF](https://datatracker.ietf.org/doc/html/rfc5869) with SHA256 such that the symmetric key is used together with the object ID (for passwords - the user ID, for TOTP secrets - the factor ID). This way there's a separate AES-GCM key per object, and additionally gives the security property that a malicious actor with write permissions to the database cannot swap passwords / TOTP secrets from Malice's account to Target's account. They would need to also change the UUIDs of these objects, which is likely to be hard. To turn on encryption the following configs need to be added: `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true` -- that turns on encryption for new objects. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=key-id` -- ID of the encryption key, allowing to rotate keys easily. `GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=key` -- Base64 URL encoding of a 256 bit AES key Once encryption has been turned on, in order to have the rows be readable **for ever** this config must be provided with all past and future keys: `GOTRUE_SECURITY_DB_ENCRYPTION_DECRYPTION_KEYS=key-id:key` -- A map of key IDs and Base64 URL key encodings of the keys. To retire keys, you should just move the old key to the decryption keys map, and advertise the new encryption key ID. On each successful sign in with password, or any MFA verification attempt, the latest key will be used to re-encrypt the column. This also applies for the non-encrypted-to-encrypted case.
🤖 I have created a release *beep* *boop* --- ## [2.154.0](supabase/auth@v2.153.0...v2.154.0) (2024-06-12) ### Features * add max length check for email ([supabase#1508](supabase#1508)) ([f9c13c0](supabase@f9c13c0)) * add support for Slack OAuth V2 ([supabase#1591](supabase#1591)) ([bb99251](supabase@bb99251)) * encrypt sensitive columns ([supabase#1593](supabase#1593)) ([e4a4758](supabase@e4a4758)) * upgrade otel to v1.26 ([supabase#1585](supabase#1585)) ([cdd13ad](supabase@cdd13ad)) * use largest avatar from spotify instead ([supabase#1210](supabase#1210)) ([4f9994b](supabase@4f9994b)), closes [supabase#1209](supabase#1209) ### Bug Fixes * define search path in auth functions ([supabase#1616](supabase#1616)) ([357bda2](supabase@357bda2)) * enable rls & update grants for auth tables ([supabase#1617](supabase#1617)) ([28967aa](supabase@28967aa)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Adds support for encrypting sensitive columns like the MFA secret and password hash.
The goal with this encryption mechanism is to add yet another layer of security on top of the database permissions provided by Postgres. In the event that the database leaks or is accessed by malicious users or the database permissions are incorrectly defined, the encryption key would also be required to inspect this sensitive data.
Encryption is done using AES-GCM-256. Strings that are encrypted are converted into a JSON string with this shape:
As AES-GCM must not be used more than 2^32 times with a single symmetric key, and this is not that much -- imagine serving 100m users -- then this means that all users can only add 42 passwords or MFA verification factors before running into this hard limit. To fix this, a symmetric key is derived using HKDF with SHA256 such that the symmetric key is used together with the object ID (for passwords - the user ID, for TOTP secrets - the factor ID). This way there's a separate AES-GCM key per object, and additionally gives the security property that a malicious actor with write permissions to the database cannot swap passwords / TOTP secrets from Malice's account to Target's account. They would need to also change the UUIDs of these objects, which is likely to be hard.
To turn on encryption the following configs need to be added:
GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPT=true
-- that turns on encryption for new objects.GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY_ID=key-id
-- ID of the encryption key, allowing to rotate keys easily.GOTRUE_SECURITY_DB_ENCRYPTION_ENCRYPTION_KEY=key
-- Base64 URL encoding of a 256 bit AES keyOnce encryption has been turned on, in order to have the rows be readable for ever this config must be provided with all past and future keys:
GOTRUE_SECURITY_DB_ENCRYPTION_DECRYPTION_KEYS=key-id:key
-- A map of key IDs and Base64 URL key encodings of the keys.To retire keys, you should just move the old key to the decryption keys map, and advertise the new encryption key ID. On each successful sign in with password, or any MFA verification attempt, the latest key will be used to re-encrypt the column. This also applies for the non-encrypted-to-encrypted case.