Cyber Security Secure Coding Basics
Secure coding is the practice of writing software so that common attack techniques fail by default, built around three habits: validating every input, trusting nothing that comes from the client, and giving code only the permissions it truly needs.
What Secure Coding Really Means
Most breaches don't start with an exotic zero-day — they start with an ordinary bug that happens to have security consequences. A form field that accepts anything, a database query built by joining strings together, an API that assumes the caller is honest. Secure coding means treating security as a property of the code itself, decided at the moment you write a function, rather than something a firewall or a security team bolts on afterward.
Rule One: Never Trust Client-Side Data
Anything running in a browser, mobile app, or desktop client is under the user's control. JavaScript validation, disabled buttons, hidden form fields, and client-side price calculations all exist purely to make the experience smoother — an attacker can rewrite the JavaScript, replay a captured request with a tool like a proxy or curl, or edit the app's binary. If a check only happens on the client, it doesn't really exist from a security point of view. Every decision that matters — who can see this record, how much does this order cost, is this user an admin — must be re-checked on the server using data the server itself controls or has independently verified.
Example: A Price Comes From the Client
// VULNERABLE: server trusts a price sent by the browser
app.post("/checkout", (req, res) => {
const { itemId, price, quantity } = req.body;
const total = price * quantity; // attacker can send any "price"
createOrder(itemId, total);
res.json({ total });
});
// FIXED: server looks the price up itself
app.post("/checkout", (req, res) => {
const { itemId, quantity } = req.body;
if (!Number.isInteger(quantity) || quantity < 1 || quantity > 100) {
return res.status(400).json({ error: "invalid quantity" });
}
const item = getItemFromCatalog(itemId); // trusted source, e.g. the database
if (!item) return res.status(404).json({ error: "unknown item" });
const total = item.price * quantity;
createOrder(itemId, total);
res.json({ total });
});Input Validation Strategies
- Allowlist, don't denylist — define the small set of characters, formats, or values you accept (e.g. digits only for a phone field) instead of trying to block every bad pattern you can think of.
- Validate type, length, range, and format before the data touches any business logic (a quantity must be a positive integer, an email must match a basic pattern, a file upload must match an expected extension and content type).
- Canonicalize before you validate — normalize encoding, case, and path separators so an attacker can't sneak a malicious value past a filter using an alternate representation.
- Use parameterized queries or an ORM's query builder for anything touching a database — never concatenate user input into SQL, shell commands, or file paths.
- Encode output for the context it lands in (HTML-escape text rendered into a page, escape values placed into a URL, a shell argument, or a log line) so validated input can't turn into an injection somewhere downstream.
The Principle of Least Privilege
Least privilege means every user, service account, API key, and process should be able to do exactly what it needs to do, and nothing more. A web application's database account should be able to read and write the specific tables it needs — it has no business being able to drop tables or read other applications' schemas. A microservice calling a cloud API should hold a scoped credential for that one action, not an administrator key that happens to also work. The benefit shows up after something goes wrong: if a component is compromised, tight scoping limits how far the attacker can move (often called reducing the 'blast radius').
- Threat-model the feature before writing code: what could a malicious user try to do here?
- Validate and type-check every input on the server, regardless of what the client already checked.
- Apply least privilege to every account, key, and process the feature touches.
- Encode or sanitize output for its destination (HTML, SQL, shell, logs).
- Fail closed and handle errors without leaking stack traces or internal details.
- Log security-relevant events without logging secrets or sensitive personal data.
- Keep dependencies patched and review new libraries before adding them.
- Get a second set of eyes through code review, focused specifically on security.