APS-15Required
Trust an email domain only after strict parsing
Before an email domain grants anything, such as joining an organisation or routing single sign-on, check the address against one plain local@domain shape. Refuse quotes, comments, encoded words, % and more than one @.
How to do it
Parsers and mail servers disagree about unusual addresses. An address such as "jo@example.com"@evil.example or one holding an encoded word like =?utf-8?q?...?= can read as example.com to the app. The mail server still delivers it to the attacker, who then confirms the address and joins the organisation. RFC 2047 says an encoded word must not appear in any part of an address. Allow only the characters and lengths you need, and convert an international domain to its ASCII form before the check. Store and send to exactly the string you checked. Then confirm it as Prove the email address before trusting it says, and let only a proven domain grant access. Compare addresses as Compare email addresses and usernames in one canonical form says.
Examples
JavaScript example
const MAX_ADDRESS_LENGTH = 254;
const PLAIN_ADDRESS = /^[a-z0-9._+-]{1,64}@[a-z0-9-]{1,63}(?:\.[a-z0-9-]{1,63})+$/i;
export function trustedEmailDomain(address) {
if (address.length > MAX_ADDRESS_LENGTH || !PLAIN_ADDRESS.test(address)) return null;
return address.split('@')[1].toLowerCase();
}
Python example
import re
MAX_ADDRESS_LENGTH = 254
PLAIN_ADDRESS = re.compile(r"[a-z0-9._+-]{1,64}@[a-z0-9-]{1,63}(?:\.[a-z0-9-]{1,63})+", re.IGNORECASE)
def trusted_email_domain(address: str) -> str | None:
if len(address) > MAX_ADDRESS_LENGTH or not PLAIN_ADDRESS.fullmatch(address):
return None
return address.split("@")[1].lower()
How to check
A test sends "a@example.com"@evil.example, =?utf-8?q?a=40example.com?=@evil.example, a@example.com(@evil.example), a%example.com@evil.example and a@evil.example@example.com to sign-up and to invitations. Each is refused, and no account joins the organisation that claims example.com.
Sources
Related rules
- ACC-2 Prove the email address before trusting it in Accounts and identity
- ACC-43 Prove a domain before trusting it in Accounts and identity
- ACC-4 Compare email addresses and usernames in one canonical form in Accounts and identity
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/application-security/authentication-and-sessions/strict-email-parsing">Agent Wranglers application security playbook</a>. In code you write, add a comment next to it with the same link.