Operate
Security
What Mica protects, who it trusts, the risks it accepts, and how to harden a self-hosted deployment. How to report a vulnerability.
8 min readThis page states what Mica protects, who it trusts, and which risks it accepts. It also tells you how to harden a self-hosted deployment.
Mica is a hobby project of one maintainer and is not independently audited. Read this page before you put a self-hosted Mica on the internet.
Report a vulnerability
Do not open a public issue. Use GitHub private vulnerability reporting: open the repository's Security tab and select Report a vulnerability. The full policy is in SECURITY.md.
What Mica protects
- The repository token. It can write to the content repository. The self-hosted Mica keeps it in its environment and never sends it to the browser.
- The session secret.
SESSION_SECRETencrypts the session cookie (AES-256-GCM). A person who knows it can create a session for any editor. - The drafts. Saved, unpublished changes live in the SQLite database on the data volume. They are not in Git until somebody publishes them.
- The content repository. Only an admitted editor can publish, and only to the configured branch.
Who Mica trusts
- The identity provider. It decides who a person is. Mica decides who may edit, from the rules in
MICA_ADMIT_*. A provider that issues a false identity or false claims gets that person in. - An admitted editor. Every admitted editor can do everything, Publish included. There are no roles.
- A contributor to the content repository. Anything in the repository reaches the editor: content, media and the content model. Give write access to the repository only to people you would also admit as editors.
- The reverse proxy. It terminates TLS. With
TRUST_PROXY=true, Mica believes the client address inX-Forwarded-For. - Processes on a developer's machine. The local editor has no sign-in. It listens only on loopback and answers only to
localhost,127.0.0.1and[::1].
Risks Mica accepts
- Removal at the provider applies at the next sign-in. Mica checks the provider's claims when a person signs in. A person you remove from a group at the provider keeps working until the session ends: at most 8 hours, or 4 hours without activity, by default. To stop a person at once, use
MICA_ADMIT_SUBJECTSor change the rules: Mica checks the rules on every request. - No server-side session revocation. Sessions are encrypted cookies, not database rows. To end every session at once, change
SESSION_SECRETand restart. Every editor must then sign in again. - The local editor is open to local processes. Any process on the developer's machine can call the local API. Such a process can already change the files in the working tree.
- A publish is immediate. There is no review step. If you need one, point Mica at a staging branch and merge on your own schedule.
What Mica does for you
- It signs in with the OIDC authorization code flow, with PKCE,
stateandnonce. - Session cookies are
HttpOnlyandSameSite=Lax. Over https they are alsoSecure, with a__Host-name, so that another subdomain cannot set or replace them. - Over https, every response carries
Strict-Transport-Security. In production, the self-hosted Mica refuses to start with anhttp://public address, except on localhost. - Every write needs a matching CSRF token, and Mica refuses cross-origin writes.
- Sign-in attempts are rate limited.
- Responses carry a same-origin
Content-Security-Policy,X-Content-Type-Options: nosniff,X-Frame-Options: DENYandReferrer-Policy: same-origin. - The editor loads nothing from another origin. Its font is served by Mica itself.
- Mica refuses SVG files, because they can contain scripts: it does not accept them as uploads and does not show them in the editor. Media files are served in a sandbox, and files that are not images are sent as downloads.
- The self-hosted Mica refuses to start when no admission rule is set.
Hardening a self-hosted deployment
- Put Mica behind a reverse proxy with HTTPS.
- Set
MICA_PUBLIC_URLto thehttps://address. In production, Mica refuses anything else. - Set
TRUST_PROXY=trueonly behind a proxy that you control. Otherwise, clients can spoof their address and avoid the rate limit. - Give the repository token access to the content repository only: on GitHub, a fine-grained token with contents write on that one repository; on GitLab, a project access token.
- Generate
SESSION_SECRETwith at least 32 random characters, for exampleopenssl rand -base64 48, and keep it out of Git. - Admit as few people as you need. Prefer a group at your provider to a whole domain.
- Keep the image up to date. Only the latest release gets security fixes.
- Back up the data volume. It holds the drafts.