Skip to main content

Memory Hardening

While protecting against user-space memory attacks in the general case is not within the threat model for Bitwarden applications, the lock state must be protected, and it should not be possible to unlock a locked vault. Because of this, passwords or keys cannot be left behind in memory in an accessible state. Besides this requirement, some features such as SSH agent need process hardening where possible, as required by the protocol specification.

Zeroizing and process reload

To clear secrets on locking, Bitwarden clients use two techniques, zeroizing and process reload. For any memory that lives in Rust, memory is overwritten with zeroes, as soon as it becomes unused or gets dropped 1. This hardens the SDK, and the Rust desktop module (desktop native) against memory being left behind. Process reload wipes the entire process - on the web app by reloading the page, on browser extensions by reloading the extension, and on desktop by force-crashing the renderer process 2. The assumption here is that since the process dies, the memory gets wiped too. JavaScript does not provide mechanisms for reliably zeroizing memory. Secrets or partial secrets frequently remain in memory even after garbage collection cycles complete.

Process isolation and key protection on desktop apps

Next to process reload and zeroizing, desktop apps can use OS-level protections to harden memory. There are two mechanisms used here: Process isolation and key protection. Process isolation uses OS-level features to isolate the process from debugger access. Windows and desktop Linux by default allow user-space processes to debug other user-space processes and read memory. MacOS does not allow this by default and requires user consent to allow a process to debug another process. On Linux, some distributions such as Ubuntu use yama.ptrace_scope to limit ptrace access.

To harden against user-space memory attacks, Bitwarden desktop isolates the main process. On Windows, DACL is used to restrict access to the process, on Linux PR_SET_DUMPABLE is used to disable ptrace access and on MacOS the process is hardened using the Hardened Runtime entitlements, and also by using PT_DENY_ATTACH to prevent debugger attachment. On Linux, a dynamic library that sets PR_SET_DUMPABLE is also injected into the renderer processes by injecting a shared object into the renderer processes 3, so that these are isolated too. These mechanisms apply to all apps except for the Snap desktop app. Snap does not support PR_SET_DUMPABLE currently and breaks file picker support, due to a bug in the desktop portal.

Next to hardening the entire process, operating systems offer mechanisms to protect cryptographic keys in memory. On Windows, DPAPI can be used to encrypt a key in memory, with a key bound to the process. On Linux, memfd_secret and keyctl are available, each of which can be used to store keys in memory while preventing other processes from reading them. This is used to hold the biometric unlock key in memory while the desktop app is locked. Access to this protected memory is available via the EncryptedMemoryStore abstraction that automatically uses the correct memory protection.