Security
Who has access to what, how it is checked and what is left to the deployment.
In short
Every token works only in its own bucket. The database stores its hash, not its value.
With the panel on, the management API answers only the panel itself, inside the container.
Five failed sign-ins within 15 minutes — and the address is locked for 5 minutes.
Temporary links are signed with the server key: they cannot be forged or extended.
Access tokens
- A token is issued for one bucket and gives access only to it.
- The database stores the token’s hash. The value itself is shown once, when issued; it cannot be recovered — only reissued.
- Two levels:
BASIC(only the check and downloads with the token) andFULL(everything). GiveBASICto those who only need to read. - A token can be limited in time, disabled or reissued (rotation). After rotation the old value stops working at once.
- Every request checks that the token is known, active, not expired and of a sufficient
level. The answers are
401(no token, or it is unknown) and403(disabled, expired, not enough rights).
Admin panel sign-in
There is one account, set by the ADMIN_LOGIN and ADMIN_PASSWORD variables.
- Without them, sign-in is closed completely. This is by design: a panel without credentials opens to no one, rather than to everyone.
- The session lives 24 hours. It is kept in the
s5w_admincookie with theHttpOnlyandSameSite=Strictflags: a script on the page cannot read it, and another site cannot send a request on the administrator’s behalf. - The session is signed with the
WINTER_KEYkey and bound to the login and password. Changing the password or the key ends every open session at once. - Brute-force lockout. Five failed sign-ins from one address within 15 minutes lock that
address for 5 minutes: sign-in answers
429with aRetry-Afterheader, even with the right password. The counter is kept in memory and resets on restart. - The client address behind a proxy is taken from
X-Forwarded-For, but only from trusted proxies (TRUSTED_PROXIES). Otherwise anyone could send someone else’s address and get around the lockout.
The default password
The examples use change-me. In production set a long random password and keep it as a
secret, not in a plain compose file in the repository.
Admin API
The management API /admin/… creates buckets, tokens and everything else — it is full access
to the storage.
- With the panel on (the default) it accepts requests only from inside the container,
that is, from the panel itself. From outside —
404, as if it did not exist. ADMIN_API=publicopens it to the outside — for automation scripts, for example. Then close port9090or the/adminpath on the proxy for everyone but the addresses that need it.- Without the panel (
ADMIN_UI=false) it is open by default — otherwise there would be nothing to manage buckets with. Close/adminat the network level.
File visibility
A file is either public or not; the folder decides.
- The bucket root is always public. A file uploaded or moved there opens at
/o/{bucketId}/{id}without a token. - A file in a folder takes the folder’s visibility. When moved between folders, its visibility changes to that of the new folder.
- The public address contains the bucket id and a random file id (96 bits of randomness) — it cannot be guessed by brute force. But whoever got the link can pass it on.
Moving to the root
A private document moved to the root becomes available to anyone who has the address. Keep private files in private folders.
Temporary links
- A link is signed with the
WINTER_KEYkey: the expiry, the file and the parameters are part of the signature and cannot be changed. - A tracked link (
revocableormaxDownloads) can be revoked on its own. - All links of a bucket, signed ones included, are revoked by a single
DELETE /v1/linksrequest or a button in the panel. - Changing
WINTER_KEYvoids every temporary link in every bucket.
Uploaded content
- HTML and SVG never open in the browser — they are only downloaded. Shown by the browser, they could run a script on behalf of your domain.
- Only images, video, audio, PDF and plain text open in the browser; everything else is served as a download.
- The file name is cleaned: path separators and control characters are replaced with
-. The name goes into theContent-Dispositionheader escaped, so no extra header can be injected through it. - Content reaches the disk under its hash: the name from the client takes no part in paths.
What is left to the deployment
- TLS. S5W itself speaks HTTP. Encryption is done by the proxy in front of it — nginx-proxy-manager, traefik, nginx.
- Network access. Do not publish ports you do not need. The panel domain is worth closing further with an access list or basic auth.
- Secrets. Keep
ADMIN_PASSWORDand, if you set it yourself,WINTER_KEYas secrets. - Volume backups — they contain every file and the signing key; protect them as you protect the data itself.