Skip to content

Security: marixdev/marix

SECURITY.md

Security Architecture

Marix SSH Client - Security Documentation
Version: 1.0.10 | Last Updated: January 2026

This document describes the security architecture, cryptographic implementations, and data protection mechanisms used in Marix SSH Client.


Table of Contents

  1. Overview
  2. Credential Storage
  3. Backup Encryption
  4. SSH/SFTP Security
  5. Known Hosts Verification
  6. SSH Key Management
  7. OAuth & Cloud Integrations
  8. LAN Sharing Security
  9. Electron Security Model
  10. Cryptographic Summary
  11. Security Recommendations

Overview & Threat Model

Marix is designed to provide high usability while minimizing the attack surface.

Marix PROTECTS against:

  • Accidental credential disclosure (via shoulder surfing or plain text files).
  • Local credential theft from casual malware (via OS Keychain binding).
  • Backup leakage (encrypted at rest before upload).
  • Offline brute-force attacks against stolen backups.
  • MITM attacks via SSH host key verification.

Marix does NOT claim protection against:

  • A malicious or fully compromised SSH server.
  • Kernel-level malware (Rootkits/Keyloggers) on the local machine.
  • Physical access to an unlocked device while the app is running.
  • Supply-chain attacks at the OS level.

Disclaimer: This is not a formal security audit. If your threat model includes nation-state adversaries, you should use OpenSSH CLI directly in a hardened environment.


Credential Storage

SecureStorage Service

All sensitive data (passwords, private keys, passphrases) are encrypted using Electron's safeStorage API, which leverages the operating system's native keychain:

Platform Backend
macOS Keychain
Windows DPAPI (Data Protection API)
Linux libsecret (GNOME Keyring / KWallet)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    SecureStorage Flow                       β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                            β”‚
β”‚   Plain Password ─────────────────────────────────────►    β”‚
β”‚         β”‚                                                  β”‚
β”‚         β–Ό                                                  β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                      β”‚
β”‚   β”‚ safeStorage API β”‚                                      β”‚
β”‚   β”‚   (OS Keychain) β”‚                                      β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                      β”‚
β”‚            β”‚                                               β”‚
β”‚            β–Ό                                               β”‚
β”‚   enc:XXXXXXXXXXXXXX (Base64 encrypted string)             β”‚
β”‚         β”‚                                                  β”‚
β”‚         β–Ό                                                  β”‚
β”‚   Stored in electron-store (config.json)                   β”‚
β”‚                                                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Security Properties:

  • βœ… Data encrypted on one machine cannot be decrypted on another
  • βœ… Credentials are tied to the device and user account
  • βœ… Automatic migration from plaintext on first run
  • βœ… Protected fields: password, privateKey, passphrase

ServerStore Implementation

// Fields automatically encrypted/decrypted
const SENSITIVE_FIELDS = ['password', 'privateKey', 'passphrase'];

// Storage format example
{
  "id": "server-001",
  "host": "example.com",
  "username": "admin",
  "password": "enc:AQAAANCMnd8BFdERjHoAwE...",  // ← Encrypted
  "privateKey": "enc:AQAAANCMnd8BFdERjHoAwE..." // ← Encrypted
}

Backup Encryption

Cryptographic Specifications

Component Algorithm Parameters
KDF Argon2id Auto-tuned (~1s target)
Encryption AES-256-GCM Authenticated encryption
Salt CSPRNG 32 bytes per backup
IV/Nonce CSPRNG 16 bytes per operation
Auth Tag GCM 16 bytes

Argon2id Auto-Tuning

Marix dynamically calibrates Argon2id parameters based on the user's hardware to achieve approximately 1 second of key derivation time. This provides consistent security regardless of machine performance.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Argon2id Auto-Tuning Algorithm                β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                            β”‚
β”‚   1. Detect System RAM                                     β”‚
β”‚      β”œβ”€β”€ β‰₯16 GB β†’ Start at 512 MB memory                   β”‚
β”‚      β”œβ”€β”€ β‰₯8 GB  β†’ Start at 256 MB memory                   β”‚
β”‚      β”œβ”€β”€ β‰₯4 GB  β†’ Start at 128 MB memory                   β”‚
β”‚      └── <4 GB  β†’ Start at 64 MB memory (minimum)          β”‚
β”‚                                                            β”‚
β”‚   2. Benchmark with timeCost=1                             β”‚
β”‚                                                            β”‚
β”‚   3. Adjust memory if baseline is off                      β”‚
β”‚      β”œβ”€β”€ Too slow (>600ms) β†’ Reduce memory                 β”‚
β”‚      └── Too fast (<100ms) β†’ Increase memory               β”‚
β”‚                                                            β”‚
β”‚   4. Scale timeCost to hit ~1000ms target                  β”‚
β”‚                                                            β”‚
β”‚   5. Fine-tune Β±200ms tolerance                            β”‚
β”‚                                                            β”‚
β”‚   6. Store parameters with backup for decryption           β”‚
β”‚                                                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Security Floors (Minimum Values):

  • Memory Cost: 64 MB minimum
  • Time Cost: 2 iterations minimum
  • Parallelism: min(4, CPU cores)

Password Requirements

Backup passwords must meet strict requirements:

Requirement Minimum
Length 10 characters
Uppercase At least 1
Lowercase At least 1
Number At least 1
Special Character At least 1 (!@#$%^&*()_+-=[]{}...)
// Password validation regex
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~`]).{10,}$/

Encrypted Backup Format

{
  "version": "2.2",
  "encrypted": "Base64(AES-256-GCM ciphertext)",
  "salt": "Base64(32 random bytes)",
  "iv": "Base64(16 random bytes)",
  "authTag": "Base64(16 bytes GCM tag)",
  "kdf": "argon2id",
  "memoryCost": 262144,
  "parallelism": 4,
  "timeCost": 3
}

Cross-Machine Compatibility:

  • KDF parameters are stored with the backup
  • Decryption uses stored parameters, not current machine's calibration
  • Legacy backups (v2.0) use fixed parameters for compatibility

Backup Data Contents

Data Type Encrypted in Backup
Server credentials βœ… Yes
SSH Keys (public + private) βœ… Yes
2FA TOTP secrets βœ… Yes
Cloudflare API tokens βœ… Yes
Port forward configs βœ… Yes
Command snippets βœ… Yes
Tag colors βœ… Yes
Theme settings βœ… Yes

SSH/SFTP Security

SSH2 Library Configuration

const connectConfig: ConnectConfig = {
  host: config.host,
  port: config.port,
  username: config.username,
  password: config.password,
  privateKey: config.privateKey,
  passphrase: config.passphrase,
  readyTimeout: 30000,
  keepaliveInterval: 10000,
  keepaliveCountMax: 3,
};

Supported Authentication Methods

Method Security Level Recommendation
SSH Key (Ed25519) 🟒 Highest Recommended
SSH Key (ECDSA) 🟒 High Good
SSH Key (RSA 4096-bit) 🟒 High Good
Password 🟑 Medium Use with strong passwords

Native SSH (PTY) Security

For terminal sessions, Marix uses the system's native SSH client via node-pty:

const sshArgs = [
  '-o', 'StrictHostKeyChecking=no',
  '-o', 'UserKnownHostsFile=/dev/null',
  '-o', 'LogLevel=ERROR',
  '-p', port.toString(),
  '-i', privateKeyPath,  // If using key auth
  `${username}@${host}`
];

Temporary Key Handling:

  • Private keys are written to temp files with mode: 0o600
  • Keys are normalized (LF line endings, trailing newline)
  • Temp files are cleaned up after session ends

Known Hosts Verification

Host Key Fingerprinting

Marix implements SSH host key verification to prevent MITM attacks:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Host Key Verification Flow                     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                            β”‚
β”‚   1. Connect to server                                     β”‚
β”‚                                                            β”‚
β”‚   2. Fetch host key via ssh-keyscan                        β”‚
β”‚      └── Preference: ed25519 > ecdsa > rsa                 β”‚
β”‚                                                            β”‚
β”‚   3. Calculate SHA256 fingerprint                          β”‚
β”‚                                                            β”‚
β”‚   4. Compare with stored fingerprint                       β”‚
β”‚      β”œβ”€β”€ NEW: Prompt user to trust                         β”‚
β”‚      β”œβ”€β”€ MATCH: Allow connection                           β”‚
β”‚      └── CHANGED: ⚠️ Security warning!                     β”‚
β”‚                                                            β”‚
β”‚   5. Store in ~/.marix/known_hosts.json                    β”‚
β”‚                                                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Fingerprint Storage Format

{
  "example.com:22": {
    "host": "example.com",
    "port": 22,
    "keyType": "ssh-ed25519",
    "fingerprint": "SHA256:AAAA...",
    "fullKey": "ssh-ed25519 AAAA...",
    "addedAt": "2026-01-21T10:00:00Z"
  }
}

SSH Key Management

Key Generation

Marix supports generating SSH keys via ssh-keygen:

Type Bits Use Case
Ed25519 256 Modern, recommended
ECDSA 521 Strong, wide support
RSA 4096 Legacy compatibility

Key Storage

  • Location: ~/.marix/ssh_keys/
  • Private keys: mode: 0o600 (owner read/write only)
  • Public keys: stored alongside private
  • Metadata: ssh_keys_meta.json

Backup Strategy

Both public and private keys are backed up:

exportAllKeysForBackup(): {
  id: string;
  name: string;
  type: string;
  publicKey: string;    // Full public key
  privateKey: string;   // Full private key (encrypted in backup)
  fingerprint: string;
  createdAt: string;
}[]

Import Deduplication:

  • Keys are matched by fingerprint
  • Duplicate keys are skipped during restore
  • Preserves original key IDs and metadata

OAuth & Cloud Integrations

Supported Providers

Provider Auth Method Scopes
Google Drive OAuth 2.0 (PKCE) drive.file
GitHub Device Flow repo
GitLab OAuth 2.0 api
Box OAuth 2.0 root_readwrite

Token Storage

OAuth tokens are stored securely:

// GitHub tokens
class SecureStore {
  async setPassword(service, account, password) {
    const encrypted = safeStorage.encryptString(password);
    fs.writeFileSync(filePath, encrypted);
  }
}

// Google Drive tokens
// Stored in user data directory, encrypted
TOKEN_PATH = path.join(app.getPath('userData'), 'google-drive-token.json');

OAuth Callback Security

Local callback server for OAuth:

  • Runs on localhost:3000 only
  • Stops immediately after receiving callback
  • Uses PKCE flow where supported

LAN Sharing Security

Server Discovery

  • Protocol: UDP Multicast
  • Address: 224.0.0.88:45678
  • Peer timeout: 30 seconds

Device Identification

// Stable device ID from hostname + MAC address
const deviceId = crypto.createHash('sha256')
  .update(`${hostname}-${macAddress}`)
  .digest('hex')
  .substring(0, 32);

Pairing Code

  • 6-digit random code for each transfer
  • Must match to initiate file transfer
  • Prevents unauthorized connections

File Transfer

  • Protocol: TCP (port 45679)
  • Chunk size: 64 KB
  • No encryption (relies on LAN trust)

⚠️ Warning: LAN transfers are not encrypted. Only use on trusted networks.


Electron Security Model

Current Configuration

webPreferences: {
  nodeIntegration: true,
  contextIsolation: false,
  backgroundThrottling: false,
  spellcheck: false,
}

Content Security Policy

<meta http-equiv="Content-Security-Policy" 
      content="script-src 'self' 'unsafe-inline';">

IPC Communication

Preload script exposes minimal IPC surface:

contextBridge.exposeInMainWorld('electron', {
  ipcRenderer: {
    invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
    on: (channel, func) => ipcRenderer.on(channel, ...args),
  },
});

Memory Management

  • V8 memory limit: 256 MB
  • Periodic cache clearing (5 minutes)
  • Manual GC triggers

Cryptographic Summary

Operation Algorithm Key Size Notes
Credential encryption OS Keychain Platform-dependent safeStorage API
Backup KDF Argon2id 256-bit output Auto-tuned
Backup encryption AES-256-GCM 256-bit Authenticated
SSH key generation Ed25519/ECDSA/RSA 256/521/4096-bit Via ssh-keygen
Host fingerprint SHA256 256-bit Via ssh-keyscan
Device ID SHA256 256-bit From hostname+MAC
Random generation CSPRNG N/A crypto.randomBytes

Security Recommendations

For Users

  1. Use SSH Keys instead of passwords whenever possible
  2. Use strong backup passwords (10+ chars, mixed case, numbers, symbols)
  3. Verify host fingerprints on first connection
  4. Use Port Knocking for additional stealth
  5. Backup regularly to multiple cloud providers

For Network Administrators

  1. Disable password authentication on SSH servers
  2. Use Ed25519 keys for best security/performance
  3. Configure fail2ban for brute-force protection
  4. Keep OpenSSH updated on all servers
  5. Use VPN for sensitive LAN transfers

Port Knocking Benefits

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Port Knocking Flow                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                            β”‚
β”‚   1. SSH port (22) is CLOSED by default                    β”‚
β”‚                                                            β”‚
β”‚   2. Client sends TCP SYN to sequence: 7000 β†’ 8000 β†’ 9000  β”‚
β”‚                                                            β”‚
β”‚   3. Server daemon (knockd) detects sequence               β”‚
β”‚                                                            β”‚
β”‚   4. Firewall opens port 22 for client IP                  β”‚
β”‚                                                            β”‚
β”‚   5. Client connects via SSH                               β”‚
β”‚                                                            β”‚
β”‚   Benefits:                                                β”‚
β”‚   β”œβ”€β”€ Port scanners see port 22 as closed                  β”‚
β”‚   β”œβ”€β”€ Prevents brute-force attacks                         β”‚
β”‚   └── Adds stealth layer before authentication             β”‚
β”‚                                                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Reporting Security Issues

If you discover a security vulnerability in Marix, please report it responsibly:

  1. Do not open a public GitHub issue
  2. Contact the maintainer directly
  3. Provide detailed reproduction steps
  4. Allow reasonable time for a fix before disclosure

Changelog

Version Date Changes
1.0.0 2026 Initial security implementation
1.0.5 2026 Added Argon2id auto-tuning
1.0.10 2026 Added Snippets to backup

This document is maintained as part of the Marix SSH Client project.

There aren't any published security advisories