# Bot protection and hosting setup

## Application protections

- Signup, password login, reset-link requests and password-reset submissions validate Turnstile tokens on the backend before processing. Validation checks success, action and the hostname in `TIMEPROOF_APP_URL`. Cloudflare rejects expired and reused tokens. Missing keys, invalid tokens and provider outages do not bypass verification.
- Turnstile is mandatory when `TIMEPROOF_APP_ENV=production`. The development enable switch cannot turn it off in production. Known Cloudflare dummy keys are rejected in production. Locally it is off unless explicitly enabled.
- Account limits complement existing per-IP limits: login 15 attempts/15 minutes, signup 5/hour per email, reset-link requests 3/hour per email, password reset 10/hour per token. Identity buckets are hashed and normalized, shared across source IPs. Limits expire automatically; they do not permanently lock accounts.
- App/landing page requests share an IP limit of 180/minute; checkout has a 20/hour authenticated-user limit. Existing action limits, email verification and unique challenge entries remain enforced. Rate-limited responses return HTTP 429 with Retry-After.
- These PHP/database limits reduce application abuse but cannot absorb large traffic floods or stop all distributed scraping. Static assets and external payment/outcome callbacks are not challenged by the form widgets.

## Turnstile configuration (required before production login/signup)

1. In Cloudflare, create a **Managed** Turnstile widget for your production hostname. Use the same canonical hostname as `TIMEPROOF_APP_URL`; redirect alternate domains to it. The widget can be used even if the domain's DNS is not hosted by Cloudflare.
2. Put the public site key and private secret into the project's `.env`:

   ```dotenv
   TIMEPROOF_TURNSTILE_SITE_KEY="your-site-key"
   TIMEPROOF_TURNSTILE_SECRET_KEY="your-secret-key"
   TIMEPROOF_TURNSTILE_ENABLED=false
   ```

   The last setting only controls development. Production always requires verification. Never publish the secret key or place it in frontend code.
3. Allow outbound HTTPS from PHP to `challenges.cloudflare.com`. The app's Content Security Policy already permits the widget script and iframe.
4. Run `php tools/production_check.php`. Then check signup, login and both reset forms in a real browser, including an expired challenge and an unavailable widget. Use provider test keys only in development, with `TIMEPROOF_TURNSTILE_ENABLED=true`.

## Cloudflare dashboard / hosting actions (not configured by the application)

1. Proxy the site's DNS records through Cloudflare and use valid origin TLS with **Full (strict)** encryption. Verify the origin certificate first.
2. Enable the available managed WAF rules and bot protection for your plan. Use a Managed Challenge for suspicious browser traffic; do not challenge verified search-engine crawlers indiscriminately.
3. Add edge rate limits for the actual login, register and forgot-password paths under your public URL. Begin in logging mode where supported and tune thresholds using traffic analytics. Add a broader rate limit for bursts to expensive public pages. Available controls and quotas depend on your plan.
4. Exempt signed provider callbacks `actions/payment_webhook.php` and `actions/challenge_outcome.php` from interactive challenges. Their server-side signature/token checks must remain enabled. Do not exempt all `/actions/` endpoints.
5. Have the host restore the original visitor IP into `REMOTE_ADDR` using a trusted-proxy configuration (Apache mod_remoteip or Nginx real_ip). Trust only the current Cloudflare IP ranges, and restrict direct origin access where supported. The application deliberately ignores arbitrary X-Forwarded-For/CF-Connecting-IP headers. Without trusted IP restoration, users behind the proxy may share one rate-limit bucket.
6. Monitor HTTP 429s, failed login logs, provider challenge analytics and origin resource usage. Adjust limits to avoid blocking shared networks and legitimate activity.

The app changes do not activate Cloudflare DNS, WAF, bot protection, DDoS protection or origin firewall rules. Those require access to your domain/hosting account.

References: [server validation](https://developers.cloudflare.com/turnstile/get-started/server-side-validation/), [layered bot protection](https://developers.cloudflare.com/use-cases/solutions/stop-malicious-bots/).

## Verification

`php tests/bot_protection.php`, `php tests/bot_protection.php production`, `node tests/bot_protection.cjs`, and the existing integration suite. Provider responses in automated tests are mocked; tests do not solve challenges or send real authentication requests to Cloudflare.
