Every architecture diagram and threat model eventually resolves into actual lines of code, and it is at that moment that most exploitable vulnerabilities are born. Secure coding practices are the discipline of writing code that behaves correctly even when handed hostile input, across input handling, error paths, session state, cryptography, access enforcement, and concurrency.
Input Validation and Output Encoding
Validate input at every trust boundary using allowlisting -- define exactly what a valid value looks like and reject anything else -- rather than denylisting, which tries to enumerate malicious patterns and always misses a new variant. Canonicalize data (decode URL-encoding, resolve path traversal, normalize Unicode) before validating it, or an attacker can smuggle a payload past the filter in an encoded form that only becomes dangerous after later decoding. On output, encoding must be context-aware: HTML, SQL, an OS shell command, and LDAP each treat different characters as special, and using the wrong scheme for the destination is itself a vulnerability.
Error Handling and Session Management
Fail closed: default to denying access rather than falling back to a permissive state. Show users a generic error while detailed diagnostics -- stack traces, paths, schema hints -- go only to internal logs, since verbose errors are reconnaissance for attackers. Sessions need long, random, cryptographically generated identifiers; HttpOnly, Secure, and SameSite cookie attributes; idle timeouts; and regeneration of the session ID at login or privilege change, which defeats session fixation.
Cryptography and Access Control in Code
Never invent custom cryptographic algorithms -- use vetted libraries and current standard algorithms, and never hardcode secrets in source code. Enforce access control on the server for every request; a hidden UI button is a usability nicety, not a boundary, since an attacker can call the API directly. Default to deny: access is granted only when a rule explicitly permits it.
Concurrency and Resource Management
Time-of-check-to-time-of-use (TOCTOU) race conditions occur when a security check and the actual resource use are separated by an exploitable window; proper locking and minimizing that window close the gap. Bound loops, close handles, and rate-limit endpoints so resource exhaustion cannot be used for denial-of-service.
Key Mechanics
- Validate against an allowlist at every trust boundary; canonicalize before validating, never after.
- Encode output for the specific sink context (HTML, SQL, shell, LDAP) -- one scheme does not fit all.
- Enforce access control server-side on every request; treat client-side controls as UX only.
- Use parameterized queries for any interpreter call, instead of string concatenation.
- Regenerate session IDs on login and privilege change; set HttpOnly/Secure/SameSite on cookies.
Exam Tip: Validation only in browser JavaScript means server-side re-validation is missing -- client-side checks are convenience, never a control.
Exam Tip: Allowlist (permit known-good) beats denylist (block known-bad) because it fails closed against novel variants a denylist has never seen.
Exam Tip: TOCTOU questions describe a gap between a permission check and the actual resource action -- that gap is the vulnerability.
Diagram
Worked example: A developer builds a search feature that inserts a username parameter directly into a SQL WHERE clause, relying only on a client-side JavaScript regex for validation. A tester bypasses the browser and posts a crafted request directly to the API, injecting a UNION-based SQL payload. The fix needs two layers: server-side allowlist validation, and a parameterized query so input is always data, never executable SQL.