Police-Data-Accessibility-Project / data-sources-app

An API and UI for using and maintaining the Data Sources database
MIT License
2 stars 5 forks source link

Encrypt API Keys server-side #388

Open maxachis opened 1 month ago

maxachis commented 1 month ago

While passwords are currently encrypted in the database, API keys are not. Considering that API keys enable someone to perform a number of actions as a given user and with the permissions available to that user, we probably want to address that.

We have a number of possible approaches to this, including:

  1. Utilizing the pre-existing password encryption functionality located in the backend code
  2. Adding a database extension like pgcrypto to the Postgres database, and handling the generation, encryption, and decryption of API keys database-side.
  3. A variety of other backend- and database-side options.

Implementation Requirements

maxachis commented 1 month ago

How to ensure proper user-specific encryption is what I'm currently noodling over.

I contemplated encrypting using a hash of the user's current password, but the downside to that is that if the user's password changes, the API key must also change, otherwise the hash of the current password would fail to actually decrypt the original content.

Another option I'm currently looking into is the (embarrassingly named) KEK (Key Encryption Key) model.

maxachis commented 1 month ago

Currently, I'm settling on having a single encryption key rather than a dual-encryption model: While ensuring greater security, it may be overkill in this context, as JWTs will be used for the more secure endpoints rather than API keys.

This encryption key would be accessed as an environment variable within the app (perhaps as ENCRYPTION_KEY), and used to encrypt and decrypt keys via the app.

maxachis commented 4 weeks ago

Also, in dev_scripts.sql, the following will need to be included:

create extension if not exists pgcrypto;
maxachis commented 4 weeks ago

@josh-chamberlain For API keys, the intended use of the api keys is tied to the method of encryption, so I want to clarify which use-case we're looking for:

Feature Users see API Key only once; hashing Users can see the same API key multiple times; reversible encryption
Description - Users see their API key only once and must manage it themselves. - Users can see their API key multiple times.
When do users need to refresh their API token? Whenever they need to see the key again or suspect a security issue. Only if they want to change the key or suspect a security issue.
How is it managed? Users are responsible for storing the key securely after initial view.
The system uses a hashed key for validation.
Users can retrieve the key multiple times from the system.
The system uses reversible encryption to store and retrieve the key.
Pros - More secure - More convenient for users
Cons - Less convenient for users - Less secure
maxachis commented 3 weeks ago

Setting up Test API Keys

One thing I'll need to work on is ensuring API keys can stay static in the development backend even while encrypted (and with the encryption done in the middleware).

My current idea is I pre-encrypt/hash an API key (using the same encryption key we'll use in the development environment), and then store that encrypted API key manually in the backend. The middleware apps will need to have the correct API key, but in theory, they shouldn't have to regenerate an API key every time.

josh-chamberlain commented 3 weeks ago

@maxachis your thoughts on encryption make sense; something like pgcrypto seems like a good option, in line with our usual strategy to use what's already been created where possible.

I think users should only see the API key once. It's more secure, and we will give them a way to regenerate a key if needed so the inconvenience cost is not too bad. I suspect most of our API users will not be deploying apps but working locally + individually. Also, many of our users are interested in cybersecurity!

maxachis commented 3 weeks ago

@josh-chamberlain In that case, we can probably use the same method we use for hashing our passwords and storing them in the database, which can simplify the steps required considerably!

joshuagraber commented 1 week ago

I think users should only see the API key once. It's more secure, and we will give them a way to regenerate a key if needed so the inconvenience cost is not too bad. I suspect most of our API users will not be deploying apps but working locally + individually. Also, many of our users are interested in cybersecurity!

I agree. It's relatively trivial to generate and encrypt a new key, so it makes sense to only return it once.