Introduction
Secure coding is the practice of writing software that resists misuse, not just software that works for legitimate users. Most real-world breaches trace back to a small set of recurring mistakes: trusting data that came from outside the application, failing to validate it, and giving code more privilege than it needs. Learning to recognize and avoid these patterns is one of the highest-leverage skills a developer can build.
Cricket analogy: Secure coding is like a fielding coach drilling players to instinctively cover their zone against any shot, not just the ones they expect; most run-outs, like most breaches, trace back to a small set of recurring positioning mistakes.
Explanation
Input validation means checking that data received by the application matches the format, type, length, and range you expect before you use it — reject anything that does not conform, ideally using an allowlist of acceptable values rather than trying to block known-bad patterns. Output encoding means transforming data before it is written into a new context (HTML, SQL, a shell command, a URL) so that it cannot be misinterpreted as code in that context; this is what prevents stored data from turning into a cross-site scripting payload when rendered in a browser. The principle of least privilege means every account, service, process, and database connection should hold only the permissions it needs to perform its job, nothing more — a web app's database user should not be able to drop tables if it only ever needs to read and write rows. Finally, never trust client-side data: anything that arrives from a browser, mobile app, or API caller — form fields, cookies, headers, hidden form values, even client-side validation results — must be re-validated and re-authorized on the server, because a client is fully under the attacker's control.
Cricket analogy: Input validation is like an umpire only accepting a review request in the correct format within the time limit; output encoding is like ensuring the scoreboard displays a player's name safely without letting special characters break the display; least privilege means a substitute fielder can't access team strategy documents; and never trusting client data means the ground staff re-measures the pitch themselves rather than trusting a visiting team's report.
Example
# BEFORE: trusts client input directly, no validation, string-built SQL
def get_user_orders(request):
user_id = request.args["user_id"] # attacker-controlled
query = f"SELECT * FROM orders WHERE user_id = {user_id}"
return db.execute(query)
# AFTER: validate type/ownership, use parameterized query, enforce authz
def get_user_orders_secure(request, current_user):
raw_id = request.args.get("user_id", "")
if not raw_id.isdigit():
raise ValueError("Invalid user_id")
user_id = int(raw_id)
# Never trust that the caller is allowed to see this user's data
if user_id != current_user.id and not current_user.is_admin:
raise PermissionError("Not authorized")
query = "SELECT * FROM orders WHERE user_id = %s"
return db.execute(query, (user_id,)) # parameterized, no injection riskAnalysis
The 'before' example fails in two independent ways: it builds SQL by string concatenation, opening the door to SQL injection, and it never checks whether the requesting user is actually allowed to see the target user's orders, opening the door to broken access control. The 'after' example fixes both with cheap, local changes — type-checking the input, using a parameterized query so the database driver handles escaping, and adding an explicit authorization check. None of these fixes require a security team; they are ordinary engineering discipline applied consistently, which is why secure coding is best taught as a habit rather than a one-time audit.
Cricket analogy: A team allowing any substitute to walk onto the field unchecked while also letting anyone rewrite the official scorecard is a double failure, fixed cheaply by checking substitute credentials and locking the scorebook, ordinary discipline rather than a one-time audit.
Key Takeaways
- Validate all input against an allowlist of expected format, type, length, and range.
- Encode output for the context it will be rendered in (HTML, SQL, shell, URL) to prevent injection.
- Apply least privilege to accounts, services, and database connections.
- Never trust client-side data or client-side validation — re-check everything on the server.
Practice what you learned
1. Why is an allowlist generally preferred over a blocklist for input validation?
2. What does output encoding primarily protect against?
3. A web app's database account can DROP TABLE, even though the app only ever needs SELECT and INSERT. What principle is being violated?
4. Why must server-side code re-validate data even if the client already validated it with JavaScript?
Was this page helpful?
You May Also Like
SQL Injection
How unsanitized input can hijack a database query, and why parameterized queries are the essential defense.
Cross-Site Scripting (XSS)
Stored, reflected, and DOM-based XSS explained, with output encoding and CSP as the core defenses.
OWASP Top 10 Overview
A practical tour of the OWASP Top 10, the industry-standard list of the most critical web application security risks.
Access Control Models
Compare DAC, MAC, and RBAC access control models and understand who decides permissions in each approach.