Everything you need to know about security on XRPL: key management, transaction signing, common attack vectors, and best practices for dApp security.
What you'll learn
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.
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:
SetRegularKey transaction).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.
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:
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 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:
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.A few failure modes show up again and again:
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.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.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.
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