Skip to content

Bug: Unsigned note consumption can force an account to pay fees when allow_unauthorized_input_notes=true #2964

@onurinanc

Description

@onurinanc

Root Cause: auth_tx_acl can skip signature::authenticate_transaction when input note consumption is allowed, but transaction fees are still charged to the executing account.

Example:

  • A public account uses AuthSingleSigAcl with allow_unauthorized_input_notes=true.
  • An attacker submits a transaction that consumes a (possibly zero-asset) input note, creates no output notes, and calls no configured trigger procedures.
  • auth_tx_acl takes the no-signature branch and the account still pays the transaction fee.

Location: miden-standards/asm/account_components/auth/singlesig_acl.masm

AuthSingleSigAcl computes auth_required as the OR of (1) "a trigger procedure root was detected as called", (2) "output notes were created and allow_unauthorized_output_notes is disabled", and (3) "input notes were consumed and allow_unauthorized_input_notes is disabled" (see the auth_required computation in auth_tx_acl). When auth_required is false, the procedure returns successfully without calling signature::authenticate_transaction, meaning the account can be executed in a "no-signature" mode.

If allow_unauthorized_input_notes=true, then input note consumption does not force authentication (see the input-note gate in auth_tx_acl). In this configuration, a transaction can consume at least one input note while avoiding both output-note creation and trigger procedures, keeping auth_required=false and skipping signature verification. Since transaction fees are still charged to the executing account, this enables fee-drain and sustained denial of service by repeatedly executing unsigned transactions until the account's fee-paying balance is exhausted; this is amplified by the no-signature nonce logic that may preserve the nonce when no pre-fee state change occurs (see the conditional nonce bump in auth_tx_acl and the fee-parameter commitment described in signature::authenticate_transaction).

How to resolve this issue?:
Approach 1:
We can consider requiring authentication whenever tx::get_num_input_notes != 0 for accounts that may be targeted by third-party executors, or otherwise ensuring that "no-signature" transactions cannot cause fee payment on behalf of the account (e.g., by forcing a nonce increment on any note consumption, or by requiring a signature for any transaction that consumes notes). Consider documenting and enforcing that allow_unauthorized_input_notes=true is unsafe for public accounts unless an additional executor authorization mechanism exists.

Approach 2:

Originally specified by @PhilippGackstatter in this comment: #2958 (comment)

We can flip the ACL semantics, mirroring the pattern already used by AuthMultisig::compute_transaction_threshold: instead of listing the procedures that trigger a signature requirement, we list the procedures that are exempt from it. Everything not on the exempt list defaults to requiring a signature, so forgetting to register a new setter can never silently leave it permissionless. This also lets us removing allow_unauthorized_output_notes and allow_unauthorized_input_notes flags.

Under the proposed semantics auth_tx_acl enforces three invariants and computes auth_required as their OR:

  • any called procedure that is not on the exempt list forces auth_required = true.
  • if any input note is consumed AND no procedure from the exempt list was called during the transaction, auth_required = true. This is the replacement for the removed allow_unauthorized_input_notes.
  • if any output note was created, auth_required = true, unconditionally.

Metadata

Metadata

Assignees

No one assigned

    Labels

    standardsRelated to standard note scripts or account components

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions