# Yatoon Security Notes

## Credential handling

Yatoon stores third-party credentials in WordPress options because Square, Vagaro, Stripe, Twilio, and Google Calendar require server-side API access. Admin settings mask saved secrets in the user interface and leave saved values unchanged when credential fields are submitted blank.

Sensitive fields include:

- Square access token
- Vagaro API key
- Stripe secret key
- Stripe webhook secret
- Square webhook signature key
- Twilio auth token
- Google client secret
- Google refresh token

As of this version, all of the fields above are encrypted at rest before being written to `wp_options`, via `YATOON_Crypto` (`includes/class-yatoon-crypto.php`), using AES-256-GCM - an authenticated cipher, meaning a tampered or truncated ciphertext is detected and rejected (returns empty) rather than silently decrypting into garbage. Values encrypted by the initial 4.2.1 release used AES-256-CBC without an authentication tag; those are still read correctly for backward compatibility and are transparently upgraded to AES-256-GCM the next time the credential is saved - no manual migration step is required. The encryption key is derived from the site's own `AUTH_KEY`/`SECURE_AUTH_KEY` WordPress salts, so no separate secret needs to be managed. This protects against database-only exposure (a stray SQL export, a misconfigured backup, another plugin reading `wp_options`) - it does not protect against an attacker who also has `wp-config.php`, since that's where the salts live. Values saved before encryption was introduced are plain text and are read back correctly as well (no migration step is required); they're encrypted automatically the next time the credential is saved.

### Rate limiting and object caches

OTP send/verify and other public-endpoint rate limits are implemented with WordPress transients (`set_transient()` / `get_transient()`). On a single-server site with the default database-backed transient storage, this works as expected. If the site uses a persistent external object cache (Redis, Memcached) or runs behind a load-balanced/multi-server setup where the object cache isn't shared and consistent across all app servers, transient-based counters can undercount or reset unexpectedly, weakening the rate limit. If you run Yatoon behind such a setup, confirm your object cache is shared and persistent across all web servers, or consider adding an edge-level rate limit (e.g., at a CDN/WAF) as a second layer for the OTP endpoints.

## Staff access

Staff Portal access is designed for day-to-day technician workflow. Staff PINs are hashed before storage. Owner/admin-only actions should remain limited to trusted users.

Yatoon registers these capabilities for future role delegation:

- `yatoon_manage_settings`
- `yatoon_manage_bookings`
- `yatoon_manage_services`
- `yatoon_manage_staff`
- `yatoon_view_reports`

Administrators receive these capabilities on activation.

## Site hardening recommendations

- Use HTTPS on every booking page.
- Use SMTP for outgoing WordPress email.
- Restrict WordPress administrator accounts to owners or trusted managers.
- Exclude booking, customer portal, staff portal, and manage-booking pages from full-page cache.
- Keep WordPress, PHP, themes, and plugins updated.
- Test Square, Vagaro, Stripe, Twilio, and Google credentials in sandbox/test mode before going live.

