All passwords are migrated to bcrypt, so {SSHA512} hashes are no longer
checked; an unknown scheme raises like before. The README documents that
Dovecot must support BLF-CRYPT.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Login:
- One message for unknown email, wrong password and deactivated
account. Before, "This account is not enabled!" only appeared after
the right password and confirmed valid credentials.
- Email and password must be non-empty strings. A missing password
field became the string "None" and logged in a user with that
password.
Forms:
- "Create user" no longer renders the password back into the HTML after
a validation error.
- Password fields for a new password use autocomplete="new-password",
so a password manager does not fill in the admin's own password. The
email fields on the create pages use autocomplete="off".
- Email addresses are limited to 64 characters in the local part and
254 in total (RFC 5321). Longer ones caused a database error and
HTTP 500.
- alerts.html no longer translates flash messages a second time. Running
str.format() again on an already formatted message was fragile and
logged "Unknown translation" for every German message. All flashes
now translate when flashing, "Invalid data" included.
Debug mode:
- The Docker image sets MAX_PRODUCTION=1, and the app refuses to start
in debug mode then. FLASK_DEBUG=1 in production would otherwise enable
the fixed secret key, cookies without Secure, no HSTS and query
arguments (including password hashes) in the log.
- make run-dev and run-dev-as-prod bind to 127.0.0.1.
Other:
- A denied access is logged with the logger at debug level instead of
print. PermissionDenied loses its unused message, it is deliberately
answered with a 404.
- Fix the typo in /user/<id>/ser-toggle-can-use-different-alias-domain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the direct dependencies were pinned. Werkzeug, Jinja2, MarkupSafe,
itsdangerous and the other transitive packages were resolved at build
time, so builds of the same commit could differ, and pip did not verify
the downloaded files. There was also no vulnerability scan.
- requirements.in / requirements-dev.in list the direct dependencies.
requirements.txt / requirements-dev.txt are lockfiles generated by
pip-compile with exact versions and hashes for every package. The dev
lockfile is constrained by the production one.
- Makefile targets: lock-deps, update-deps, sync-deps and audit
(pip-audit against requirements.txt).
- The locked production versions match the previously installed ones.
The first audit found no known vulnerabilities.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Anonymous users must not get cookies or session store entries, so a
CSRF token for /login is not possible. Instead, every unsafe request is
checked statelessly via Sec-Fetch-Site, falling back to Origin. Requests
without both headers are not from a browser and are accepted. The
expected origin comes from the new MAX_ORIGIN setting, which is required
outside of debug mode.
Further CSRF changes:
- Do not delete the CSRF token on a failed check. It added no security,
but invalidated forms in open tabs and could be triggered cross-site.
- Treat HEAD and OPTIONS as safe methods.
Keep the login page free of sessions:
- Show login messages via ?msg=login_required|logout (fixed keys only)
and the login error via the template context instead of flash().
- Only send the session deletion cookie if the client sent a cookie.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>