Are there any security measures that can be implemented in case an attacker gains access to the database?
if your org does not have a strong ops function yet, and you're in scrappy startup phase: just use your language or framework's best practices, standard encryption tools. do not try to create your own encryption scheme. let your app take two keys; `CURRENT_ENCRYPTION_KEY` and `PREVIOUS_ENCRYPTION_KEY`, obviously named however you want. when encrypting data, always encrypt with `CURRENT_ENCRYPTION_KEY`. when decrypting data, first attempt with `CURRENT_ENCRYPTION_KEY`, then attempt with `PREVIOUS_ENCRYPTION_KEY` if it is set. if `PREVIOUS_ENCRYPTION_KEY` was successful, re-encrypt the unencrypted value with `CURRENT_ENCRYPTION_KEY` and save it back to the database. this gives you backward compatible deploys. you'll then still want to write and run a script that will run through and manually re-encrypt all the old data, or else you might rotate a second time and lose access to any data encoded with the first key that had not yet been rotated. rotate your key regularly by copying `CURRENT_ENCRYPTION_KEY` to `PREVIOUS_ENCRYPTION_KEY`, and putting a new key into `CURRENT_ENCRYPTION_KEY`. the appropriate value of "regularly" depends on a lot of variables, and only you can determine what is appropriate for your situation.
this is table stakes level security; realistically if your DB is compromised, your encryption key probably is too, because they probably got in through your application which holds the key in memory. this just prevents "oops I accidentally copied the DB somewhere and it leaked" (which, at most startups, I would say is the most likely leak).
if you have, or when you get to the point that you have, a competent ops org, just use HashiCorp Vault.
For example how often are you using their API Key? Who initiates using the key (e.g. customer, yourselves on schedule, yourselves but manually)?
The password cheatsheet linked elsewhere isn't directly relevant, because an API Key needs to be stored in a recoverable way, unlike a password. A more relevant cheatsheet might be this one (Cryptographic Storage):
https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic...
Without more context or information I am making a lot of assumptions: My gut "go to" is store an entire SQLite single-file database in your regular database (binary/file column), and take advantage of SEE[0] AES-256 (OFB), SQLCipher, or one of the free/open source extension alternatives. Then encrypt/decrypt it using something derived securely from the logged-in user's password (not otherwise stored in the database) OR a mix of something user specific held in your database (e.g. UUID) + something secret stored in environmental variables/on servers. The result is that database compromise ALONE isn't enough to steal the user's secrets, you'd need server + database, or the logged-in user's original password.
I realize a whole SQLite database for secret storage might seem excessive, but it is very scalable and self-documenting as you add additional secrets or elements to existing secrets. Most commercial database offerings offer encryption at rest, and a few offer table-specific encryption, but in my experience utilizing some of those implementations is actually MORE complex than what I'm describing here, even if you'd expect the inverse.
Are the secrets sensitive enough to encrypt them at rest?
Keeping the lock (the encrypted secret) and the key (the decryption key) in two separate places makes it slightly harder for an attacker to recover the plaintext secret, but also means you need to take the necessary precautions to not leak the key accidentally.
Sometimes, we can't even trust our system to be secure enough to prevent the key from becoming compromised, so Hardware Security Modules (HSMs)[1] became a thing, something with, presumably, a smaller attack surface that holds the key and can be used to decrypt the secret.
https://cheatsheetseries.owasp.org/cheatsheets/Password_Stor...