intermediate 60 min

Security Essentials

Everything you need to know about security on XRPL: key management, transaction signing, common attack vectors, and best practices for dApp security.

Prerequisites

Complete these before starting this module:

What you'll learn

  • Explain the difference between a master key pair and a regular key pair, and why you keep the master offline.
  • Choose a safe key-management posture for a dApp: regular keys, multi-signing, and never embedding secrets in client code.
  • Reason about reserves so your application never strands an account below its requirement.
  • Recognise the common signing and submission pitfalls (replays, blind signing, trusting a result code too early) and how to avoid them.
  • Write a mainnet pre-flight checklist before pointing any code at real value.
Complete this module by self-assessment. Jump to assessment

Overview

Up to now you've been moving test-XRP around a playground where the worst thing that can happen is you have to refund a wallet. Security is what changes when the value stops being free. On the XRP Ledger there is no support desk, no "forgot password" link, and no chargebacks: whoever holds the keys controls the money, and a validated transaction is final. That's a feature — settlement you can build on — but it means the safety has to live in your design, not in a recovery process bolted on afterward.

This module is the mental model you carry into every other build. We'll work through four things that bite real teams: how keys actually work and where to keep them, how to sign transactions without handing over the keys to the universe, how reserves quietly constrain what your app can do, and the everyday pitfalls that turn a working demo into a mainnet incident. None of it requires new code — it requires knowing which questions to ask before you ship.

Keys: master vs regular

An XRPL account's address is derived from its master key pair. That relationship is permanent — you cannot change or remove the master key, only disable it. So the master key is less like a password and more like the deed to the house: lose control of it and you've lost the account, full stop. Anyone who learns your master private key or its seed has complete control, with one escape hatch we'll get to.

Here's the move that makes this survivable. The ledger lets you authorise a second, independent regular key pair for day-to-day signing. The regular key can do almost everything the master can, but unlike the master it can be rotated or replaced at any time without touching the account itself. So the pattern is:

  • Generate the account, then assign a regular key (a SetRegularKey transaction).
  • Take the master key offline — paper, hardware wallet, a vault — and never use it for routine operations.
  • Run your application on the regular key.

Think of the master key as the spare key in a safe-deposit box and the regular key as the one on your keychain. If the keychain key is ever compromised, you retrieve the master from the box, rotate to a fresh regular key, and you're back in control without ever migrating funds. If you'd been signing with the master all along, your only option after a leak would be to drain the account to a new one and hope you moved faster than the attacker.

A subtlety worth internalising: only the master key can do a few specific things — send the account's very first transaction, disable itself, permanently give up freeze ability, and send the special zero-cost key-reset transactions. So even in a regular-key world, you can't throw the master away; you keep it safe precisely so it's there when you need to recover.

Signing algorithms and secrets

When you create a wallet you pick a signing algorithm. secp256k1 is the default (the same curve Bitcoin uses); ed25519 is the newer option with better performance and slightly shorter keys. For most applications the choice is not a security decision — both are supported as master or regular keys throughout the ledger, and xrpl.js handles either transparently. Pick one and be consistent; don't agonise over it.

What actually matters is how you treat the secret. The seed, the passphrase, and the private key are all the same level of secret: anyone who has any of them has full control of the address. That has direct consequences for how you build:

  • Never put a secret in client-side code. Anything shipped to a browser or mobile app is readable. Signing for an account the user doesn't personally control belongs on a server or in a signing service the user explicitly authorises.
  • Never commit a seed to git, log it, or paste it into an error tracker. Keep secrets in environment variables or a secrets manager, and scrub them out of anything you might capture.
  • Treat a leaked seed as a total compromise, immediately. There's no partial leak. If a seed touched somewhere it shouldn't have, rotate the regular key (or recover via the master) before doing anything else.

Multi-signing: removing the single point of failure

A single key is a single point of failure. Multi-signing spreads authority across a SignerList — between 1 and 32 addresses, each carrying a weight, with a quorum that defines the minimum combined weight needed to authorise a transaction. A transaction is only valid once enough signers have signed to meet the quorum.

The payoff is that an attacker now has to compromise several keys, ideally on several devices, instead of one. It also enables shared custody (a treasury that needs two of three officers) and graceful delegation when someone's unavailable. The classic example: a CEO with weight 3, three VPs at weight 2, three directors at weight 1, quorum 3 — so the CEO alone, or any two VPs, or all three directors can sign. You get flexibility without lowering the bar. A few rules keep the list sane: an account can't list its own address, no duplicates are allowed, and the quorum has to be reachable by the weights you assigned (set a quorum no one can ever meet and you've locked yourself out).

A useful pattern is to combine both ideas: assign a SignerList for routine operations and keep the master key offline as the ultimate backstop. The master can rebuild the SignerList if signers are lost, and the multi-sig setup means no single online key ever holds unilateral control. That layering — offline master, multi-sig for daily authority — is roughly what mature custodians and treasuries converge on.

The cost is concrete and worth budgeting for: a multi-signed transaction requires a fee of at least (N+1) times the normal fee, where N is the number of signatures. More security, slightly more cost per transaction. For anything holding meaningful value — a treasury, a custodial service, a DAO wallet — that trade is almost always worth it.

Reserves: a security constraint in disguise

Reserves feel like an accounting detail until they break your app at the worst moment. The ledger requires every account to hold a base reserve (currently 1 XRP on mainnet) just to exist, plus an owner reserve (currently 0.2 XRP) for each object it owns — trust lines, offers, escrows, checks, NFT pages, and so on. These numbers can change over time through fee voting, so treat them as current values, not constants baked into your code.

The security-relevant behaviour is what happens at the edge. If an account's balance drops to its reserve — say its fees eat into it — it can no longer send XRP or create new objects that would raise the reserve. It doesn't disappear; it just becomes partly frozen in a way that surprises people. So:

  • A balance is not the same as spendable XRP. Always compute spendable as balance minus the current reserve before you let a user "send everything."
  • Every object your app creates raises the account's reserve. If you open trust lines or place offers on a user's behalf, you're consuming their XRP. Don't strand them at the threshold.
  • Read the reserve from the network (it's reported in server_info/server_state) rather than hard-coding 1 and 0.2. Hard-coded reserve math is a bug waiting for the next fee vote.

Common pitfalls and mainnet hygiene

A few failure modes show up again and again:

  • Trusting submission over validation. Submitting a transaction is not the same as it succeeding. Only tesSUCCESS in the validated metadata means the ledger applied it. Treating a ter/tec code, or a mere "submitted" response, as success is how money appears to move but doesn't.
  • Re-submitting blindly. Each transaction has a Sequence. Setting LastLedgerSequence bounds how long a transaction can linger, so a stuck transaction definitively expires instead of landing unexpectedly later. Without it, a "retry" can result in the original and the retry both applying.
  • Blind signing. Never sign a transaction you haven't inspected. For user-facing apps, show the human exactly what they're authorising — destination, amount, type — before signing.
  • Same code, different network. Testnet and mainnet differ only by endpoint. That's the danger: the wrong WebSocket URL with a real-funded wallet spends real value. Gate mainnet behind an explicit, deliberate config flag.

Before any code touches mainnet, run a pre-flight: master key offline and account signing with a regular key; secrets in a manager, not in source or logs; multi-signing for anything custodial; reserve-aware balance math; LastLedgerSequence on every submission; validation checked on tesSUCCESS; and a hard, obvious switch between test and live networks. Mainnet hygiene isn't a separate phase — it's just refusing to skip the checklist when the value becomes real.

Check

You're done when you can take a sample (or your own) XRPL dApp and write a short security review that holds up. Cover three areas at minimum: key management (is the master offline? is signing done with a rotatable regular key? are any secrets exposed to a client or to logs?), the signing and submission flow (is success confirmed on validated tesSUCCESS? is LastLedgerSequence set? is anything blind-signed?), and reserve assumptions (does the app treat balance as spendable, and does it account for objects it creates?). Produce at least three concrete findings, each with a specific fix. Submit the doc and self-assess it against this checklist.

Assignments

0 of 1 complete