Skip to content
Security

How we keep your users' accounts safe

Anchr.ing holds your users' identities, so security is the product. This is how it is built, how we run it, and how to tell us about a vulnerability.

Isolation by design

Every customer in its own namespace. A bug in our code can't reach another customer's data.

Every customer has its own namespace with separate test and production databases. Our server reaches each environment only through that environment's own scoped database user, so a query that forgets a filter, or a token from one customer used against another, is refused by the database itself.

  • The customer comes from the address the request was sent to, never from a token or a form
  • Each environment has its own users, apps, sessions, signing key and database user: a test token fails in production
  • The request-serving process never holds the database's root credentials
How customers are kept apartEach customer has its own namespace with separate Test and Production databases. Each environment holds its own users, apps, sessions and tokens, signing key and database user. A request from customer A's production app reaches only A's production database: a query from it towards customer B is refused by the database itself, and test never trusts production. A's production app Anchr.ing server Customer Aown namespace TestUsersAppsSessions, tokensSigning keyDatabase user ProductionUsersAppsSessions, tokensSigning keyDatabase user Test never trusts production Customer Bown namespace TestUsersAppsSessions, tokensSigning keyDatabase user ProductionUsersAppsSessions, tokensSigning keyDatabase user The databaserefuses How customers are kept apartEach customer has its own namespace with separate Test and Production databases. Each environment holds its own users, apps, sessions and tokens, signing key and database user. A request from customer A's production app reaches only A's production database: a query from it towards customer B is refused by the database itself, and test never trusts production. A's production app Anchr.ing server Customer Aown namespace TestUsersAppsSessions, tokensSigning keyDatabase user ProductionUsersAppsSessions, tokensSigning keyDatabase user Test never trusts production Customer Bown namespace Test Production The database refuses
The second layer

Keys live in a separate key service

Isolation stops bugs. For the worse case, a web server taken over by an attacker, the keys are somewhere else: the web servers never hold signing keys or the master key, so an attacker in a web server can't take keys away.

Signing without the key

Tokens are signed inside the key service, a separate process with no network access of its own. The web server sends what needs signing and gets back only the signature.

Secrets decrypted one at a time

The master key and each project's data keys never leave the key service. It decrypts one field at a time, only for the project and purpose it was stored for.

Database access for minutes

The web servers hold no database passwords. The key service gives them access tokens that expire after ten minutes, one project environment each.

Every key operation counted

Each caller proves who it is on every request. Every operation is written to a tamper-evident log, and each project has a rate limit on signing and decryption.

No fallback

If the key service can't be reached, sign-ins pause with a clear error. The web servers never fall back to keys of their own.

What it doesn't stop

While an attacker is inside a web server, they can ask the key service for signatures and decryptions as the server does. Rate limits slow that down and the log records it; removing them ends it.

How it is built

Isolation and encryption by default

Every project kept apart

Each project's test and production environments are separate databases with their own database users, signing keys and secrets. The project comes only from the host name, never from a token or a request body.

Passwords as argon2id

Passwords are hashed with argon2id (64 MiB, 3 passes). Codes, tokens and client secrets are stored only as SHA-256 or keyed HMAC hashes, and shown once.

Keys encrypted per project

Signing keys, authenticator secrets and webhook secrets are encrypted with AES-256-GCM under a data key per project environment, itself encrypted with a master key that only the key service holds, kept apart from backups.

Signed tokens, per environment

Ed25519 signing keys per environment, so a test token never works in production. Your API verifies tokens offline against the published keys.

Secrets that scanners find

Every secret we hand out starts with anchr_ and ends in a checksum, so secret scanners can spot a leaked one and we can revoke it without guessing.

No third-party scripts on sign-in

Sign-in pages load no third-party scripts, and the dashboard runs no analytics. Only these public pages are measured, by analytics we host ourselves in the EU.

How we run it

Hardened where it runs

Backups encrypted to an offline key

Every backup is encrypted to a key whose private half is kept offline, never on our servers. A stolen backup is unreadable, and a backup is checked for tampering before it is ever restored.

Minimal images

The service runs in images with only the runtime and our code: no shell, no package manager, a read-only file system and no privileges. A bug that runs code finds nothing to work with.

Restricted network access

The database has no route to the internet. The service may only open encrypted connections to public addresses, for sign-in providers, webhooks and mail, never to internal ones.

Watching for unusual access

We alert when one process reaches unusually many projects or signs and decrypts unusually much for one. A decoy project with fake credentials raises an alarm the moment anyone touches it. Hidden traps on the sign-in and admin pages, which people never see, end the session of any script that trips one and block it for a while.

Threat model, in short

We keep a written threat model of the service and update it as the code changes. It is the brief for an external security review before general availability. The main threats and what stops them:

  • One customer attacking another. The project comes from the host name only; each project environment has its own database users; the process that serves requests never holds the database's root credentials.
  • Stolen or forged tokens. Short-lived access tokens, refresh tokens that rotate on use, signing keys per environment, and a recent second factor for sensitive actions where you ask for it.
  • Requests to addresses a customer chooses (webhooks, identity providers): sent through a guarded client that refuses private and internal addresses.
  • Support access to a user's account. It is real impersonation, so it is off by default and needs a reason, a second factor and a time limit. It is audited, and the user is told.
  • A compromised web server. It holds no keys, so an attacker can't take keys away, and nothing they saw keeps working after they are removed beyond ten-minute database tokens. While inside, though, they could read projects' data and ask the key service for signatures and decryptions as the server does. Per-project rate limits slow that down, every key operation is logged, and we watch for unusual access. We keep the server's dependencies few, its image minimal and its network access narrow, and the external review spends its time there.

Reliability

Anchr.ing runs on one server at Hetzner in the EU. There is no high-availability setup yet, and we would rather say so than imply one:

  • Recovery point: 24 hours. A backup runs every night, so at most a day of changes is at risk.
  • Recovery time: 4 hours. The time to restore the service from a backup, which we practise.
  • Backups kept for 30 days, in the EU, encrypted to a key kept offline. Secrets stay encrypted inside them too; the master key is kept apart.
  • Your API keeps working while we are down. It checks tokens offline with cached keys; new sign-ins and refreshes wait until we are back.
  • No uptime promise yet. The status page shows the live state and any incidents.

Support: hello@anchr.ing, business hours (CET), first reply within one business day.

Where your data lives

All service data is stored in the EU, with Hetzner in Germany and Finland, and sign-in mail goes through Brevo in France. Payments for Anchr.ing plans happen on the payment page of Adyen in the Netherlands: card and bank details never reach our servers. The sub-processor list names every company that processes personal data for us, what it sees and where. The Data Processing Agreement covers your users' data and the measures above.

Report a vulnerability

Email security@anchr.ing with what you found, how to reproduce it and what it affects. We confirm within one business day, keep you posted while we fix it, and credit you when it is fixed if you want.

  • Test only against your own account and projects. Don't access or change other people's data.
  • No denial-of-service or load tests, no social engineering, no spam.
  • Give us reasonable time to fix it before you publish.

We won't pursue research done in good faith within these rules. There is no paid bug bounty yet. Machine-readable: security.txt.