security(hygiene): best-effort scrubbing of residual in-memory secret copies (Tier 4)
Defense-in-depth against a local attacker / core dump reading un-zeroed heap copies of secrets (threat model is local, not remote). Best-effort by nature — std::string SSO/reallocation and ImGui buffer growth can still strand copies — so these shrink the exposure window rather than guarantee absence: - rpc_client performCall: a scope guard now sodium_memzeroes the request body + payload tree (walletpassphrase / *_importkey args) after the call and detaches CURLOPT_POSTFIELDS. Request content sent to curl is unchanged. - New ui::scrubImGuiInputTextState() zeroes ImGui's retained InputText buffers (live/revert/callback/deactivated) — the app memzeroed its own char[]s but ImGui kept copies. Called from wipeSecrets()/wipeDisplayedSecrets(). PINNED to ImGui 1.92.6 via a #error guard so a vendored bump forces a re-audit (the stb undo buffer is an incomplete type in the header, so it's left as a noted gap). - Lite exportSeed/exportPrivateKeys wipe result.value + the parsed JSON tree. - Seed-word grid (SplitSeedWords/RenderSeedWordGrid) zeroes its per-frame word and cell copies; the wizard word/chip vectors are zeroed before clearing. - ChatStore::clear() + ResetChatTab() zero decrypted message bodies on lock/switch/teardown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -370,6 +370,20 @@ std::string RPCClient::performCall(const std::string& method, const json& params
|
||||
|
||||
json payload = makePayload(method, params);
|
||||
std::string body = payload.dump();
|
||||
// Request-side secret scrub (responses are already scrubbed): the body + payload tree can carry
|
||||
// walletpassphrase / encryptwallet / *_importkey args. Wipe them when this scope ends (all return
|
||||
// and throw paths) and detach CURLOPT_POSTFIELDS so the reused handle holds no dangling pointer.
|
||||
struct RequestScrubGuard {
|
||||
CURL* curl; std::string& body; json& payload;
|
||||
~RequestScrubGuard() {
|
||||
if (!body.empty()) sodium_memzero(&body[0], body.size());
|
||||
scrubJsonSecrets(payload);
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, nullptr);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L);
|
||||
}
|
||||
}
|
||||
} requestScrubGuard{impl_->curl, body, payload};
|
||||
std::string response_data;
|
||||
|
||||
// Set POST data
|
||||
|
||||
Reference in New Issue
Block a user