Where Canberra’s Tech, Data Community Collaborates and Grows Together

Secure Coding Practices: Writing Software That Stands the Test of Cyber Threats

Introduction

Software security isn’t just about running security tools after development—it starts with how we write our code. A single oversight, like trusting user input or failing to properly hash passwords, can lead to major security breaches.

This blog dives into practical secure coding techniques that help developers, engineers, and security professionals build resilient software. We’ll look at real-world examples, best practices, and tools you can integrate into your workflow to prevent vulnerabilities before they happen.

Let’s get started!


1. Why Secure Coding Matters More Than Ever

We’ve all seen the headlines—massive data breaches, ransomware attacks, and compromised systems. Many of these incidents trace back to simple coding mistakes that attackers exploit.

Security isn’t something we “bolt on” at the end of development; it has to be part of the entire Software Development Lifecycle (SDLC). Here are a few common mistakes that lead to security flaws:

Trusting user input without validation → Leads to SQL injection & cross-site scripting (XSS).
Hardcoding secrets and API keys in source code → Attackers love scanning GitHub for leaked credentials.
Weak authentication and password storage → Hackers can crack or steal user accounts with ease.
Improper error handling → Exposing system details in error messages gives attackers clues on how to break in.

Fixing these issues after deployment is costly, time-consuming, and painful. Writing secure code from the start saves time and prevents potential disasters.


2. The Core Principles of Secure Coding

Before jumping into specific techniques, let’s lay down a few security-first principles that should guide how we write software:

🔹 Defense in Depth

  • Don’t rely on a single security measure—layer multiple controls (e.g., input validation + WAF + authentication).

🔹 Principle of Least Privilege (PoLP)

  • Give users and applications only the permissions they need—nothing more.

🔹 Fail-Safe Defaults

  • Deny access by default and allow only what is explicitly permitted.

🔹 Avoid Security by Obscurity

  • Just because your system details aren’t “public” doesn’t mean attackers can’t find them.

🔹 Separation of Duties

  • Critical system actions should require multiple levels of approval and authentication.

If we build with these principles in mind, we’re already reducing risk before writing a single line of code.


3. Common Software Vulnerabilities & How to Fix Them

3.1 Unvalidated Input & Injection Attacks (SQLi, XSS, Command Injection)

Let’s start with one of the biggest culprits: unvalidated input.

Vulnerable Example – SQL Injection in Python

pythonCopyEditcursor.execute("SELECT * FROM users WHERE username = '" + user_input + "';")

If an attacker enters ‘ OR 1=1 —, this query will return all users in the database.

Secure Fix – Using Parameterized Queries

pythonCopyEditcursor.execute("SELECT * FROM users WHERE username = %s;", (user_input,))

🔹 Why It Works? It treats user input as data, not executable SQL, preventing SQL injection.

Best Practices for Input Validation:
✔ Always use parameterized queries for database access.
✔ Sanitize input using allow-lists (whitelisting) instead of blacklists.
✔ For web apps, use framework-specific security features (e.g., Django’s ORM handles SQL injection protection automatically).


3.2 Authentication & Password Security

Weak authentication systems make it too easy for attackers to steal accounts.

🔹 Best Practices for Authentication:
✔ Enforce Multi-Factor Authentication (MFA) whenever possible.
✔ Never store passwords in plaintext—always use hashed & salted storage.
✔ Implement secure session management (e.g., cookies should be HttpOnly and Secure).

Secure Password Hashing with Bcrypt in Python

pythonCopyEditimport bcrypt

password = "SecurePass123!"
hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())

print("Hashed password:", hashed_password)

🔹 Why It Works? bcrypt ensures passwords are hashed & salted, making them resistant to brute-force attacks.

What NOT to do:

pythonCopyEdithashed = hashlib.md5(password.encode()).hexdigest()  # ❌ DO NOT use MD5—it’s broken!

3.3 Hardcoded Secrets & API Keys

Developers often accidentally leave API keys in source code. Attackers know this and use tools like TruffleHog to scan GitHub for leaked credentials.

Vulnerable Code – Hardcoded API Key

pythonCopyEditAPI_KEY = "abc123-my-secret-key"

Secure Fix – Use Environment Variables

pythonCopyEditimport os

API_KEY = os.getenv("MY_API_KEY")

🔹 Best Practices for Secrets Management:
✔ Use environment variables or a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault).
✔ Set up automated scans for exposed secrets (git-secrets, TruffleHog).
✔ Rotate API keys regularly.


4. Secure Deployment & Monitoring

Security doesn’t stop at coding—we need to secure deployment and continuously monitor applications.

🔹 Best Practices for Deployment Security:
Use Infrastructure as Code (IaC) (e.g., Terraform) to enforce security policies.
✔ Ensure least privilege access for cloud services (e.g., AWS IAM roles).
✔ Automate security scans in CI/CD pipelines.

Example: Automating Security Scanning in GitHub Actions

yamlCopyEditname: Security Checks

on: [push, pull_request]

jobs:
  security_scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run OWASP Dependency-Check
        run: dependency-check.sh --scan .
      - name: Run TruffleHog for Secrets Detection
        run: trufflehog --regex --entropy=True .

🔹 Why It Works? Every time code is pushed, secrets & dependency vulnerabilities are automatically checked.


5. Tools for Secure Software Development

StageToolPurpose
Static Analysis (SAST)SonarQube, CheckmarxIdentify vulnerabilities in source code
Dependency ScanningSnyk, OWASP Dependency-CheckDetect known vulnerabilities in dependencies
Dynamic Testing (DAST)OWASP ZAP, Burp SuiteTest running applications for vulnerabilities
Secrets DetectionTruffleHog, Git-SecretsPrevent hardcoded credentials
Container SecurityTrivy, Docker BenchSecure containerized environments
MonitoringPrometheus, SplunkDetect real-time threats

Final Thoughts: Secure Code is Good Code

Writing secure code isn’t just about compliance—it’s about building software that users and businesses can trust.

Shift security left—catch vulnerabilities early in development.
Automate security checks in your CI/CD pipeline.
Educate developers—security is everyone’s responsibility.

What security practices do you use in your projects? Let’s discuss in the comments! 🚀


Further Reading & References

Leave a comment