Skip to content

Build1 publisher2 min readPublished

Anyone who reads one STK Push request body can decode the M-Pesa passkey

Safaricom's Daraja STK Push builds its request password as reversible base64 of the passkey and two values that are public or sent in the same body. Any log, APM trace or proxy that captures that body holds a decodable copy of a credential with no documented way to revoke it.

The Engineer · Build desk

Illustration accompanying Anyone who reads one STK Push request body can decode the M-Pesa passkey

What happened

  • The shortcode is not secret and the timestamp travels in the same request body, so decoding the field and removing those two known values returns the passkey.
  • A debug log that dumps request bodies, an APM trace, a body-capturing reverse proxy, a support screenshot or a forum snippet each keeps a recoverable copy of the credential.
  • Safaricom issues the passkey, and the developer found no documented endpoint to revoke it, leaving replacement as the only response to a leak.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure Anyone who can read the logs, traces, or a pasted support snippet can reconstruct the passkey, including roles that never handle credentials.
  • constraint With no way to revoke the passkey, a leak is not contained until you replace it and reconfigure every service that calls Daraja.
  • decision Protecting the passkey now lives in logging configuration: request-body capture has to be redacted where the request is built, because scrubbing it in the log viewer comes after the body is already stored.

Base64 is a transport encoding, not a hash. [1] The developer who found this, while building an open-source self-hosted mobile money gateway, put it in four words: "Base64 encodes. It does not hash." [5][8] Decode the string and the original bytes come back unchanged. The STK Push password is base64 of the business shortcode, the passkey and a timestamp concatenated, so recovering the passkey is three steps: decode the field, strip the shortcode off the front, strip the timestamp off the end. [1][2] Both values already sit in the request body you sent, so nothing external is needed to finish the job. What remains is the credential Safaricom issued you. [2]

To a reviewer skimming a request, the password field could pass for ciphertext. Base64 output only appears opaque; one function call reverses it, so the field survives inspection while carrying the passkey in the clear. [1] The secret is no longer confined to your config store. A decodable copy rides inside every STK Push request you make, so an ordinary debug log or trace ends up holding it. [2][3]

One boundary on the finding. The measurements were taken in Safaricom's sandbox, on shortcode 174379, between 18 and 23 September 2026. [7] The password construction is not a sandbox artefact: it is how the field is built, and base64 decodes the same way in production. [1]

What to watch

  • Whether Safaricom publishes a passkey revocation endpoint, giving operators a way to kill a leaked key instead of replacing it.
  • Whether Daraja moves STK Push authentication off reversible base64 toward a hashed or short-lived token.
  • Whether production Daraja behaves like these sandbox observations, since the tests ran only in the sandbox.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories