PAM - Security

<- Back to PAM


PAM Security Model

This document describes the security model for PAM, the known risks, and the mitigations in place.


Threat model

PAM is a client-side web application. All encryption and decryption happens in the browser using the Web Crypto API (SubtleCrypto). No data is ever sent to a server. The encrypted file is stored locally by the user.

The primary threat vectors are:

  1. Compromise of the encrypted file — an attacker obtains the .pam file and attempts to brute-force the master password offline.
  2. XSS via malicious file content — a user is tricked into loading a PAM file from an untrusted source that contains malicious HTML in an html-type field.
  3. Master password exposure — the master password is cached in browser storage and exposed via XSS, a compromised extension, or physical access to the device.

SEC-001 — HTML field rendering

PAM supports an html field type that allows rich content (formatted notes, links, instructions). This is an intentional feature designed for controlled internal deployments where a security team authors PAM data and distributes it on a read-only volume to a defined cohort.

Risk: If a user loads a PAM file from an untrusted source, html fields could contain malicious scripts (XSS).

Mitigation: - HTML field rendering is disabled by default. Fields of type html render as escaped plain text with a </> badge. - HTML rendering can only be enabled in Preferences → Security → Allow HTML Field Rendering, which is accessible only after unlocking preferences with the prefs password. - When HTML rendering is enabled, a persistent ⚠ HTML ON warning badge is shown in the toolbar so users always know they are in a mode where HTML fields render live.

When is it safe to enable? - The PAM file was authored by a trusted party (e.g. your own security team). - The file is distributed on a read-only volume and cannot be modified by end users. - You are not loading PAM files from untrusted URLs.

Residual risk: A PAM file distributed with HTML rendering already enabled in its prefs block carries that setting with it. The mitigation protects against foreign malicious files, not against the trusted file itself being compromised.


SEC-002 — Master password caching

The master file password can be cached in browser storage to avoid re-entry on every save/load. The cache strategy is controlled in Preferences → Administration → filePass Cache Strategy.

Options: - session (default) — password is cached in sessionStorage for the current browser tab only and cleared when the tab is closed. - local — password persists in localStorage across sessions and power cycles. Use with caution on shared devices. A ⚠ PASS: LOCAL warning badge appears in the toolbar while this is active. - global — password is held in a JavaScript variable only (lost on page reload). - none — password is never cached; must be re-entered on every load/save.

Per-device strategy persistence:

The chosen strategy is stored per-device in localStorage under the key pamCacheStrategy. On startup, PAM reads this key and applies it before loading any file, solving the chicken-and-egg problem where the PAM file (which contains preferences) cannot be opened without first knowing the cache strategy.

This means the user’s strategy choice persists across page reloads, browser restarts, and power cycles, independently of the PAM file. It can be changed at any time in Preferences and takes effect immediately. When the strategy is changed, the password is cleared from the previous storage location before the new strategy is applied.

History: - v2.0.3 — default changed from local to session as a security improvement (SEC-002). - v2.0.5 — default reverted to local due to PWA reload behaviour causing excessive re-entry friction; ⚠ PASS: LOCAL badge added as compensating control. - v2.0.7 — default changed back to session; per-device pamCacheStrategy persistence introduced so the user’s choice survives reloads without being tied to the PAM file default. - v1.3.1 — BUG-002 fixed: password was stored in the wrong storage bucket when the loaded file’s filePassCache pref differed from the startup default; manifested as lost password on iOS Safari PWA relaunch.

PWA note: When PAM is installed as a Progressive Web App on iOS Safari, each launch may start a fresh session, wiping sessionStorage. If your PAM file specifies filePassCache: local, PAM now re-stores the password in localStorage after loading the file and persists that strategy as pamCacheStrategy so subsequent launches find it there. (BUG-002)

Recommendation: Use session (the default) for most environments. Use local only on a trusted personal device where re-entry friction is a genuine concern.


SEC-003 / SEC-004 — Encryption format (v1 vs v2)

PAM v1.x uses AES-256-CBC with PBKDF2-SHA-256 key derivation. The v1 implementation has two known weaknesses:

  1. Low iteration count (100,000) — NIST SP 800-132 (2023) and OWASP now recommend ≥ 600,000 iterations.
  2. Salt entropy bug — the random salt bytes are passed through TextEncoder.encode() which calls .toString() first, producing an ASCII string like "0,34,211,..." instead of raw bytes, dramatically reducing salt entropy.

Both bugs define the key derivation for all existing v1 files. Fixing them requires a new file format (v2).

Migration: v2 format support is planned for PAM v1.3. The migration is non-breaking — v1 files can always be decrypted; users choose when to re-save in v2 format. See MIGRATION.md (published with v2.0).


SEC-006 — Preferences lock password

The preferences lock password (lockPreferencesPassword) is stored as plaintext in the PAM file’s prefs block.

This is intentional. The prefs lock is a convenience feature whose sole purpose is to prevent casual or accidental modification of preferences in a shared deployment. It is not a cryptographic boundary.

The entire PAM file — including the prefs block and lockPreferencesPassword — is encrypted with the master password (AES-256-CBC). An attacker who has the master password can read everything. Hashing lockPreferencesPassword would add no security benefit in this threat model, and would silently break existing files where the value was stored as plaintext.

Threat model: The prefs lock protects against an authorised user (who has the master password) accidentally changing deployment-managed settings. It does not protect against a determined attacker.


SEC-007 — URL load validation

The Load → URL feature only accepts https:// and http:// URLs. javascript:, data:, ftp:, and other URI schemes are rejected to prevent protocol-based injection attacks.


SEC-008 — Content Security Policy

PAM sets a Content-Security-Policy meta tag in index.html:

default-src 'self';
script-src 'self' https://cdn.jsdelivr.net;
style-src 'self';
img-src 'self' data:;
font-src 'self'

This prevents loading of scripts, styles, and resources from untrusted origins. The cdn.jsdelivr.net exception covers the DOMPurify library used for HTML sanitization.


Reporting security issues

This is an open-source personal password manager. If you find a security issue please open a GitHub issue at jlinoff/pam.