Manav.id
Security ยท 14 min read

Reply APPROVE is not an approval. It is a mailbox proof.

Somewhere in your company today, a payment, a production access grant, or a new supplier will be authorized by a thumbs-up emoji. The workflow engine will record a name, an approver, and a timestamp. What actually happened is that someone holding a chat session sent a message, and those are not the same fact.

The thumbs-up that moved a hundred and eighty thousand dollars

Picture a finance channel in Slack at a company of about six hundred people. It is the kind of channel that exists at every company now: half genuine collaboration, half a feed of automated cards from the procurement system.

At 4:40 on a Thursday a card appears. New supplier, first invoice, one hundred and eighty thousand dollars, thirty day terms, requested by a real person in operations against a real project code. Underneath the card are two buttons, Approve and Reject, and a note that this request is waiting on the VP of Finance.

At 4:52 the card updates. Approved by the VP of Finance. The procurement system writes a row: request ID, approver identity, timestamp, decision. That row is the audit evidence. It is what an auditor will sample next year, and it will look immaculate.

Now change exactly one detail in that story. The VP of Finance did not tap the button. Nine days earlier they had signed in to what looked like the company single sign-on page, through a link in what looked like a document share notification. It was a proxy. The credentials went through to the real identity provider, the multi-factor prompt went through, the VP approved it because they had genuinely just tried to sign in, and the attacker kept the thing that actually matters: the session token that came back.

From that moment the attacker was the VP of Finance to every system that trusts that session. Not "could impersonate". Was. Same token, same cookies, same scopes. Every approval card, every reply-to-approve email, every Teams action that the VP could exercise, the attacker could exercise, silently, at 4:52 on a Thursday.

The audit row is not wrong, exactly. It faithfully records what the workflow engine knew. The workflow engine knew that a message arrived from an authenticated session belonging to the VP of Finance. That is the entire extent of its knowledge, and it is dramatically less than the word "approved" implies.

Short answer: No, approving purchase orders or payments by email reply, Slack reaction, or Teams card is not secure. Those approvals prove only that something controlled the channel at that moment, so anyone holding a stolen session, a shared mailbox, or an agent with chat scopes can produce them. A secure approval is a cryptographic signature made on the approver's own device over the specific request details, stored as evidence.

What is a mailbox-grade approval?

A mailbox-grade approval is an authorization whose strength is exactly the strength of the channel that carried it, and no more.

The name is deliberately unflattering, because the category deserves it and because it currently has no name at all, which is precisely why it has spread. Nobody buys a mailbox-grade approval. They buy "approve from anywhere", which is the same thing with better marketing.

The cheque and the note

Here is the analogy, and then the precise version underneath it.

There are two ways a company can tell a bank to pay someone. The first is a signed cheque. The signature is made by a specific person, it is physically bound to that particular piece of paper with that particular amount and payee, and the bank can compare it against a specimen on file. The second is a note that says "the finance director says it is fine to pay Kellner one hundred and eighty thousand". The note might be entirely true. But the bank is not verifying the finance director. It is verifying that a note arrived.

Every chat and email approval in your company is the second thing. The workflow engine has an authenticated channel and it takes delivery of a message on that channel as evidence of a decision. The message is the note.

Now the precise version. In a signature, the private key never leaves the approver's device, and the thing signed is the request itself, so the artifact binds one human to one specific set of facts. In a chat approval, the artifact is a message, the credential is a bearer token held by the client, and the binding to a specific request is a request ID inside the message body that the transport does not protect. Steal the bearer token and you inherit every decision the holder can make. This is the same structural weakness that lets an attacker walk through a help desk, which we take apart in the piece on MFA reset attacks.

How much does this actually cost?

Be honest about the evidence here, because the honest version is strong enough.

No public dataset isolates "approval by chat" as a loss line. Nobody files a complaint that says the emoji did it. What the data does show is the size of the channel compromise problem that sits underneath.

Business email compromise produced $3.05 billion in reported United States losses across 24,768 complaints in 2025 (FBI Internet Crime Complaint Center annual report). That entire category exists because mailboxes are where authorization happens. The attacker is not breaking cryptography. They are getting into the channel where decisions are communicated, and then communicating a decision.

The industrialization of that access is the part that should change your risk model. Adversary in the middle phishing kits, sold as a service, proxy the real login page so that multi-factor authentication completes normally and the kit walks away with the session. Microsoft Threat Intelligence, reporting on the disruption of the Tycoon 2FA platform with Europol and industry partners, described a service used against more than 96,000 victims, with 330 domains seized in the action (Microsoft Security, March 2026). Researchers at Sekoia counted eleven distinct adversary in the middle kits in commercial operation in the first four months of 2025, which tells you this is a market with competitors, pricing, and support, not a single tool.

Now do the multiplication that nobody does. Each of those 96,000 stolen sessions was not one login. It was a standing right to exercise every approval that person could exercise, for as long as the session lived. A mid-size enterprise routes thousands of financially binding approvals a month through mail and chat: purchase orders, supplier onboarding, expense sign-off, access grants, contract countersignature. The blast radius of one stolen session is not one fraudulent transaction. It is a portfolio.

This is a feature, not a misconfiguration

It is worth being clear that nobody did anything wrong to end up here. Approval by mail and chat is a documented, supported, actively marketed capability of the major workflow platforms. SAP Ariba, Coupa, Oracle NetSuite, ServiceNow, Workday and Jira Service Management all ship some form of approve by email or approve from a chat card, because customers asked for it and because approvals that wait for someone to log in to a procurement portal do not get done.

That is evidence of prevalence, not evidence of loss, and the distinction matters. The point is not that these vendors are careless. The point is that an entire category of control migrated into a transport that was never designed to carry authorization, and the migration happened one convenience feature at a time, with no moment where anyone asked what an approval is.

What identity does the workflow engine actually record?

There are three common implementations, and it is worth knowing which one you are running, because they fail slightly differently and all of them fail.

MechanismWhat it verifiesWhat defeats it
Reply parsing (reply APPROVE to this email)A message arrived from an address on the approver listMailbox access, thread injection, a forwarding rule, header spoofing where SPF is lax
Signed action link (a unique URL in the email)Someone opened a URL that only the recipient should haveAnyone who reads the mailbox, link scanners, shared inboxes, forwarded mail
Chat card action or reactionA client holding a valid session invoked an actionStolen session token, infostealer, a bot or agent with the right scopes

Every row has the same shape. The system verifies possession of a channel credential and records it as a human decision. The gap between those two things is where the loss lives, and no amount of alerting closes it, because there is nothing anomalous to observe: the approval came from the right place, at a plausible hour, from a real session.

Why re-authentication does not fix it

The instinctive fix is a step-up: make the approver re-authenticate before the approval commits. This helps against a shoulder surfer and against a walked-away laptop. It does nothing against session theft, and the reason is worth stating precisely.

If the attacker holds the session, the re-authentication prompt renders inside the attacker's browser, and the attacker satisfies it the same way they satisfied the first one: by proxying it. A step-up inside a compromised session is a compromised step-up. You have added a second lock to the same door and given the attacker both keys.

The property you need is different in kind. The proof has to be produced by something the attacker does not hold, which in practice means a private key on a device in the approver's hand, and it has to cover the request, not the session. That is the distinction we walk through in how you prove an action was authorized.

What happens when an AI agent has chat scopes?

This is the part that turns a slow-burning problem into an urgent one, and it has arrived faster than most approval policies have been reviewed.

Agents now hold workspace credentials. They read channels, post messages, react to messages, and click buttons on cards, because those are ordinary capabilities granted by ordinary application scopes. An agent with permission to write in a channel and act on interactive components can, mechanically, approve things.

Sit with the implication. If your approval control is a button in a chat client, then your approval control is satisfiable by software. Not by a sophisticated attacker with a novel exploit. By a well-behaved integration doing exactly what its scopes permit, or by a prompt injected into a document that an agent summarizes.

We call this approval theater: a human-in-the-loop control that an agent can complete on its own. It looks like oversight in the architecture diagram, it satisfies the auditor's checklist, and it stops nothing. Any control whose entire physical manifestation is a click inside a session that software can hold is not a human control. It is a UI convention.

The corollary is not that agents should be kept out of chat. It is that the approval must live somewhere an agent structurally cannot reach, which means a separate device, a separate key, and a payload the human sees on that device.

What does a real approval look like?

Keep the notification exactly where it is. The Slack card stays. The email stays. What changes is what the button does and what the workflow engine records.

Instead of the button being the approval, the button opens a signing sheet on the approver's own device. The sheet shows the facts that matter in plain language, and a confirmation produces a signature over those exact facts.

What gets signed is not "approved". It is the request:

{
  "action":      "purchase_order.approve",
  "tenant":      "acme-corp",
  "request_id":  "PO-2026-04871",
  "vendor":      "Kellner Fabrication Ltd",
  "vendor_id":   "KELLNER-0142",
  "amount":      "180000.00",
  "currency":    "USD",
  "terms":       "net30",
  "requested_by":"[email protected]",
  "approver":    "[email protected]",
  "nonce":       "01JQ7B2M4T",
  "issued_at":   "2026-09-03T16:40:11Z",
  "expires_at":  "2026-09-03T17:10:11Z"
}

The amount, the vendor, and the request ID are inside the signed bytes. That single design choice removes an entire family of attacks. An attacker who intercepts a signing request cannot raise the amount, cannot swap the payee, and cannot replay yesterday's approval against today's invoice, because any of those changes breaks the signature. The approver also cannot approve something other than what they read, because what they read is what was signed.

The workflow side is small:

def on_approval(request, receipt):
    payload = verify_receipt(receipt, MANAV_PUBLIC_KEY)   # offline, no callback
    assert payload["action"]     == "purchase_order.approve"
    assert payload["tenant"]     == THIS_TENANT
    assert payload["request_id"] == request.id
    assert payload["amount"]     == request.amount        # value is bound
    assert payload["vendor_id"]  == request.vendor_id     # payee is bound
    assert payload["approver"]   in approvers_for(request)
    assert not nonce_seen(payload["nonce"])
    assert now() < payload["expires_at"]

    request.approve(by=payload["approver"])
    attach_receipt(request, receipt)      # this is the audit evidence
    consume_nonce(payload["nonce"])

Two things about verify_receipt are worth noticing. It is an Ed25519 signature check against a published key, so it makes no network call, which means the verification works offline, works in five years, and works when our service is down. And the receipt it validates is a portable artifact: the auditor can check it themselves without trusting your workflow platform, your logs, or us. The mechanics are in the developer docs, and there is a working privileged-grant version in the admin approval demo.

The experience, honestly

The VP taps Approve on the Slack card. A sheet opens on their phone showing four lines: Kellner Fabrication, one hundred and eighty thousand dollars, net 30, PO-2026-04871. They confirm with the gesture that unlocks the phone. The card updates. Elapsed time is about ten seconds, and one of those seconds is the part where they actually read the amount, which is not nothing.

For the attacker holding the stolen session, the experience is different. They can tap the button. The sheet opens on a device they do not have. Nothing happens.

What does an auditor get out of this?

Under Sarbanes Oxley and in SOC 2 engagements, approval is a control that gets tested by sampling. The tester picks transactions and asks for evidence that the right person approved them.

Today that evidence is a screenshot of a workflow log, which is an assertion by the system under audit that the control operated. It is circular, and everyone involved knows it is circular, and it passes anyway because there has been nothing better to offer.

A receipt breaks the circle. It is verifiable independently of the system that produced it. Hand the tester a receipt and a public key and they can confirm, on their own laptop, that a specific enrolled key signed those specific facts at that time. The sample stops being "we believe our log" and becomes "here is a signature, check it yourself".

Honest limits

What to do this week

  1. Inventory your approval channels. For each workflow platform, list every path by which an approval can be recorded. Most teams find two or three they did not know were enabled.
  2. For each path, write down the identity it records. Not the name it displays, the fact it verifies. If the honest answer is "an authenticated session", write that down. It is the finding.
  3. Sort approvals by value and by blast radius. Payments and supplier changes are obvious. Do not forget production access grants and privileged role changes, which have no dollar value attached and cause the largest incidents.
  4. List every bot and agent with chat scopes that can act on interactive messages. Then ask, for each, whether it could complete an approval. The answer will be uncomfortable for at least one of them.
  5. Pick one threshold and one request type as a pilot. Supplier bank changes and privileged access grants are the two best starting points, because both are high consequence and low volume.
  6. Ask your auditor what evidence they would accept. They will usually say a verifiable artifact beats a log entry, and that answer is useful to have in writing when you ask for budget.
  7. Kill reply-to-approve on anything financial. This one costs nothing and can be done on Monday. Reply parsing is the weakest mechanism in the table above and it is almost always enabled by default.

Frequently asked questions

Is approving purchase orders or payments by email or Slack secure? No. An email reply or a chat card action proves that something controlled the channel, not that a specific human decided anything. Stolen session tokens, shared mailboxes, forwarding rules, and bots with workspace scopes can all produce these approvals. A secure approval is a device-bound signature over the request details, kept as evidence.

Can an AI agent approve requests in Slack? Mechanically, yes. An agent granted permission to post and to act on interactive components can click Approve. That is why an approval that consists only of a click inside a session is not a human control. A signature produced on a separate enrolled device is not reachable by an agent operating in the workspace.

Does multi-factor authentication protect chat approvals? It protects the login, not the approvals that follow it. Adversary in the middle kits proxy the real sign-in page so multi-factor completes normally and the attacker keeps the resulting session. Every approval that session can make is then available to the attacker, without any further authentication challenge.

What does SOX require as approval evidence? Auditors test that an authorized person approved a transaction, usually by sampling and inspecting whatever evidence the system holds. A workflow log entry is an assertion by the audited system. An independently verifiable signature over the transaction details is stronger evidence, because the tester can check it without trusting the system that produced it.

Will this slow our approvers down? By roughly ten seconds on the approvals you choose to gate, which should be a small subset. The correct design is threshold based: sign the payments, supplier changes, and privileged access grants, and leave routine low value approvals in the existing flow. Gating everything produces fatigue, and fatigued approvers stop reading.

Do we have to replace our workflow platform? No. The notification stays where it is. The change is that the approval action opens a signing sheet and the platform stores the returned receipt against the record. Integration is at the approval callback, and it is API first today rather than a native application in the chat client.

Sources

  1. FBI Internet Crime Complaint Center, 2025 Internet Crime Report: business email compromise loss totals and complaint counts. ic3.gov annual reports
  2. Microsoft Threat Intelligence on the Tycoon 2FA adversary in the middle platform and its disruption with Europol and industry partners, March 2026. Microsoft Security blog
  3. Sekoia Threat Detection and Research on adversary in the middle phishing kits in commercial operation, 2025. blog.sekoia.io
  4. Europol press materials on coordinated action against phishing as a service platforms. europol.europa.eu
  5. Slack application permission scopes documentation, describing what a workspace application can read and act on. api.slack.com/scopes
  6. NIST Special Publication 800-63B, Digital Identity Guidelines, on authenticator and session management. pages.nist.gov
  7. W3C Web Authentication (WebAuthn) specification, the basis for device bound public key credentials. w3.org WebAuthn
If an agent can click your human-in-the-loop control, it was never a human-in-the-loop control.