Security at BlockAlt
How the service is built, what we keep and for how long, and how to report a vulnerability. This page describes the code of the current release; the changelog lists what is already deployed.
Not in your traffic path
BlockAlt is not a proxy. Your visitors talk to your servers; your backend asks us about one token per action.
No visitor tracking
No analytics, fingerprinting, session replay or cross-site identifiers. These public pages set no cookies.
Events kept for seven days
The site, the outcome, the reason and the time. Never raw IP addresses, tokens, secrets or request bodies.
Hosted in Helsinki, Finland
A dedicated virtual machine (Hetzner), with PostgreSQL on a private network without a public port.
What we keep, and for how long
Everything BlockAlt records about your visitors, with its retention. Errors are logged without request bodies, tokens, secrets or IP addresses.
| Data | What it holds | Kept for |
|---|---|---|
| Protection events | The site, the outcome, the reason and the time. They never contain raw IP addresses, tokens, secrets or request bodies. | 7 days |
| Refusals of tokens we did not issue | Forged, malformed, empty, expired or another site's tokens get no event: they are counted per site, minute and reason. | 7 days |
| Rate-limit counters | Per visitor network (an IPv4 address or an IPv6 /64) in short-lived counters held in memory; counters kept in the database use keyed hashes (HMAC-SHA-256 with a server secret), never the address. They never reach logs or events. | Minutes |
| Consumed tokens and challenges | Random identifiers only, to stop replays. | Until an hour after they expire |
Tokens, signing and secrets
Which keys protect what, and how every secret is kept at rest.
Tokens and challenges
- Authenticated with HMAC keys derived on the server and identified by a key id, so they can be rotated without touching customer pages.
- Challenges expire after 90 seconds and tokens after 120 seconds. Each works once.
- Bound to the site, hostname, action and published configuration version; the server re-checks every claim at verification.
- Each token carries a per-token keyed hash of the network that solved it, checked when your server sends
remoteip.
Signed, pinned configuration
- The browser script is served under a content-addressed URL that you pin with Subresource Integrity.
- It accepts site configuration only when it is signed (Ed25519, or P-256 for browsers without Ed25519) by a key you pinned in your page.
- Standby keys allow rotation without breaking installs. Those keys sign configuration only.
In transit and in webhooks
- All traffic uses TLS, with HSTS on every public page.
- SIEM notifications are signed with HMAC-SHA-256 over a timestamp and the raw body, using a per-endpoint secret shown once, so receivers can refuse replays.
How each secret is kept
Hashes where we only need to compare; encryption where the value must be read back.
- Passwords
- Hashed with Argon2id. New passwords are checked against known breaches with the Have I Been Pwned range API: only the first 5 characters of the password's SHA-1 hash leave our servers.
- Site secret keys
- Shown once and stored as SHA-256 hashes; revoking one takes effect on the next verification.
- Sessions, emailed links and API keys
- Session cookies, sign-up, password-reset, email-change, deletion and invitation links and management API keys are stored only as hashes.
- Recovery codes
- Stored as keyed hashes (HMAC-SHA-256 with a server secret).
- Passkeys
- Stored as public keys.
- Authenticator secrets, webhooks and queued email
- Authenticator (TOTP) secrets, notification webhook URLs and signing secrets, and queued email are encrypted with AES-256-GCM, each sealed to its own record.
How the service is built
Where BlockAlt runs, and what it is deliberately not.
Not in your traffic path
BlockAlt is not a proxy. Three separate conversations, and only the last two involve us:
- Your visitorsyour serversPages, forms and data. BlockAlt never sees this traffic.
- Browser scriptBlockAlt APISigned configuration, a challenge, and a single-use token.
- Your backend
POST /v1/siteverifyOne call per action, with your secret key.
Deliberately absent: visitor analytics, fingerprinting, session replay and cross-site identifiers.
Where it runs
- Location
- A dedicated virtual machine in Helsinki, Finland (Hetzner).
- Software
- One API process and one worker, written in TypeScript on Bun, with PostgreSQL.
- In front
- Cloudflare DNS and TLS proxying, and a TLS reverse proxy on the host. The host's reverse proxy logs no visitor addresses or URLs.
Containers
- Every container has a read-only root filesystem and cannot gain privileges.
- The API, worker and migration containers run as a non-root user with all Linux capabilities dropped.
- PostgreSQL runs as its own unprivileged user with no capabilities (its start-up script keeps only the four it needs to hand the data directories to that user) and sits on a private network without a public port.
Account security
Every workspace is managed with two factors, short sessions and an audit log.
Passwordless sign-up
Sign-up asks only for an email address, and the person who opens the emailed link chooses the password, so an address cannot be claimed in advance. Repeated sign-ups send no repeated email.
Two factors, always
A verified email address and an authenticator app (TOTP) are required to manage any workspace; passkeys (WebAuthn, with user verification) can replace the six-digit code. Recovery codes work once.
Wrong codes slow down
Five wrong codes in a row pause code checks (15 minutes, doubling up to a day) with an email; a password-reset link also needs the second factor and dies after three wrong codes.
Short sessions
Console sessions end after 12 hours without activity and after 7 days in any case, in an HttpOnly,
SameSite=Strictcookie with the Secure flag and__Host-prefix in production. Every change checks the exact Origin and a per-session CSRF token. You can list your sessions (browser and system family only) and end any of them.A fresh second factor
Sensitive changes (password, email, second factor, API keys, roles, ownership, deletion, and on a site: server keys, deletion, pausing or locking down protection, notification destinations) need a second factor entered in the last 15 minutes.
Security notices
Email notices tell you about password, email, second-factor and passkey changes, recovery-code use, sign-ins from a new browser or network, and API-key and team changes. See accounts and security.
Roles and audit log
Workspaces have owner, admin and member roles, enforced on the server. An audit log records who changed what, by email or API key name. Management API keys are scoped, expiring, hashed and cannot manage the team.
No lockout by flooding
Sign-in, sign-up and password reset have per-network limits but no global limit anyone could exhaust; password hashing runs in a bounded queue that serves networks in turn; the forms carry BlockAlt's own proof of work (always for sign-up and reset, after failed attempts for sign-in), so mass attempts cost the attacker CPU instead of locking out customers.
Domains checked daily
Verified domains are checked again daily; a hostname whose DNS record stays missing for 7 days is marked unverified, after email warnings.
Backups and recovery
Encrypted dumps
Database backups are consistent dumps encrypted with age together with the matching keys, so a restore can open MFA secrets and verify signatures.
Restores rehearsed
Restores always target a new, isolated database. The encrypted restore is rehearsed automatically in continuous integration.
Objectives being measured In progress
Off-site replication, backup frequency and recovery objectives for production are being validated before general availability; we will publish them here once measured.
Public pages
This site sends a strict set of security headers on every page:
Content-Security-Policywithout inline scripts, with Trusted Types enforcedStrict-Transport-Security(HSTS)frame-ancestors 'none'Cross-Origin-Opener-PolicyandCross-Origin-Resource-Policy- A restrictive
Permissions-Policy
The public pages set no cookies and run no inline script.
Report a vulnerability
Email support@blockalt.click with the affected URL or component, the steps to reproduce, what an attacker could achieve, and how to reach you. Never include another customer's data, and never send a site secret key: rotate it in the console instead.
We acknowledge every report, keep you informed while we fix it, and credit you on the changelog if you wish. There is no paid bug bounty at the moment.
The same contact, machine-readable, in /.well-known/security.txt:
Contact: mailto:support@blockalt.click
Expires: 2027-05-08T00:00:00.000Z
Preferred-Languages: en
Canonical: https://blockalt.click/.well-known/security.txt
Policy: https://blockalt.click/security#disclosureSafe harbour
If you act in good faith and follow these rules, we will not pursue legal action against your research:
- Test only against accounts, workspaces and sites you own. Stop and tell us as soon as you reach data that is not yours, and do not keep it.
- Do not run denial-of-service or high-volume tests, spam, social engineering or physical attacks. Proof-of-work cost, rate limits and the documented limits of the product are not vulnerabilities on their own.
- Give us reasonable time to fix the issue before you publish, and coordinate the date with us.