CI / check (push) Has been cancelled
The file named security@heidrich-digital.de, which I had assumed rather than checked — a security contact that bounces is worse than none. Replaced with the maintainer's actual address. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nh4A79bbqT9dcd2inzYUdg
44 lines
2.0 KiB
Markdown
44 lines
2.0 KiB
Markdown
# Security
|
|
|
|
## Reporting a vulnerability
|
|
|
|
Please do **not** open a public issue. Mail **mail@tillheidrich.de** with
|
|
what you found and how to reproduce it. You will get an acknowledgement within a
|
|
few days. There is no bounty programme — this is a one-person project — but
|
|
credit in the release notes is yours if you want it.
|
|
|
|
## What Klarbild already does
|
|
|
|
- Passwords are hashed with argon2. Stored credentials (SMTP, delivery targets,
|
|
API keys) are encrypted with AES-256-GCM using `ENCRYPTION_KEY`, never written
|
|
in plain text.
|
|
- Sessions are a signed cookie **plus** a server-side check. Changing a password
|
|
or a role invalidates every open session; the role comes from the database, not
|
|
from the cookie.
|
|
- Content-Security-Policy with per-script hashes rather than `'unsafe-inline'`,
|
|
set at build time. `style-src` keeps `'unsafe-inline'` because React writes
|
|
styles as element attributes, which cannot be hashed — a narrower class of
|
|
problem than script injection, which stays closed.
|
|
- Remote paths are validated immediately before writing, not somewhere upstream:
|
|
`posixpath.join('/customers/acme', '../../../etc/cron.d')` is `/etc/cron.d`, and
|
|
the upload would have created the directory first. See `safeSegment` in
|
|
`src/lib/remotetarget.ts` and its tests.
|
|
- Share links: expiry, revocation, optional argon2 passphrase with a rate limit,
|
|
`noindex`, and access bound to the link rather than to the image. Visit logging
|
|
hashes the IP with a server secret and the link id, keeps one row per visitor
|
|
per hour, and deletes after 90 days.
|
|
- A module that is switched off returns 404, not 403.
|
|
|
|
## What it does not do
|
|
|
|
- There is no multi-tenant isolation. Every account on an instance is on the same
|
|
instance; roles are `user` and `admin` and that is the whole model.
|
|
- Klarbild expects to run behind a reverse proxy that terminates TLS. It does not
|
|
do TLS itself.
|
|
- `ENCRYPTION_KEY` has no rotation path yet. Losing it means re-entering the
|
|
stored credentials.
|
|
|
|
## Supported versions
|
|
|
|
The latest release. There is no long-term support branch.
|