Skip to content

Commit

Permalink
docs: add a short guide on security. (#2201)
Browse files Browse the repository at this point in the history
Covering data access and encryption:

https://deploy-preview-2201--electric-next.netlify.app/docs/guides/security

---------

Co-authored-by: Kyle Mathews <[email protected]>
  • Loading branch information
thruflo and KyleAMathews authored Dec 20, 2024
1 parent 9b4616b commit caabf7c
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 4 deletions.
1 change: 1 addition & 0 deletions website/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export default defineConfig({
{ text: 'Writes', link: '/docs/guides/writes' },
{ text: 'Installation', link: '/docs/guides/installation' },
{ text: 'Deployment', link: '/docs/guides/deployment' },
{ text: 'Security', link: '/docs/guides/security' },
{ text: 'Troubleshooting', link: '/docs/guides/troubleshooting' },
{ text: 'Client development', link: '/docs/guides/client-development' },
]
Expand Down
4 changes: 3 additions & 1 deletion website/docs/guides/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ You can proxy the request in your cloud, or at the edge, [in-front of a CDN](#cd

### Rules are optional

You *don't* have to codify your auth logic into a database rule system. There's no need to use database rules to secure a sync engine when it runs over standard HTTP.
You *don't* have to codify your auth logic into a database rule system.

There's no need to use database rules to [secure data access](/docs/guides/security) when your sync engine runs over standard HTTP.

## Patterns

Expand Down
19 changes: 16 additions & 3 deletions website/docs/guides/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ description: >-
outline: [2, 3]
---

<script setup>
import ComponentsJPG from '/static/img/docs/guides/deployment/components.jpg?url'
import ComponentsPNG from '/static/img/docs/guides/deployment/components.png?url'
import ComponentsSmPNG from '/static/img/docs/guides/deployment/components.sm.png?url'
</script>

<img src="/img/icons/deploy.png" class="product-icon"
style="width: 72px"
/>
Expand All @@ -28,11 +34,11 @@ An Electric deployment has three main components. Your Postgres database, the El
Electric connects to your Postgres using a `DATABASE_URL`. Your app connects to Electric [over HTTP](/docs/api/http), usually using a [Client library](/docs/api/clients/typescript).

<figure>
<a href="/img/deployment/components.jpg">
<img src="/img/deployment/components.png" class="hidden-sm"
<a :href="ComponentsJPG">
<img :src="ComponentsPNG" class="hidden-sm"
alt="Illustration of the main components of a successfull deployment"
/>
<img src="/img/deployment/components.sm.png" class="block-sm"
<img :src="ComponentsSmPNG" class="block-sm"
style="max-width: 360px"
alt="Illustration of the main components of a successfull deployment"
/>
Expand All @@ -51,6 +57,13 @@ You also often want to proxy requests to Electric through your API, or other pro

Note also that, when running Electric behind a CDN, you may want your proxy in front of the CDN. This is where primitives like [edge functions](/docs/integrations/supabase#sync-into-edge-function) and [edge workers](/docs/integrations/cloudflare#workers) can be very useful.

### Securing data access

By default, Electric exposes public access to the contents of your database. You generally don't want to expose the contents of your database, so you need to [lock down access](/docs/guides/security#secure-data-access) to the Electric HTTP API.

See the [Security guide](/docs/guides/security) for information.


## 1. Running Postgres

You can use ***any standard Postgres***, version 14 and above.
Expand Down
103 changes: 103 additions & 0 deletions website/docs/guides/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: Security - Guide
description: >-
How to secure data access and encrypt data with Electric.
outline: [2, 3]
---

<script setup>
import ComponentsJPG from '/static/img/docs/guides/deployment/components.jpg?url'
import ComponentsPNG from '/static/img/docs/guides/deployment/components.png?url'
import ComponentsSmPNG from '/static/img/docs/guides/deployment/components.sm.png?url'
</script>

<img src="/img/icons/security.svg" class="product-icon"
style="width: 72px"
/>

# Security

How to secure data access and [encrypt data](#encryption) with Electric.

## Data access

Electric is a [sync service](/product/electric) that runs in front of Postgres. It connects to a Postgres database using a [`DATABASE_URL`](/docs/api/config#database-url) and exposes the data in that database via an [HTTP API](/docs/api/http).

<figure>
<a :href="ComponentsJPG">
<img :src="ComponentsPNG" class="hidden-sm"
alt="Illustration of the main components of a successfull deployment"
/>
<img :src="ComponentsSmPNG" class="block-sm"
style="max-width: 360px"
alt="Illustration of the main components of a successfull deployment"
/>
</a>
</figure>

This API is [public by default](#public-by-default). It should be secured in production using [network security](#network-security) and/or an [authorization proxy](#authorization).

### Public by default

Electric connects to Postgres as a normal [database user](https://www.postgresql.org/docs/current/user-manag.html). It then exposes access to **any&nbsp;data** that its database user can access in Postgres to **any&nbsp;client** that can connect to the Electric HTTP API.

You generally do _not_ want to expose public access to the contents of your database, so you **must** secure access to the Electric HTTP API.

### Network security

One way of securing access to Electric is to use a network firewall or IP whitelist.

You can often configure this using the networking rules of your cloud provider. Or you can use these to restrict public access to Electric and only expose Electric via a reverse-proxy such as Nginx or Caddy. This reverse proxy can then enforce network security rules, for example, using Caddy's [`remote-ip` request matcher](https://caddyserver.com/docs/caddyfile/matchers#remote-ip):

```hcl
@denied not remote_ip 100.200.30.40 100.200.30.41
abort @denied
```

This approach is useful when you're using Electric to sync into trusted infrastructure. However, it doesn't help when you're syncing data into client devices, like apps and web browsers. For those, you need to restrict access using an authorizing proxy.

### Authorization

Electric is designed to run behind an [authorizing proxy](/docs/guides/auth#requests-can-be-proxied).

This is the primary method for securing data access to clients and apps and is documented in detail, with examples, in the [Auth guide](/docs/guides/auth).

## Encryption

Electric syncs ciphertext as well as it syncs plaintext. You can encrypt and decrypt data in HTTP middleware or in the local client.

### End-to-end encryption

For example, you can achieve end-to-end encryption by:

- *encrypting* data before it leaves the client
- *decrypting* data when it comes off the replication stream into the client

You can see an example of this in the [encryption example](/demos/encryption):

<<< @../../examples/encryption/src/Example.tsx{tsx}

### Key management

One of the primary challenges with encryption is key management. I.e.: choosing which data to encrypt with which keys and sharing the right keys with the right users.

Electric doesn't provide or prescribe any specific key management solution. You're free to use any existing key management system, such as Hashicorp Vault, for key management. However, for end-to-end encryption of shared data, you will at some point need to share keys between clients. This is a job that Electric is good at: syncing the right data to the right users.

For example, imagine you store keys in a seperate, extra secure, Postgres database and you segment your encryption by tenant (or group, or some other shared resource). You could sync keys to the client using a shape like this:

```ts
import { ShapeStream } from '@electric-sql/client'

const stream = new ShapeStream({
url: `${ELECTRIC_URL}/v1/shape`,
params: {
table: 'tenants',
columns: [
'keys'
],
where: `id in ('${user.tenant_ids.join(`', '`)}')`
}
})
```

You could then put a denormalised `tenant_id` column on all of the synced tables in your main database and lookup the correct key to use when decrypting and encrypting the row in the client.
15 changes: 15 additions & 0 deletions website/public/img/icons/security.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit caabf7c

Please sign in to comment.