question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Database objects and the Principle of Least Privilege

See original GitHub issue

Current Implementation

Currently, the database is used with a PostgreSQL superuser, which has full rights the the database cluster. The Table diagnosis_key is accessible to the “PUBLIC” pseudo user, so every other database user has full access too.

Suggested Enhancement

Following the Principle of Least Privilege, the application user(s) should have only the least possible privileges. e.g. It is not necessary to have the rights for DELETE, TRUNCATE, UPDATE or use DDL statements.

I describe my suggestion in more detail below. I am not fully aware of the application server design in the cwa-server, so maybe there are some differences in your current implementation. So, let’s assume, there are two main applications, application parts or micro services. Ons is for inserting, and one for reading. In this case, there should be two users:

  1. inserter
  2. reader

For the inserter, it should only be possible to insert values. This user should not have the right to read, delete or update something.

The reader should only be able to read, but not modify any data.

See below for examples and for two different approaches

e.g. see also: BSI IT-Grundschutz, APP.4.3 Relationale Datenbanksysteme

Expected Benefits

It should not be possible for an attacker to delete the database, read data without permission, create new database objects or perform a denial of service attack by creating millions of rows of random data, etc.

Details

PostgreSQL config

The cwa-server seems to use the postgres superuser. Usually connections by superusers should only be possible via unix domain sockets and via localhost. An entry with method “peer” in pg_hba.conf is OK:

# TYPE     DATABASE        USER            ADDRESS                 METHOD
local      all             postgres                                peer
host       all             all             10.23.42.1/24           scram-sha-256

With this and when no password is set for the postgres superuser, connection is only possible via the postgres unix user and via local unix domain sockets. Example:

## As root (connecting to database postgres with user postgres):
# psql postgres postgres
psql: Fehler: konnte nicht mit Server verbinden: FATAL:  Peer authentication failed for user "postgres"

# # as postgres user (connecting to database postgres with user postgres):
# su - postgres
> psql 
psql (12.3)
Geben Sie »help« für Hilfe ein.

postgres=# 

Basic variant with PostgreSQL Roles and permissions

A script may look like this:

-- New database
CREATE DATABAE cwa;

-- connect to cwa DB 
\connect cwa

BEGIN;
-- revoke all permissions from this database for all users (beside superuser)
REVOKE ALL ON DATABASE cwa FROM PUBLIC;

-- One role (gets connection right) and two users:
CREATE ROLE cwa_users; 
CREATE ROLE cwa_reader   IN ROLE cwa_users LOGIN PASSWORD 'change-me-in-production';
CREATE ROLE cwa_inserter IN ROLE cwa_users LOGIN PASSWORD 'change-me-in-production';

-- ("CREATE USER […] "instead of "CREATE ROLE […] LOGIN […]" is the same

-- Both Login roles get connection right:
GRANT CONNECT ON DATABASE cwa TO cwa_users;

CREATE TABLE diagnosis_key (
    key_data                BYTEA    NOT NULL PRIMARY KEY,  -- really BYTEA? Why not UUID? Is 16 bytes, bytea takes 20 (4 bytes length) and has dynamic length
    rolling_period          INTEGER  NOT NULL,              -- is this a timestamp?!?
    rolling_start_number    INTEGER  NOT NULL,
    submission_timestamp    BIGINT   NOT NULL,              -- usually better: TIMESTAMP WITH TIME ZONE!
    transmission_risk_level INTEGER  NOT NULL
);

-- revoke from everyone
REVOKE ALL ON diagnosis_key FROM PUBLIC;
REVOKE ALL ON diagnosis_key FROM current_user; 

-- Set rights to the users
GRANT SELECT ON diagnosis_key TO cwa_reader;   -- reader can do only SELECTs 
GRANT INSERT ON diagnosis_key TO cwa_inserter; -- inserter can do only INSERTs


COMMIT;

Recreating the database and everything

Usually it is a good idea to have a set of scripts, which creates a database and all needed users from scratch, and maybe this can delete an old database etc.

Create a “create all” script and include all others with \ir …

(I may provide you with some sql-scripts for this if needed.)

Better variant with least privileges via functions

It is better, more secure and often may give better performance, creating functions for the table access. Then the login users get NO (!) access to the tables, they can only call functions like “insert_key” or “get_key_by_id” or whatever. The following is only an example; in the application logic here, you may use other queries (e.g. returning multiple rows).

-- New database
CREATE DATABAE cwa;

-- connect cwa DB (long form: \connect)
\c cwa

BEGIN;
-- revoke all permissions from this database for all users (beside superuser)
REVOKE ALL ON DATABASE cwa FROM PUBLIC;

-- One role (gets connection right) and two users:
CREATE ROLE cwa_users; 
CREATE ROLE cwa_reader   IN ROLE cwa_users LOGIN PASSWORD 'change-me-in-production';
CREATE ROLE cwa_inserter IN ROLE cwa_users LOGIN PASSWORD 'change-me-in-production';

-- ("CREATE USER […] "instead of "CREATE ROLE […] LOGIN […]" is the same

-- Both Login roles get connection right:
GRANT CONNECT ON DATABASE cwa TO cwa_users;

CREATE TABLE diagnosis_key (
    key_data                UUID      NOT NULL PRIMARY KEY,                  -- changed to uuid ;-)
    rolling_period          INTEGER   NOT NULL,                              -- is this a timestamp too?!?
    rolling_start_number    INTEGER   NOT NULL,
    submission_timestamp    TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), -- set transaction time by default 
    transmission_risk_level INTEGER   NOT NULL
);

-- revoke from everyone
REVOKE ALL ON diagnosis_key FROM PUBLIC;
REVOKE ALL ON diagnosis_key FROM current_user; 

-- now create functions for accessing the data 

CREATE OR REPLACE FUNCTION get_diagnosis_data_for_key(in_key UUID)
   RETURNS diagnosis_key
   AS 
   $code$
     SELECT * FROM diagnosis_key WHERE key_data = in_key;
   $code$
   LANGUAGE sql 
   SECURITY DEFINER 
   SET search_path = public, pg_temp;

-- maybe alter owner, if there is an extra owner:
--   ALTER FUNCTION get_diagnosis_data_for_key(UUID) OWNER TO cwa_owner;

-- set execute permission ONLY to cwa_reader:
REVOKE ALL    ON FUNCTION get_diagnosis_data_for_key)      FROM PUBLIC;
GRANT EXECUTE ON FUNCTION get_diagnosis_data_for_key(UUID) TO   cwa_reader;


-- and a function for inserting too!


COMMIT;

This is only an example for showing the principle!

In this example, it is only possible to read one row; an attacker, wo takes control over the “inserter” application can only call the inserter function, nothing else. In reality you need other functions, this is only an example.

The benefit is, that this is much more secure.

See also

In this thread I mentioned some other possible improvements (e.g. partitioning for automatic deletion of old data) too.

An example for using functions in an application is Posemo, PostgreSQL Secure Monitoring. There all functions are generated by Code, e.g. see the checks folder or documentation how to write new checks.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:5
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

5reactions
christian-kirschnickcommented, Jun 5, 2020

Hi @alvar-freude,

thanks for giving feedback to the CWA and sharing your thoughts here. All ideas helping us making CWA better are well appreciated!

Let’s go through your points one by one:

Least Privilege Users

We are following the principle of Least Privilege based on separation of the necessary scope of our microservices. We introduced dedicated database users for the different scenarios we have:

  • Submission User: Only INSERT permission, and only for diagnosis keys table
  • Distribution User: Only SELECT/DELETE and only for diagnosis keys table
  • Migration User: Requires all permissions, but is no super user

The different roles were introduced with https://github.com/corona-warn-app/cwa-server/pull/396.

Our DBA’s are creating the three database users with dedicated SQL scripts, which are not part of the code base, they are only part of internal runbooks. The scripts are working the same way you explained in your post, especially with regards of revocation of permissions for PUBLIC.

Postgres superuser

CWA server does not use any superuser (like postgres) on any deployed instance. This repository only contains the source code & setup for local environment, e.g. with the help of docker compose.

The actual deployment is done differently. We are using helm charts internally and are currently evaluating to open source them as well. The actual secrets are stored in Hashicorp Vault.

Recreating the database and everything

As already mentioned above, we are using the SQL scripts in the runbooks for creation of the database users. In order to construct the necessary tables & perform database migration we delegate the task to Flyway, which manages migration scripts. Flyway uses the dedicated migration user, while the microservices use their assigned database user (submission/distribution) in order to enforce the concept of least privilege.

Variant via functions

Using functions here are a valid option for achieving the database access & control. However, we also need to account for other aspects as well. Therefore we decided to go with Spring Data JPA in order to stick with the ORM approach. In the end, this question of ORM vs database native capabilities is also a question of personal preference and style.

We believe that both approaches are valid, but we decided to stick to the ORM.

Proposed Changes on the Diagnosis Key Table

Those topics were also raised in a couple of days ago, and answered in this https://github.com/corona-warn-app/cwa-server/issues/345#issuecomment-635588014

Thanks again for having a critical eye on the repo and helping us making CWA better.

Chris

4reactions
christian-kirschnickcommented, Jun 6, 2020

Since this topic is gaining a lot of visibility everywhere, I would like to make one thing very clear. The issue description states a false fact:

Currently, the database is used with a PostgreSQL superuser, which has full rights the the database cluster. The Table diagnosis_key is accessible to the "PUBLIC" pseudo user, so every other database user has full access too.

You are referring here to the local development / demo setup, used within the docker compose steps we provided for enabling the community & Fraunhofer Institute to easily set up the CWA server on you local machines.

On any deployed environment we are using, we are neither using a superuser, nor is the table diagnosis_key accessible to PUBLIC.

We realize that the details about the actual deployment are currently not transparently available in the codebase. I hope we can add those in the near future.

Read more comments on GitHub >

github_iconTop Results From Across the Web

What is the Principle of Least Privilege (POLP)? A Best ...
The principle of least privilege can be applied to every level of a system. It applies to end users, systems, processes, networks, databases ......
Read more >
What Is the Principle of Least Privilege and Why is it Important?
Confidentiality involves protecting the secrecy of data, objects, and resources by granting access only to those who need it. Integrity protects ...
Read more >
SQL Server, Part 3: Adopting the principle of least privilege
The principle of least privilege works on the basis of users and systems having the bare minimum privileges needed to carry out their...
Read more >
Security: The Principle of Least Privilege (POLP)
This security best practice is generally referred to as service account isolation and is related to POLP: Using distinct service accounts ...
Read more >
Least Privilege - US-CERT - CISA
Definition 13-1. The Principle of Least Privilege states that a subject should be given only those privileges needed for it to complete its...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found