Every production incident involving “bad data” starts the same way: the application assumed input was safe. It never is. Browser UI constraints can be bypassed, requests can be forged, and payloads can be crafted to trigger SQL injection, XSS, header injection, and log poisoning.
1. Validation vs. Sanitization
Validate when you must enforce a strict shape (IDs, emails, enums). Sanitize when you must normalize “soft” input (whitespace, casing) but still store it. The safest pattern is:
- Validate required fields (presence, type, length, allowed characters)
- Normalize (trim, collapse whitespace)
- Persist using prepared statements
- Escape output at render time
2. Use a single input gateway
Centralize input reads so your app doesn’t scatter raw superglobals everywhere. If a value isn’t explicitly permitted, it doesn’t exist.
3. Prepared statements are mandatory
If user input touches SQL, you use PDO prepared statements with named placeholders. Never concatenate.
4. Escape output, not input
Store the raw, validated value. Escape when rendering HTML using htmlspecialchars(). This prevents double-escaping issues and keeps storage clean.
5. CSRF and intent validation
Any state-changing form must include a CSRF token and the server must verify it. Combine this with a simple math CAPTCHA if you want bot friction without third-party services.
Bottom line: if your code touches $_POST directly in multiple files, you will eventually ship a security bug. Consolidate, validate, and keep the rules strict.