Follow-up from review of #736.
/signup/email and /signup/email/resend are rate limited per address: a
pending signup carries a send count and a last-sent timestamp, and the guards
ride inside the write so two concurrent requests cannot both pass. That bounds
how much mail any one address can be made to receive.
It does not bound one client across many addresses. Someone scripting the
endpoint with a fresh address each time gets a fresh budget each time, and can
push a lot of mail through the sender before anything stops them. The cost lands
on the sending reputation of the domain rather than on any single inbox, so the
per-address limit never sees it.
What this needs is a limit keyed on the caller — per IP, or per IP plus a coarse
window — applied ahead of the per-address one. Worth thinking about together
with the deployment: behind a proxy the client address has to come from a
forwarded header that is only trustworthy if the proxy sets it, so the key
should be configurable rather than assumed.
Out of scope for #736, which is about the verification flow itself.
Follow-up from review of #736.
/signup/emailand/signup/email/resendare rate limited per address: apending signup carries a send count and a last-sent timestamp, and the guards
ride inside the write so two concurrent requests cannot both pass. That bounds
how much mail any one address can be made to receive.
It does not bound one client across many addresses. Someone scripting the
endpoint with a fresh address each time gets a fresh budget each time, and can
push a lot of mail through the sender before anything stops them. The cost lands
on the sending reputation of the domain rather than on any single inbox, so the
per-address limit never sees it.
What this needs is a limit keyed on the caller — per IP, or per IP plus a coarse
window — applied ahead of the per-address one. Worth thinking about together
with the deployment: behind a proxy the client address has to come from a
forwarded header that is only trustworthy if the proxy sets it, so the key
should be configurable rather than assumed.
Out of scope for #736, which is about the verification flow itself.