Solana Upgrade Authority Security: The $40M Lesson Most Protocols Haven't Learned

Published: (March 13, 2026 at 04:34 PM EDT)
8 min read
Source: Dev.to

Source: Dev.to

The Problem

Every Solana program deployed with solana program deploy is upgradeable by default.
The upgrade authority – a single keypair – has god‑mode access to your protocol: it can replace the entire program binary. No timelock. No multisig. No warning.

The January 2026 Step Finance collapse ($40 M drained) wasn’t a smart‑contract bug.
It was a compromised executive device that held the upgrade‑authority keys. The attacker didn’t need a code vulnerability – they only needed the key to replace the program.

If your Solana program’s upgrade authority is a single hot wallet, you’re one phishing email away from the same fate.

How Upgrade Authority Works

When you deploy a Solana program, the BPF Loader creates:

  1. A program account that points to a data account containing your executable bytes.
  2. An upgrade‑authority pubkey stored in the program account’s metadata.

Anyone who controls that key can call BPFLoaderUpgradeable::Upgrade and swap the entire program binary.

Example: Checking Who Controls Your Program

solana program show 

Output

Program Id:          YourProgram111111111111111111111111
Owner:               BPFLoaderUpgradeab1e11111111111111111111111
ProgramData Address: ProgramData111111111111111111111
Authority:           HotWa11et1111111111111111111111111  ← single point of failure
Last Deployed In:    Slot 250000000
Data Length:         832456 bytes

The Authority field is the attack target. If an attacker obtains this key they can:

  • Deploy a malicious binary that drains all PDAs.
  • Replace program logic to bypass access controls.
  • Insert a backdoor that siphons funds over time.
  • Brick the program entirely.

Default authority = developer’s local keypair (~/.config/solana/id.json).
This is how 60 %+ of Solana programs ship to mainnet. Your program’s security equals your laptop’s security.

Risks: malware, phishing, device theft, insider threat.

Securing the Upgrade Authority

1. Transfer Authority to a Ledger (Hardware Wallet)

solana program set-upgrade-authority  \
  --new-upgrade-authority  \
  --keypair usb://ledger

Pros: the key never touches a hot machine.
Cons: still a single key held by a single person → bus factor = 1.

Risks: physical theft, coercion, single‑person compromise.

2. Use a Multisig (Squads Protocol – the standard Solana multisig)

import { Squads } from "@sqds/sdk";

// Create a multisig vault for upgrade authority
const multisigAccount = await squads.createMultisig(
  2,               // threshold: 2‑of‑3 required
  createKey,
  [
    member1.publicKey, // CTO – hardware wallet
    member2.publicKey, // Lead Dev – hardware wallet
    member3.publicKey, // Security Lead – hardware wallet
  ]
);

// Transfer program upgrade authority to multisig
await squads.createProposalForProgramUpgrade(
  multisigAccount,
  programId,
  bufferAddress,
  spillAddress,
  "v2.1.0 – Fix oracle validation"
);

Result: 2‑of‑3 keyholders must approve any upgrade. An attacker must compromise multiple parties across different security boundaries.

Risks: social engineering of multiple members, governance attacks, emergency‑response latency.

3. Add a Mandatory Delay (Timelock) Between Proposal and Execution

Example: Custom Upgrade Program with a 48‑hour Timelock (Anchor)

// ── Propose Upgrade ───────────────────────────────────────────────────────
pub fn propose_upgrade(ctx: Context, buffer: Pubkey) -> Result {
    let proposal = &mut ctx.accounts.proposal;
    proposal.buffer = buffer;
    proposal.proposed_at = Clock::get()?.unix_timestamp;
    proposal.execution_time = proposal.proposed_at + TIMELOCK_DURATION; // 48 h
    proposal.approvals = 0;
    proposal.executed = false;

    emit!(UpgradeProposed {
        program: ctx.accounts.program.key(),
        buffer,
        execution_time: proposal.execution_time,
    });

    Ok(())
}

// ── Execute Upgrade ───────────────────────────────────────────────────────
pub fn execute_upgrade(ctx: Context) -> Result {
    let proposal = &ctx.accounts.proposal;
    let now = Clock::get()?.unix_timestamp;

    require!(proposal.approvals >= THRESHOLD, ErrorCode::InsufficientApprovals);
    require!(now >= proposal.execution_time, ErrorCode::TimelockNotExpired);
    require!(!proposal.executed, ErrorCode::AlreadyExecuted);

    // Execute the BPFLoaderUpgradeable::Upgrade instruction via CPI
    // ...

    Ok(())
}

// ── Cancel Upgrade ───────────────────────────────────────────────────────
pub fn cancel_upgrade(ctx: Context) -> Result {
    // Any single guardian can cancel during the timelock window
    let proposal = &mut ctx.accounts.proposal;
    require!(!proposal.executed, ErrorCode::AlreadyExecuted);
    proposal.cancelled = true;

    emit!(UpgradeCancelled {
        program: ctx.accounts.program.key(),
        cancelled_by: ctx.accounts.guardian.key(),
    });

    Ok(())
}

Effect: Even if attackers compromise the multisig, the community has 48 h to:

  • Detect an unauthorized upgrade proposal.
  • Cancel it via any single guardian.
  • Alert users to withdraw funds.

Risk: Emergency bugs take longer to patch (mitigated by an emergency fast‑path with a higher threshold).

4. Nuclear Option: Revoke Upgrade Authority Permanently

solana program set-upgrade-authority  --final

The program can never be changed again. This is the gold standard for security but means:

  • No bug fixes possible.
  • No feature additions.
  • Must be absolutely certain the code is correct.

Best for: core AMM logic, token contracts, bridges (after extensive auditing).

Putting It All Together – A Maturity Pattern

PathGovernanceTimelockExecution Speed
Normal Upgrade2‑of‑3 multisig48 hStandard
Emergency4‑of‑5 multisig (expanded council)4 hFast
Critical (active exploit)5‑of‑5 multisig0 h (immediate)Immediate + automatic pause

Key insight: Higher urgency requires higher consensus, not lower. If something truly needs immediate deployment, gathering all 5 signers should be achievable because the stakes are highest.

TL;DR Checklist

  • Never leave the default hot‑wallet authority on mainnet.
  • Move authority to hardware wallets (preferably a multisig).
  • Add a timelock to any upgrade path.
  • Consider finalizing the program once it’s battle‑tested.
  • Document the upgrade process and train the team on phishing/physical‑security best practices.

Secure the upgrade authority today – it’s the single most critical key protecting your Solana protocol.

Upgrade Authority Monitoring & Security Checklist

Detection is as important as prevention. Set up alerts for any interaction with your program’s upgrade authority:

import asyncio
from datetime import datetime
from solders.pubkey import Pubkey
from solana.rpc.websocket_api import connect

PROGRAM_ID = Pubkey.from_string("YourProgram111111111111111111111111")
BPF_LOADER = Pubkey.from_string("BPFLoaderUpgradeab1e11111111111111111111111")

async def monitor_upgrades():
    async with connect("wss://api.mainnet-beta.solana.com") as ws:
        # Subscribe to any transaction mentioning the BPF Loader + your program
        await ws.logs_subscribe(
            filter_={"mentions": [str(BPF_LOADER)]},
            commitment="confirmed"
        )

        async for msg in ws:
            logs = msg[0].result.value.logs
            signature = msg[0].result.value.signature

            # Check if this upgrade targets our program
            if any("Upgrade" in log for log in logs):
                if any(str(PROGRAM_ID) in log for log in logs):
                    await send_alert(
                        f"🚨 PROGRAM UPGRADE DETECTED!\n"
                        f"Program: {PROGRAM_ID}\n"
                        f"Tx: {signature}\n"
                        f"Time: {datetime.utcnow()}\n"
                        f"ACTION REQUIRED: Verify this was authorized"
                    )

async def send_alert(message: str):
    # Send to Telegram, Discord, PagerDuty, etc.
    print(message)

asyncio.run(monitor_upgrades())

Also monitor for SetAuthority instructions

An attacker’s first move might be to transfer upgrade authority to their own key:

# Quick check: has your authority changed?
solana program show  | grep Authority

Audit Checklist (based on 2026 incidents such as Step Finance)

  • Upgrade authority is NOT a single hot wallet
  • Multisig with ≥2‑of‑3 threshold
  • Each signer uses a hardware wallet
  • Signers are geographically distributed
  • No single person has access to the threshold number of keys
  • Backup/recovery procedure documented and tested
  • Timelock of ≥24 hours for normal upgrades
  • Emergency path with higher threshold (not lower timelock)
  • Program binary verified on‑chain before execution
  • Buffer account contents independently verified by ≥2 parties
  • Upgrade proposals publicly announced before execution
  • Real‑time alerts for Upgrade and SetAuthority instructions
  • Authority pubkey checked on every deployment
  • On‑chain program hash verified against source code
  • Solana Explorer / Anchor Verifiable Builds used
  • Program pause mechanism exists (separate from upgrade authority)
  • Emergency contact list for all multisig signers
  • Written runbook for “upgrade authority compromised” scenario
  • Regular drills (quarterly) to test emergency upgrade flow

Staged Path to Immutability

PhaseTimelineRequirements
Phase 1 (Launch)Immediate2‑of‑3 multisig, 24 h timelock
Phase 26 months3‑of‑5 multisig, 48 h timelock, ≥2 audits completed
Phase 31 yearCore logic frozen; only peripheral modules upgradeable
Phase 42+ yearsFull immutability after extensive battle‑testing

Each phase is a public commitment to your users. Document your current phase and the criteria for advancing to the next one.

Key Takeaways

  • The $40 M loss at Step Finance shows that a program is only as secure as its upgrade authority.
  • A perfectly audited contract is worthless if the key to replace it sits on an executive’s laptop.
  • The fix isn’t exotic cryptography; it’s operational discipline:
    1. Move to multisig today.
    2. Add a timelock this quarter.
    3. Plan your path to immutability.
    4. Monitor everything.

Your users trust you with their funds. The least you can do is ensure a single compromised laptop can’t betray that trust.

DreamWork Security publishes weekly research on DeFi security patterns. Follow us for more Solana and EVM security analysis.

0 views
Back to Blog

Related posts

Read more »

Travigo

Travel as fast as you speak with Gemini! Where live agents meet immersive storytelling & 3D navigation. This project was created for entering the Gemini Live Ag...

Micro games

Hey Gamers! 👾 As part of the Rapid Games Prototyping module, we are tasked with reviewing a peer's game. The challenge is to analyse a prototype built in just...