Skip to content

3.2.1.6

Latest

Choose a tag to compare

@nilsteampassnet nilsteampassnet released this 11 Aug 14:24
719b481

What's Changed

This is a security release on the 3.2.1 line. It closes a broad sweep of stored cross-site scripting sinks across the administration and item screens, folder titles, role names, directory-supplied identities, task error messages, item icons and WebSocket notifications, and stops the client IP that feeds the audit log and the anti-bruteforce counter from being forged through proxy headers. It also fixes two earlier security fixes that had silently never taken effect, because they were applied to the wrong copy of a duplicated JavaScript file. Upgrading is recommended for all installations, and strongly recommended for anyone provisioning accounts from LDAP or OAuth2, or importing folder trees from KeePass.

This release changes no table structure, but it carries three data migrations. UPGRADE_MIN_DATE is raised, so every installation goes through the upgrade wizard once to normalize values that earlier releases stored raw. The migrations are idempotent and only write the rows that actually change, a clean database performs no write at all.

🔒 Security fixes

  • Stored markup in folder titles imported from KeePass is no longer executable - createFolder() in the import decoded HTML entities before writing nested_tree.title, which undid the sanitization the import had just applied. A group named with a tag was stored executable and rendered in the folder tree, the roles matrix and every folder selector. The decode is gone, the value is normalized once and reused for the duplicate check, and an upgrade pass rewrites trees imported by an earlier release.

  • Identity fields coming from LDAP and OAuth2 are neutralized before being stored - externalAdCreateUser() wrote email, name and lastname exactly as the directory returned them, unlike every other user creation path. A display name carrying markup was persisted verbatim and rendered in the admin screens. The three fields are now filtered on creation, and an upgrade pass normalizes accounts provisioned by an earlier release. The login is deliberately left alone: it is the value later logins are matched on, and re-filtering it would lock accounts out.

  • Directory values are encoded in the LDAP and OAuth2 user sync screens - logins, display names, mails and group names arrive straight from the directory. purifyData() removes tags but keeps quotes, which is enough to break out of the data-user-* and title attributes those screens build. Each value is now encoded, including when the attributes are read back to populate the add-user modal.

  • Folder titles, folder paths and role names are encoded in the roles matrix - they were interpolated straight into the matrix markup. The same sweep covers the role badges in the user role selectors, the field role badges, the item role badges and the remaining page renderers that concatenated identity or title values into markup.

  • Item labels and recipient emails are encoded in attribute contexts - simplePurifier() strips tags but decodes " back into a real quote, so a label carrying a double quote escaped the data-label attribute of the search result panel. The same applied to the recipient email in the announce selector, a field directory-created users stored unfiltered.

  • Task error messages no longer break out of their tooltip - the error column of the finished tasks table was injected into a title attribute after simplePurifier(), which returns plain text and turns " back into a quote, closing the attribute. The value is encoded instead, and the server-side cell is routed through tpDatatableJsonCell() like the others.

  • WebSocket notification payloads are escaped - folder titles, task fields, maintenance and session messages were concatenated into toastr messages, which render as HTML. They are escaped like the item handlers already were, and the task percentage and id are coerced to integers before reaching a style and an id attribute.

  • Item icons stored by the API before 3.2.1 can no longer inject markup - the API stored the icon parameter verbatim, and the 3.2.1 fix on the write path was not retroactive. The item detail card was the last sink concatenating fa_icon raw into a class attribute; it now encodes it, and an upgrade pass strips unsafe characters from icons already in the database.

  • Item descriptions are hardened against multi-encoded markup - the search preview builder decoded entities in a single pass, which left entity-escaped tags that strip_tags() could not see; it now decodes until the value stabilizes. Descriptions reaching the two summernote editors are purified with DOMPurify, as the item detail card already did.

  • Two earlier security fixes that had never taken effect are now actually served - public/assets/js is the copy index.php loads, but two fixes had only been applied to app/includes/js. As a result password and otp_secret kept being purified despite the fixes, the purify=false flag was ignored, and rehydratePresenceIndicators() had never been served at all. The served files are resynchronized, and a sentinel test now covers all 21 duplicated pairs — five JavaScript files and fifteen teampassclasses packages — so the next drift fails the build instead of shipping silently.

  • The client IP used for security decisions can be resolved through trusted proxies - getClientIpServer() feeds the audit log, the authentication lock and the per-IP bruteforce counter, but read proxy headers unconditionally: any client could forge the logged address and rotate the header to escape nb_bad_authentication_by_ip entirely. It now delegates to the same resolver the API already uses, which trusts a proxy header only when REMOTE_ADDR matches a configured trusted proxy. This strict path is opt-in, see the upgrade notes below.

🛠️ Improvements

  • Accented names are stored with their accents - add_new_user, store_user_changes, add_user_from_ad and the directory creation path used FILTER_SANITIZE_FULL_SPECIAL_CHARS, which encodes every character above U+007F, so "Jérôme" was stored as Jérôme and read back literally outside an HTML context, mail bodies, CSV exports. All four write paths now use htmlspecialchars, which neutralizes the five characters that matter and leaves accents alone. The upgrade pass realigns names already in the database.

  • Client-side HTML encoding is enforced by a sentinel test - purifyData() returns plain, unescaped text while every consumer inserts it into markup. A new sentinel test pins the encoding at each sink, with an allow-list carrying the known non-sinks and the server-composed status messages, so a new raw insertion fails the build.

⬆️ Upgrade notes

  • Schema. No table structure changes. Three data migrations run once through the upgrade wizard: legacy Font Awesome icon values are stripped of unsafe characters, the email/name/lastname of every account are normalized, and folder titles are normalized. All three are idempotent and skip rows that are already correct.

  • Stored names change form on upgrade. The account normalization pass decodes and re-encodes name and lastname for every account, not only the directory ones, because the four creation paths previously disagreed on the stored form. An account displayed as Jérôme will read Jérôme after the upgrade. Logins are never touched.

  • Proxy-aware IP detection stays opt-in. The web login path now honours the IP detection mode setting (Settings → Network), which the API already used. The default remains Direct access, i.e. the historical behaviour, switching it on is deliberately an administrator decision, not an upgrade side effect. If TeamPass sits behind a reverse proxy or a WAF, set the mode to Reverse proxy / WAF and declare the proxy addresses in Trusted proxies; otherwise every request is attributed to the proxy address and a handful of failed logins locks the whole organisation out on one shared counter. If nothing sits in front of TeamPass, leave it on Direct access.

  • Hard-refresh the browser after upgrading. Several fixes land in the served JavaScript assets; a cached copy keeps the old behaviour.

  • Back up your database before upgrading, as always.

Full Changelog: 3.2.1.5...3.2.1.6

Important

  • Requires at least PHP 8.2

Languages

Please join Teampass v3 translation project on Poeditor and translate it for your language.

Installation

Follow instructions from Documentation.

Upgrade

Follow instructions from Documentation.

Ideas and comments

Are welcome ... please use Discussions.

Download TeamPass