Secure Coding Practices

Secure coding practices reduce vulnerabilities at the source by ensuring that code is written safely from the first line. Secure development shifts security to the earliest stages of coding so that insecure patterns never reach production. Secure coding eliminates common attack vectors such as injection flaws, insecure authentication, authorization gaps, bad error handling, weak cryptography, and unsafe data processing.

Why Secure Coding Matters

Most vulnerabilities originate in the code itself. Insecure functions, poor validation, unsafe queries, or hardcoded secrets introduce exploitable weaknesses. Secure coding prevents these issues before they reach CI/CD, before scans detect them, and before attackers exploit them. Writing secure code reduces remediation costs and strengthens overall architecture.

Secure coding is a mindset and a discipline applied to every function, module, and logic block.

Core Secure Coding Principles

Validate All Input

Never trust user input. Validate for type, length, format, range, and constraints. Reject invalid input early before it interacts with logic, files, or queries.

Encode All Output

Output encoding prevents injection attacks by ensuring data is treated as data, not executable code.

Principle of Least Privilege

Functions, modules, and services must only have access to what they need.

Fail Safely

On error, systems must default to secure behavior (deny access, close connection, reject request).

Remove Insecure Functions

Functions such as eval, insecure deserialization, weak random generators, and outdated cryptographic methods must be avoided entirely.

Secure Error Handling

Do not reveal stack traces, query strings, internal logic, or sensitive metadata.

Secrets Must Never Be Hardcoded

API keys, tokens, passwords, encryption keys, and certificates must not appear in the code.

Secure Logging

Log events that help in security analysis but never log sensitive information such as passwords, tokens, or personal data.

Use Safe Libraries and Frameworks

Select actively maintained libraries with strong security track records.

Keep Code Simple

Complex code introduces hidden vulnerabilities. Simplicity reduces attack surface and improves maintainability.

Secure Input Validation

What to Validate

• Data format
• Allowed characters
• Data length
• Data type
• Boundary ranges
• Whitelist allowed input

Safe Validation Example (Python)

import re

if not re.fullmatch(r"[a-zA-Z0-9_]{3,20}", username):
    raise ValueError("Invalid username")

Input validation must occur server-side, not just client-side.

Preventing Injection Attacks

Injection occurs when untrusted input reaches commands, queries, or interpreters.

SQL Injection Prevention

Use parameterized queries:

cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))

Never build SQL dynamically using string concatenation.

Command Injection Prevention

Use safe subprocess calls:

subprocess.run(["ls", "-la"])

Avoid:

os.system("ls -la " + user_input)

No eval or unsafe parsing

Avoid all dynamic code execution.

# unsafe
data = eval(user_input)

Authentication and Password Security

Password Handling Rules

• Hash passwords using modern algorithms
• Never manually implement hashing
• Use salt automatically generated
• Enforce strong password rules

Use libraries such as:

bcrypt
argon2
scrypt

Session Security

• Use secure cookies
• Enforce expiration
• Invalidate sessions on logout
• Use HTTPS only cookies
• Regenerate tokens frequently

Authorization Rules

Authorization ensures users can perform only allowed actions.

Secure Authorization Patterns

• Role-based access control
• Fine-grained permissions
• Check authorization on every request
• Never rely solely on client-side checks
• Avoid insecure direct object references

Example of secure access:

if request.user.id != resource.owner_id and not request.user.is_admin:
    raise PermissionError("Unauthorized")

Safe Cryptography

Rules

• Use established libraries
• Never create custom encryption
• Use AES-256 for symmetric encryption
• Use RSA or ECC for asymmetric encryption
• Use TLS 1.2 or higher
• Randomness via secure RNG only

Safe Python example:

from secrets import token_hex
api_key = token_hex(32)

Avoid:

random.random()

Secure Error Handling

Errors must not reveal internal workings.

Unsafe

print(exception)

Safe

log.error("Operation failed")
return {"error": "Unexpected error occurred"}

Avoid leaking stack traces to users.

Logging & Monitoring

Log Security Events

• Login failures
• Permission errors
• File access attempts
• Unexpected input patterns

Do Not Log

• Passwords
• Tokens
• Credit card data
• Personal information

Secure logging example:

log.info("Failed login attempt", extra={"user": user_id})

Dependency and Supply Chain Security

Rules

• Use minimal dependencies
• Pin versions
• Scan dependencies on install
• Avoid unmaintained libraries
• Validate checksums
• Avoid libraries with poor supply chain reputation

Run scanning tools:

npm audit
pip audit
osv-scanner .

Fix dependency issues early.


Extensive Practicals

These practicals enforce secure coding at every stage.


Practical 1: Create Secure Coding Guidelines for Your Team

Add a document:

/secure-dev/secure-coding-guidelines.md

Include rules for:

• Input validation
• Output encoding
• Error handling
• Authentication
• Authorization
• Cryptography
• Logging
• Dependency hygiene
• Secrets management

This becomes the reference for all developers.


Practical 2: Validate Input in Real Code

Take any function that accepts user input.

Original unsafe code:

def search(q):
    return db.query(f"SELECT * FROM items WHERE name LIKE '%{q}%'")

Rewrite into safe parameterized version:

def search(q):
    q = q.strip()
    cursor.execute("SELECT * FROM items WHERE name LIKE %s", (f"%{q}%",))

Test with:

'
"; DROP TABLE items; --
%_test

Ensure no injection occurs.


Practical 3: Create a Secure Authentication Flow

Implement:

  1. Password hashing with argon2

  2. Rate limiting login attempts

  3. Password reset with signed tokens

  4. HTTPS-only session cookies

Example hashing:

from argon2 import PasswordHasher

ph = PasswordHasher()
hashed = ph.hash(password)
ph.verify(hashed, login_password)

Practical 4: Fix Insecure Direct Object Reference

Original insecure endpoint:

GET /download?file=/etc/passwd

Secure version:

allowed = ["report1.pdf", "report2.pdf"]
if filename not in allowed:
    raise PermissionError("Unauthorized file access")

Test with malicious payloads:

../../../app_secret.env
/etc/hosts
../

Practical 5: Replace Unsafe Cryptography

Unsafe:

encrypted = base64.b64encode(data)

Secure:

from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher = Fernet(key)
encrypted = cipher.encrypt(data)

Test encrypt/decrypt cycle.


Practical 6: Implement Secure Logging

Original:

log.info(f"User logged in with password {password}")

Secure:

log.info("User logged in", extra={"user": user_id})

Test logs to verify sensitive data never appears.


Practical 7: Add Static Code Analysis to Detect Insecure Code

Run:

bandit -r src/

Fix findings such as:

• Use of eval
• Hardcoded passwords
• Insecure temp files
• Weak hashing
• Unsafe subprocess commands


Practical 8: Add Secrets Detection to Your Repository

Run:

gitleaks detect

Test by adding a mock API key:

API_KEY = "123456789SECRET"

Verify that pipeline blocks the commit.


Practical 9: Solve Injection Vulnerabilities in a Sample Project

Take a vulnerable endpoint:

os.system("ping " + ip)

Rewrite:

subprocess.run(["ping", "-c", "4", ip])

Test with payloads:

127.0.0.1; ls
127.0.0.1 && whoami
127.0.0.1 | cat /etc/passwd

Verify they fail.


Practical 10: Implement Authorization Middleware

Write middleware:

def authorize(required_role):
    def wrapper(func):
        def inner(request, *args, **kwargs):
            if request.user.role != required_role:
                raise PermissionError("Forbidden")
            return func(request, *args, **kwargs)
        return inner
    return wrapper

Test:

• Unauthorized user call
• Admin user call

Ensure correct behavior.


Practical 11: Fix Error Handling

Replace:

return str(e)

With:

log.error("Unexpected error", exc_info=True)
return {"error": "Request failed"}

Test by causing intentional errors.


Practical 12: Dependency Hardening

Run:

pip install safety
safety check

Fix:

• Vulnerable packages
• Outdated components
• Unsafe versions

Create file:

requirements-secure.txt

Pin secure versions.


Practical 13: Build a Local Secure Coding Testing Suite

Create:

/secure-dev/tests/

Write tests for:

• Input validation
• Authentication
• Authorization
• Error handling
• Injection prevention
• Logging correctness

Run tests for every commit.


Practical 14: Code Review Security Checklist

Add file:

/secure-dev/review-checklist.md

Checklist includes:

• Input validated?
• Output encoded?
• No secrets?
• No injection?
• Errors safe?
• Logging sanitized?
• Dependencies safe?
• Privileges minimal?
• Encryption strong?

Reviewers use this for every pull request.


Intel Dump

• Secure coding prevents vulnerabilities at the source
• Key principles include validation, encoding, least privilege, error safety, no hardcoded secrets, safe cryptography, and secure logging
• Developers must avoid insecure functions and weak logic patterns
• Dependencies require strict scanning and hygiene
• Extensive practicals include input validation, injection prevention, secure authentication, safe cryptography, logging hygiene, SAST, secrets detection, authorization middleware, error handling, and secure code review templates

HOME COMMUNITY CAREERS DASHBOARD