security: wipe RPC creds, lock down generated conf, auto-clear secret clipboard (audit #4-6)

- rpc_client: wipe the plaintext "user:password" temporary with sodium_memzero after
  base64-encoding it into the auth header (std::string doesn't zero its buffer on
  destruction).
- connection: the auto-generated DRAGONX.conf holds rpcuser/rpcpassword in plaintext but
  was written with the default umask (often world-readable 0644). Restrict it to owner
  read/write after creation so another local user can't read the credentials.
- app: copying a seed phrase / private key to the clipboard now arms an auto-clear —
  App::copySecretToClipboard() copies the secret and, after 45s, wipes the clipboard IF it
  still holds that secret (compared via a stored hash, never the plaintext). Wired into the
  lite first-run wizard's seed Copy and the Settings export-secret Copy, with a
  "clipboard auto-clears in 45s" notice. pumpSecretClipboardClear() runs each frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 14:00:45 -05:00
parent e40962cdf2
commit 094771af81
5 changed files with 58 additions and 4 deletions

View File

@@ -11,6 +11,7 @@
#include "../util/base64.h"
#include <curl/curl.h>
#include <sodium.h>
#include <atomic>
#include <cstdio>
#include <cstring>
@@ -145,9 +146,11 @@ bool RPCClient::connect(const std::string& host, const std::string& port,
port_ = port;
last_connect_info_ = json();
// Create Basic auth header with proper base64 encoding
// Create Basic auth header with proper base64 encoding, then wipe the plaintext
// "user:password" temporary (std::string does not zero its buffer on destruction).
std::string credentials = user + ":" + password;
auth_ = util::base64_encode(credentials);
if (!credentials.empty()) sodium_memzero(credentials.data(), credentials.size());
impl_->url = std::string(useTls ? "https://" : "http://") + host + ":" + port + "/";
VERBOSE_LOGF("Connecting to dragonxd at %s\n", impl_->url.c_str());