# Security - SSR websites

> What the API Maker server does to keep SSR websites and their data safe by default - escaping, CSP with nonces, CSRF tokens, sealed sessions, tables and rows a website may reach, open redirects, isolation of websites, safe caching - and what is left to you.

Source: https://docs.apimaker.dev/v4/docs/ssr/security.html

A website runs code next to the data of the account. The server is built so that the code you write is safe by default, and so that a mistake opens as little as it can.

## What the server does

| Risk | What the server does |
|---|---|
| **A script slipped into a page** (XSS) | Every value is escaped where it is written, in a text and in an attribute. `javascript:` and `vbscript:` addresses are refused when a file is saved, and when a value gives one. HTML which is not escaped is asked for by name : `raw()`, `dangerouslySetInnerHTML`. The text of a `<script>` or a `<style>` can not end its element. |
| **Scripts of others** | A Content Security Policy on every page : the scripts of the website, of the UI libraries the page loads, and the ones with the nonce of the answer (a new one each time). Every `<script>` a page writes gets the nonce by itself ; attributes such as `onclick` do not run. |
| **A post in the name of a visitor** (CSRF) | Every `<form method="post">` gets the CSRF token of its visitor ; a `POST`, `PUT`, `PATCH` or `DELETE` without a valid token is refused (`403`) before any code runs. The cookies of a website are `SameSite=Lax`. |
| **A stolen or forged session** | A session is a cookie sealed with AES-256-GCM, under a key of the installation and of the website : it can not be read nor changed by the browser, and a session of one website is nothing to another. `HttpOnly`, `Secure` over HTTPS, for the path of the website. The cookies of API Maker are never seen by a page. |
| **Reading what a website should not** | `ctx.db` reaches the tables `site.config.ts` lists, nothing else. A rule keeps the rows of each visitor (`$user`), checks a row read or changed by its id, and checks the tables `deep` and paths read on the way. Writes may be kept to `create`. See [data](https://docs.apimaker.dev/v4/docs/ssr/data.html). |
| **More than the visitor may** | `ctx.api` calls the APIs as the visitor : their access, groups, tokens and hooks. `ctx.sys` is off unless the website turns it on. |
| **A page opened by whom it should not** | `access` of a page and of its layouts, read when the file is saved : a visitor who may not is refused before any code of the page runs. A setting which can not be read closes the page, it never opens it. |
| **A redirect to another site** (open redirect) | `redirect()` refuses another host unless the code says `{ external: true }` : a `?next=` a visitor wrote never sends to another site. |
| **A request to an inner server** (SSRF) | `ctx.fetch` reaches the hosts `data.fetch` lists only. |
| **A page shown in a frame of another site** (clickjacking) | `frame-ancestors 'self'` and `X-Frame-Options: SAMEORIGIN` unless the website lists who may frame it. |
| **A kept page shown to someone else** | A page is kept for everyone only when it says so and the visitor is anonymous ; never one which read cookies, headers, the address or the token of its visitor, set cookies, or showed one-time messages. |
| **One website against another** | Each website runs in engines of its own, with a limit of memory (`ssr.memoryMb`) and of time (`ssr.cpuMs`, `ssr.timeoutMs`). The objects of JavaScript are frozen : a page can not change them for the pages after it. Nothing of one website is seen by another. |
| **Errors which say too much** | A visitor reads the message of an error thrown on purpose, and a sentence for any other : the stack and the lines of your files are shown in the preview of the panel only. |
| **The browser guessing** | `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`, `Cross-Origin-Opener-Policy: same-origin`, `Permissions-Policy` (no camera, microphone, geolocation, payment), `Strict-Transport-Security` over HTTPS. |

## The CSP

| `security.csp` | The policy |
|---|---|
| `'strict'` (default) | `default-src 'self'` ; scripts of the website, of its libraries and with the nonce ; styles of the website, of its libraries and inline ; images from the website, `data:`, `blob:` and any HTTPS address ; fonts of the website and of its libraries ; requests (`fetch`) to the website ; forms post to the website ; no `<object>` ; frames of the same origin. |
| `'relaxed'` | What HTTPS gives, inline scripts and `eval` included : for pages which load what they find. |
| `'off'` | No policy. |
| any other text | The policy itself, `{nonce}` written as the nonce of the answer. |

A script of the page which needs the nonce in a value of its own (a library which adds scripts) finds it on its own `<script>` element : `document.currentScript.nonce`.

## The panel and the websites

Serve the admin panel and the websites on **different origins** : another host, or another port. The panel of API Maker runs on a port of its own by default (`adminPanel.port`), so a website at `/ssr/<apiPath>/<name>` of the API port shares nothing with it. Behind a reverse proxy, give the panel and the websites different hosts : a website then can not read what the panel keeps in its browser.

## What is left to you

- **Check every field a visitor sends**, on the server : what a browser checks a request skips. Take the fields you name, never `ctx.form` as it came.
- **Never hand a value of a visitor to the database as a query** : `find: { category: String(ctx.query.category) }`, not `find: ctx.query`. Escape the text of a regular expression.
- **Never give `raw()` what a visitor wrote.**
- **Never write a value of `ctx.user` into a page kept for everyone** (`cache.scope: 'public'`).
- **Keep `data.allowSys` off** unless a page needs everything the account may do ; then check every value which reaches it.
- **List the tables a website needs, no more**, with the rows of each visitor (`where: { owner: '$user._id' }`), and `allow: ['create']` for a table visitors only add to.
- **Serve the websites over HTTPS** (a reverse proxy with TLS) : the session cookie is `Secure` then, and browsers get `Strict-Transport-Security`.
