Security¶
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. |
| 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.formas it came. - Never hand a value of a visitor to the database as a query :
find: { category: String(ctx.query.category) }, notfind: ctx.query. Escape the text of a regular expression. - Never give
raw()what a visitor wrote. - Never write a value of
ctx.userinto a page kept for everyone (cache.scope: 'public'). - Keep
data.allowSysoff 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' }), andallow: ['create']for a table visitors only add to. - Serve the websites over HTTPS (a reverse proxy with TLS) : the session cookie is
Securethen, and browsers getStrict-Transport-Security.