Security Model
About
The Signer is the security core of Extra Wallet ecosystem.
It is a separate web app, loaded in an <iframe> from a different origin than the main app.
All secrets (mnemonics, private keys, etc.) live only inside the signer.
This page explains:
- how the iframe security model works,
- how communication is handled,
- how data is stored and encrypted,
- why we keep a derived key in memory,
- and what the limitations are.
1. Iframe Isolation and Origins
Separate Origins
- Main app origin: e.g.
https://app-staging.extrawallet.app/ - Signer origin: e.g.
https://signer-staging.extrawallet.app/
The signer is embedded into the main app as an <iframe> that points to the signer origin.
Because browsers enforce same-origin policy, the main app JS cannot:
- read the signer’s IndexedDB,
- inspect the signer’s global variables or closures,
- or directly access its DOM.
This gives us a hard isolation boundary between:
- the host (wallet UI, mini apps), and
- the signer (secrets, cryptography, confirmations).
Even if the main app is compromised (e.g. a malicious code or a bug), the attacker still cannot access the signer’s local database or in-memory keys.
2. Communication Via MessageChannel
The main app and signer communicate strictly via MessageChannel:
- The main app creates a
MessageChannel. - It sends one of the
MessagePorts into the signer iframe usingpostMessage. - After that, both sides talk on the dedicated
MessagePort.
Key Properties
-
Structured messages only. We send small JSON-serialisable payloads:
{ requestId: "123", service: "safeSigner", method: "signTransaction", args: [...] }No direct object or function references. -
Explicit request–response. Each request has a
requestId,service,method, andargs. The signer replies with either:{ requestId, result }{ requestId, error }.
-
Public data only towards the main app. The signer never sends:
- private keys,
- raw mnemonics,
- decrypted seeds,
- or the password-derived key.
The main app can only request operations like “derive addresses”, “sign this payload”, etc. Everything sensitive stays on the signer side.
3. Local Encrypted Storage (IndexedDB)
All secrets are stored only in the signer origin’s IndexedDB.
What We Store
Examples of sensitive data stored:
- encrypted mnemonics
- encrypted private keys
- derivation configs / metadata
Non-sensitive metadata (e.g. names, token information, derivation paths, public keys) can also be stored, but secret material is always encrypted.
No Server-Side Copy
The signer:
- does not upload your keys or mnemonics to any backend,
- does not rely on remote “key storage” services.
Everything is local to the browser, on the signer origin.
4. Password-Based Encryption
Password as the Root Secret
The user chooses a password. This password never leaves the signer iframe.
From this password we derive a symmetric crypto key used to encrypt/decrypt the rest of the data.
Algorithms
Users can choose between two modes:
-
Secure (recommended)
- Key derivation:
argon2id(from@noble/hashes/argon2) - Encryption:
AES-GCM - Properties:
- memory-hard
- slow to brute-force
- modern and recommended
- Key derivation:
-
Weak / legacy
- Key derivation:
PBKDF2 - Encryption:
AES-GCM - Properties:
- widely supported but easier to brute-force
- included for compatibility / performance-sensitive environments
- Key derivation:
Regardless of the mode, the signer:
- never stores the plaintext password,
- never exposes the derived key to the main app origin.
Password Prompts
For any operation that needs secrets (e.g. decrypt mnemonic, sign transaction, export keys), the signer:
- Shows its own UI inside the iframe asking for the password.
- Verifies / derives the encryption key.
- Decrypts only the required data to perform the requested operation.
- Returns only non-sensitive results to the main app (e.g. a signature).
5. Why We Keep a Derived Key in Memory
Deriving a strong key from the password (especially with argon2id) can easily take seconds.
If we derived the key from scratch on every single action, some operations could take 1–5 minutes in worst-case settings or low-power devices.
That would lead to:
- terrible UX,
- users choosing weaker passwords,
- users disabling security features “because it’s too slow”.
Our Approach
We use the following pattern:
-
When the user enters their password:
- we derive a session key via the chosen KDF (
argon2idorPBKDF2), - we keep only that key in memory inside the signer iframe.
- we derive a session key via the chosen KDF (
-
While the signer iframe remains loaded:
- the key can be reused to decrypt/encrypt data for subsequent operations (still with explicit user interaction/confirmation in the signer UI where appropriate).
-
When the iframe is removed:
- the JS context is destroyed,
- all in-memory data (including the key) is dropped by the browser.
Why This is Still Safe
- The key lives only in the signer’s origin and process, not in the main app’s.
- The main app cannot read the signer’s memory or grab references to the key.
- Once the iframe is closed or reloaded, the key disappears from memory and must be re-derived with the password.
This is a deliberate trade-off:
- We keep a strong KDF and strong encryption.
- We avoid deriving it on every single click.
- We restrict the key’s lifetime to the signer iframe session.
If we didn’t keep the key in memory, users would experience long delays for each action, making the system practically unusable and pushing them towards much worse security trade-offs (like weak passwords or unencrypted local storage).
6. Sensitive UI is Rendered Inside the Signer
Any UI that deals with sensitive data is rendered inside the signer iframe, not in the main app.
Examples:
- entering the password,
- showing a mnemonic phrase or private key,
- confirming a signing request that includes sensitive details.
The main app can only embed the signer iframe but it doesn’t render or control the UI that shows secrets. This means a compromised main app UI cannot silently read your mnemonic.
7. Threat Model & Limitations
This model is designed to protect you against:
- bugs or malicious code in the main app,
- accidental exposure of secrets through the host origin,
- simple XSS in the main app.
It does not protect against:
- malware on your device (key loggers, screen grabbers, remote-control trojans),
- a fully compromised browser process running the signer origin.
For best security, users should still:
- keep their OS and browser updated,
- avoid installing untrusted extensions,
- and use strong, unique passwords.
Summary
The signer:
- lives on a separate origin inside an iframe,
- communicates via MessageChannel only,
- stores secrets locally in IndexedDB, fully encrypted,
- derives keys from the user’s password using a strong KDF (argon2id recommended),
- keeps the derived key only in memory for the iframe’s lifetime,
- renders all sensitive UI itself.
Together, this makes Signer's consumers closer to a mini hardware-style model running in the browser, with clear boundaries between:
- general-purpose UI, and
- the minimal, isolated environment that handles your private keys.