Lesson 16 / 28

Human Approval That Cannot Be Tampered With

Bind an approval to exactly the action the human saw.

Approve the action, not a summary

A confirmation dialog protects you only if it shows the real action and the approval is bound to it. Show exactly what will happen ("Refund 300 rupees on order 481516 to the original card"), not the model's paraphrase, which an attacker may have shaped. After the human approves, the system should execute exactly that action and nothing else: sign the approved action (for example an HMAC over its fields with a server-side key) or store it server-side by ID, and verify before executing, so the model cannot change the amount after approval. Beware of approval fatigue: too many prompts train people to click yes; ask only for what matters, group related steps, and make the risky details prominent. For the highest-risk actions, require a second person.

Signing the approved action, run

I ran this with plain Python 3 (standard library only). All attacks here are harmless demonstrations on local data, using no real systems. The token is an HMAC over the exact approved action. The original action verifies; the same action with the amount changed from 300 to 30000 fails verification, so the model cannot alter it after approval. The secret here is a placeholder; real code loads it from a secrets manager.

import hmac, hashlib, json

SECRET = b"server-side-secret"         # in real code: from a secrets manager, never in the repo

def sign(action):
    msg = json.dumps(action, sort_keys=True).encode()
    return hmac.new(SECRET, msg, hashlib.sha256).hexdigest()

def verify(action, token): return hmac.compare_digest(sign(action), token)

proposed = {"tool": "refund", "order": "481516", "amount": 300}
token = sign(proposed)                                         # issued when the HUMAN approves exactly this action
print("approved action executes:", verify(proposed, token))
tampered = {**proposed, "amount": 30000}                       # the model (or an attacker) tries to change it after approval
print("tampered action executes:", verify(tampered, token))

Output:

approved action executes: True
tampered action executes: False

Avoid approval fatigue

Ask for approval only for meaningful actions and show the key details prominently.

Quick check: Why bind the approval to the exact action?

  • To avoid logging
  • To make approvals slower
  • So the amount or target cannot be changed after the human said yes
  • Approvals do not need binding
Answer

So the amount or target cannot be changed after the human said yes — An unbound "yes" can be reused for a different, harmful action.