| @@ -9,10 +9,9 @@ | ||
| 9 | 9 | |
| 10 | 10 | class EmailVerificationHandler |
| 11 | 11 | { |
| 12 | 12 | /** |
| 13 | - * How long an issued signup verification code stays valid, in seconds. | |
| 14 | - * Matches the "valid for 10 minutes" copy in the verification email below. | |
| 13 | + * Must match the "valid for 10 minutes" copy in the email below. | |
| 15 | 14 | */ |
| 16 | 15 | const CODE_TTL_SECONDS = 10 * MINUTE_IN_SECONDS; |
| 17 | 16 | |
| 18 | 17 | public static function sendSignupEmailVerificationHtml($formData) |
| @@ -18,21 +17,17 @@ | ||
| 18 | 17 | public static function sendSignupEmailVerificationHtml($formData) |
| 19 | 18 | { |
| 20 | 19 | $email = strtolower(trim($formData['email'])); |
| 21 | 20 | |
| 22 | - // IP bucket is a generous volumetric backstop (shared office/NAT IPs can have | |
| 23 | - // many unrelated signups); the email bucket is the primary throttle, since this | |
| 24 | - // endpoint is otherwise unauthenticated and can be used to mail-bomb any address. | |
| 21 | + // The email bucket is the main throttle against mail-bombing an address. The IP | |
| 22 | + // bucket is looser because offices and NATs share IPs. | |
| 25 | 23 | $ipKey = 'fs_signup_verify_ip_' . wp_hash(Helper::getIp()); |
| 26 | 24 | $emailKey = 'fs_signup_verify_email_' . wp_hash($email); |
| 27 | 25 | |
| 28 | 26 | /* |
| 29 | - * Site-wide ceiling on signup verification mail, as a circuit breaker against a | |
| 30 | - * distributed attacker who rotates both source IP and target address and so never | |
| 31 | - * trips either bucket above. Deliberately generous: this is the one bucket a | |
| 32 | - * legitimate signup rush shares, and tripping it turns signup off for everyone, | |
| 33 | - * so it is sized to be unreachable by organic traffic and filterable for sites | |
| 34 | - * that genuinely run hotter. | |
| 27 | + * Site-wide hourly cap on signup verification mail, for attackers who rotate | |
| 28 | + * both IP and address. Tripping it blocks signup for everyone, so keep it well | |
| 29 | + * above organic traffic. | |
| 35 | 30 | * |
| 36 | 31 | * @since v2.2.2 |
| 37 | 32 | * @param int $limit Signup verification emails allowed site-wide per hour. |
| 38 | 33 | */ |
| @@ -37,13 +32,10 @@ | ||
| 37 | 32 | * @param int $limit Signup verification emails allowed site-wide per hour. |
| 38 | 33 | */ |
| 39 | 34 | $globalLimit = (int) apply_filters('fluent_support/signup_verification_hourly_limit', 100); |
| 40 | 35 | |
| 41 | - // Order matters: PHP short-circuits, so the global counter is only incremented by | |
| 42 | - // requests that already cleared the IP and email gates. Were it checked first, a | |
| 43 | - // flood from a single IP would burn the site-wide budget with requests that are | |
| 44 | - // about to be rejected anyway, handing an attacker a cheap way to trip the breaker | |
| 45 | - // and lock out every legitimate signup. | |
| 36 | + // Check the global bucket last. Short-circuiting means only requests that pass | |
| 37 | + // the IP and email gates count toward it, so one IP can't exhaust it. | |
| 46 | 38 | if (Helper::hitRateLimit($ipKey, 20) |
| 47 | 39 | || Helper::hitRateLimit($emailKey, 5) |
| 48 | 40 | || Helper::hitRateLimit('fs_signup_verify_global', $globalLimit, HOUR_IN_SECONDS) |
| 49 | 41 | ) { |