Skip to content

Commit

Permalink
Security documentation plus improved configuration docs
Browse files Browse the repository at this point in the history
Closes #6, closes #17
  • Loading branch information
simonw committed May 9, 2021
1 parent d2fce53 commit f3eabeb
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ setup
sql
saved-dashboards
widgets
security
contributing
```
Expand Down
12 changes: 12 additions & 0 deletions docs/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Security

Allowing people to execute their own SQL directly against your database is risky business!

Configured correctly, Django SQL Dashboard uses a number of measures to keep your data and your database server safe:

- I strongly recommend creating a dedicated PostgreSQL role for accessing your database with read-only permissions granted to an allow-list of tables. PostgreSQL has extremely robust, well tested permissions which this tool can take dull advantage of.
- Likewise, configuring a PostgreSQL-enforced query time limit can reduce the risk of expensive queries affecting the performance of the rest of your site.
- Setting up a read-only reporting replica for use with this tool can provide even stronger isolation from other site traffic.
- Your allow-list of tables should not include tables with sensitive information. Django's auth_user table contains password hashes, and the django_session table contains user session information. Neither should be exposed using this tool.
- Access to the dashboard is controlled by Django's permissions system, which means you can limit access to trusted team members.
- SQL queries can be passed to the dashboard using a ?sql= query string parameter - but this parameter needs to be signed before it will be executed. This should prevent attempts to trick you into executing malevolent SQL queries by sending you crafted links - while still allowing your team to create links to queries that can be securely shared.
75 changes: 63 additions & 12 deletions docs/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,87 @@ Install this library using `pip`:

Add `"django_sql_dashboard"` to your `INSTALLED_APPS` in `settings.py`.

Define a `"dashboard"` database alias in `settings.py`. It should look something like this:
Add the following to your `urls.py`:

```python
from django.urls import path, include
import django_sql_dashboard

urlpatterns = [
# ...
path("dashboard/", include(django_sql_dashboard.urls)),
]
```

## Setting up read-only PostgreSQL credentials

Create a new PostgreSQL user or role that is limited to read-only SELECT access to a specific list of tables.

If your read-only role is called `my-read-only-role`, you can grant access using the following SQL (executed as a privileged user):

```sql
GRANT USAGE ON SCHEMA PUBLIC TO "my-read-only-role";
```
This grants that role the ability to see what tables exist. You then need to grant `SELECT` access to specific tables like this:
```sql
GRANT SELECT ON TABLE
public.locations_location,
public.locations_county,
public.django_content_type,
public.django_migrations
TO "my-read-only-role";
```
Think carefully about which tables you expose to the dashboard - in particular, you should avoid exposing tables that contain sensitive data such as `auth_user` or `django_session`.

## Configuring the "dashboard" database alias

Django SQL Dashboard defaults to executing all queries using the `"dashboard"` Django database alias.

You can define this `"dashboard"` database alias in `settings.py`. Your `DATABASES` section should look something like this:

```python
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "mydb",
"USER": "read_write_user",
"PASSWORD": "read_write_password",
"HOST": "dbhost.example.com",
"PORT": "5432",
},
"dashboard": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "mydb",
"OPTIONS": {"options": "-c default_transaction_read_only=on -c statement_timeout=100"},
"USER": "read_only_user",
"PASSWORD": "read_only_password",
"HOST": "dbhost.example.com",
"PORT": "5432",
"OPTIONS": {
"options": "-c statement_timeout=100"
},
},
}
```
Even better: create a new PostgreSQL role that is limited to read-only SELECT access to a list of tables, following [these instructions](https://til.simonwillison.net/postgresql/read-only-postgresql-user).
In addition to the read-only user and password, pay attention to the `"OPTIONS"` section: this sets a statement timeout of 100ms - queries that take longer than that will be terminated with an error message.

Add the following to your `urls.py`:
Now visit `/dashboard/` as a staff user to start trying out the dashboard.

```python
from django.urls import path, inclued
import django_sql_dashboard
### Danger mode: configuration without a read-only database user

urlpatterns = [
Some hosting environments such as Heroku charge extra for the ability to create read-only database users. For smaller projects with dashboard access only made available to trusted users it's possible to configure this tool without a read-only account, using the following options:

```python
# ...
path("dashboard/", include(django_sql_dashboard.urls)),
]
"dashboard": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"USER": "read_write_user",
# ...
"OPTIONS": {
"options": "-c default_transaction_read_only=on -c statement_timeout=100"
},
},
```

Now visit `/dashboard/` as a staff user to start trying out the dashboard.
The `-c default_transaction_read_only=on` option here should prevent accidental writes from being executed, but note that dashboard users in this configuration will be able to access _all tables_ including tables that might contain sensitive information. Only use this trick if you are confident you fully understand the implications!

## Additional settings

Expand Down

0 comments on commit f3eabeb

Please sign in to comment.