I'm not an expert, but obviously anything where the encryption can be reversed (unlike a password hash) could get compromised if the application key is discovered. There's just no avoiding that.
If someone accesses the database only that's fine, without the application key, they cannot decrypt the values. If they get both app key and database then the API keys are vulnerable.
Off the top of my head best practices include making sure that any API key given to your application should follow the "principle of least privilege".
- If the API key is only to allow sending of emails - that's all the API key should have the permission to do: no user creation, template usage, account manipulation etc. I know you can set the abilities for SparkPost, I imagine the others are similar.
- Most API keys can be restricted to only be usable from a range of email addresses. Your users should be provided with the IP addresses of your servers that will be sending requests to the service API endpoints so that the API keys are only valid for requests from your servers.
- If users can cap usage, then that's an idea as well - to avoid anyone compromising your server and a customer's API excessively (it's a less likely scenario)
- The keys they provide should be unique to your service, in case you have to ask them to cancel them (see below).
In the (hopefully unlikely) event of a compromise - have a defined procedure of how you will deal with it. In this case it will most likely be an email to customers to tell them to cancel the keys they gave you to stop them being used.
If your database is compromised/inaccessible... how will you email them? Will you have an alternative location (backup) where you can get the current list of active customers?
This should reduce the scope of what people can do. In the above scenario, they would only be able send more emails by making more API calls from your server - that's going to be easy to spot and easy to halt. You then have a very direct route to notify users and get them to arrange new API keys.