Lewati ke konten

Security

Konten ini belum tersedia dalam bahasa Anda.

Email is attacker-controlled content that you have asked to be shown. The design assumes every message is hostile.

Bodies are stored as sent — their CSS, tables, and layout intact, because mangling them is what makes self-hosted mail clients unpleasant. Safety comes from constraining what the page may do, not from rewriting it.

Each body is served under:

Content-Security-Policy:
default-src 'none';
style-src 'unsafe-inline';
img-src 'self' data:; ← widened only when you show images
font-src data:;
sandbox
  • sandbox gives the document a unique origin and disables scripting. It applies even if the URL is opened directly in a tab, not only inside the frame.
  • The reading pane frames it with an empty sandbox attribute, so the message never shares the dashboard’s origin.
  • img-src is what withholds remote images. Blocking is a header concern, which is precisely what allows the markup to be left alone.

Obvious active content is also stripped at ingest — <script> blocks, plain on* handlers, and javascript:/vbscript: URLs — which costs no fidelity because none of it affects how a message looks.

Treat that as tidying, not as a defence. It is regex over HTML, and HTML has more ways to write an event handler than a regex can enumerate: a / instead of a space before onerror, an entity-encoded scheme, a tab inside javascript:, srcdoc, <meta http-equiv=refresh>. All of those get past it.

The CSP above is what actually stops scripts running, and it does not depend on the stripper being thorough. An earlier version of this page claimed the stripper would still hold if the CSP were misconfigured; that was wrong, and saying so mattered more than the sentence sounded.

Always served with Content-Disposition: attachment and X-Content-Type-Options: nosniff. Nothing opens in place.

Embedded images are inlined as data: URIs when the body is served, capped per file and in total. SVG is excluded — it can carry script, and inlining it would hand it the document’s origin.

Unknown addresses that fail the catch-all guards are rejected with setReject() at the SMTP level, so they never reach storage. The daily budget for auto-created aliases means a spammer guessing addresses cannot mint unlimited rows, and it counts only auto-created ones — aliases you made by hand never consume it.

Over the limit, senders get a temporary failure and retry, so a real message caught in someone else’s burst is delayed rather than lost.

  • The session password is stored only as a salted PBKDF2 hash — the plaintext never leaves your machine. It and the cookie signing key are Worker secrets, so neither appears in the Worker’s plain-text settings.
  • The two are separate values on purpose. Signing the cookie with the password hash, as an earlier release did, meant that reading the hash was enough to mint a session.
  • The Cloudflare API token is not saved unless you opt in during setup; when saved it goes to ~/.mailriz/config.json with mode 600.
  • mailriz-cli status reports whether a token is on disk, never its value.
  • Anyone with your Cloudflare account can read the mailbox directly through D1 and R2. MailRiz protects the dashboard, not your Cloudflare login — treat that account’s security as the real perimeter.
  • Mail in transit is subject to whatever the sender negotiated. MailRiz receives what Cloudflare accepts.
  • Access JWTs are signature-verified by the Worker against the team domain’s JWKS, plus audience, issuer, and expiry. See Authentication.