A person sitting at a laptop presses a glowing digital security icon, symbolizing solid code security.A person sitting at a laptop presses a glowing digital security icon, symbolizing solid code security.

Code Security 101: Protecting Yourself in a Digital World

March 10, 2025
Cybersecurity 101

Code is at the heart of today’s digital world, and as such is a prime target for hackers. With so many attacks exploiting so many vulnerabilities in software, code security is essential for building strong and resilient systems.

In this 101-level piece, we’ll dive into what code security really means, why it’s so important, and walk through some real-world examples of both secure and vulnerable code.

What is Code Security?

Code security is the practice of writing, testing, and maintaining code in a way that protects it from attacks or exploitation. This happens by identifying and addressing vulnerabilities in code to prevent attackers from exploiting weaknesses to compromise systems, steal data, or disrupt services.

Why is Code Security Important?

Vulnerabilities in code are one of the biggest entry points for cyber criminals. Like we've seen time and time again, a single vulnerability can expose sensitive data or even bring down an entire system. Preventing breaches starts with secure coding from the ground up.

Then there’s reputation - once a company suffers a breach, customer trust takes a serious hit. Restoring that trust isn’t easy, and the financial and legal fallout can be just as damaging.

From a cost perspective, fixing security issues while developers are drafting code is far more cost-effective than scrambling to patch a system after deployment (or worse, after an attack). The longer a vulnerability goes unnoticed, the more expensive it becomes to fix.

Another reason code security is important is for compliance. For many industries, compliance isn’t optional; Finance, healthcare, and tech companies must follow strict regulations like SOC 2, GDPR, and HIPAA. Secure coding ensures these requirements are met without last-minute panic.

Finally, scalability depends on security. If an application is built on shaky foundations, expanding it only increases the risk. A secure-by-design approach ensures that growth doesn’t come at the cost of security.

In short, investing in secure coding isn’t just about avoiding threats—it’s about building a resilient, scalable, and trustworthy business.

Examples of Secure and Insecure Code

1. Input Validation and Injection Attacks

An example of insecure code in python:

In this example, the application is vulnerable to SQL injection because an attacker could input malicious SQL, such ' OR '1'='1' --, which always returns true and retrieves all users.

import sqlite3

def get_user_info(username):
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()

    query = f"SELECT * FROM users WHERE username = '{username}'"  # 🚨 INSECURE
    cursor.execute(query)  # Attacker can input: ' OR '1'='1' --
    result = cursor.fetchall()

    conn.close()
    return result

Securing this code

Using parameterized queries ensures user input is treated as data, not code, so the database never executes user input directly.

import sqlite3

def get_user_info_secure(username):
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()

    query = "SELECT * FROM users WHERE username = ?"
    cursor.execute(query, (username,))  # ✅ SAFE: Prevents SQL Injection
    result = cursor.fetchall()

    conn.close()
    return result

2. Hard-coded Secrets

Insecure Code

In this example, credentials are stored in the source code, making them easy to steal.

import pymysql

# 🚨 INSECURE: Hardcoded credentials
DB_HOST = "localhost"
DB_USER = "admin"
DB_PASSWORD = "supersecret123"
DB_NAME = "sensitive_db"

def connect_db():
    conn = pymysql.connect(
        host=DB_HOST,
        user=DB_USER,
        password=DB_PASSWORD,
        database=DB_NAME
    )
    return conn

def get_users():
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users")
    result = cursor.fetchall()
    conn.close()
    return result

Secure Code

In the secure code, credentials are stored with environment variables to keep secrets out of the codebase, reducing the risk of exposure.

import os
import pymysql
from dotenv import load_dotenv

# Load environment variables from .env file (if used)
load_dotenv()

def connect_db():
    conn = pymysql.connect(
        host=os.getenv("DB_HOST"),
        user=os.getenv("DB_USER"),
        password=os.getenv("DB_PASSWORD"),
        database=os.getenv("DB_NAME")
    )
    return conn

def get_users():
    conn = connect_db()
    cursor = conn.cursor()
    cursor.execute("SELECT * FROM users")
    result = cursor.fetchall()
    conn.close()
    return result

3. Access Control (Principle of Least Privilege)

Insecure code

The example below shows a function running with a role check that doesn't verify whether or not the user is an admin, which, in this case, gives all users the ability to delete accounts.

def delete_user(user_id, current_user_role):
    """Deletes a user account"""
    if current_user_role:  # 🚨 No role check! Any user can delete accounts.
        print(f"User {user_id} deleted.")
    else:
        print("Access denied.")

# Test cases
delete_user(5, "regular_user")  # 🚨 A normal user can delete accounts!
delete_user(3, "admin")         # ✅ Admin should have access

Secure Code

This secure example solves that issue by verifying that a user has admin permissions before granting the user that privilege.

def delete_user(user_id, current_user_role):
    """Securely deletes a user account"""
    if current_user_role != "admin":  # ✅ Only admins can delete users
        print("Access denied: Insufficient privileges.")
        return

    print(f"User {user_id} deleted.")

# Test cases
delete_user(5, "regular_user")  # ❌ Access denied
delete_user(3, "admin")         # ✅ Admin can delete accounts

4. Error Handling

Insecure code

The error messages in this code example give too much information to attackers, who can use them to find valid usernames, guess passwords, and identify errors.

def login(username, password):
    try:
        # Simulate database connection
        if username != "admin":
            raise ValueError("User not found in database")  # 🚨 Reveals too much info

        if password != "securepassword":
            raise ValueError("Incorrect password")  # 🚨 Helps attackers guess passwords

        return "Login successful!"

    except Exception as e:
        return f"Error: {e}"  # 🚨 Exposes internal error details

# Test the function
print(login("testuser", "password"))

Secure Code

These generic messages do not expose system details, so attackers can't see whether the username or password was wrong or what type of error is returned.

import logging

# Configure logging
logging.basicConfig(filename="errors.log", level=logging.ERROR)

def login(username, password):
    try:
        # Simulate database connection
        if username != "admin" or password != "securepassword":
            return "Invalid login credentials."  # ✅ Generic message

        return "Login successful!"

    except Exception as e:
        logging.error(f"Unexpected error: {e}")  # ✅ Logs detailed error securely
        return "Something went wrong. Please try again later."  # ✅ Generic response

# Test the function
print(login("testuser", "password"))

Best Practices for Writing Secure Code

We’ve written the definitive guide for secure coding practices that you can read here, but for now:

  1. Validate All Inputs: Always sanitize and validate input data to prevent attacks like SQL injection and cross-site scripting (XSS).
  2. Use Parameterized Queries: Never concatenate user input directly into SQL queries or other commands.
  3. Store Secrets Securely: Use secure vaults or environment variables for managing API keys and sensitive credentials
  4. Secure Data Storage and Transmission: Encryption transforms sensitive data into an unreadable format to protect it from unauthorized access
  5. Implement Least Privilege: Grant minimal permissions to users, applications, and services.
  6. Secure and Update Dependencies: Regularly update and patch third-party libraries to eliminate known vulnerabilities.
  7. Adopt Static and Dynamic Analysis: Use tools to scan code for vulnerabilities during development and at runtime.
  8. Enforce Strong Authentication and Authorization: Implementing Role-Based Access Control (RBAC) and Multi-Factor Authentication (MFA) correctly is critical
  9. Use Secure Coding Frameworks: OWASP, CERT, or NIST
  10. Enable Logging and Monitoring: Monitor applications for unusual behavior to catch and mitigate threats early.
  11. Educate developers: Security is not a one-time effort. Developers must stay informed about emerging threats and secure coding techniques

Conclusion

Code security is not just a technical requirement; it’s a fundamental practice to ensure trust, resilience, and reliability in modern software. By prioritizing secure coding practices, developers safeguard not just their applications but also the users and businesses that rely on them daily.

Whether you’re a seasoned developer or just starting your journey, embracing secure coding is a skill that will pay dividends in both security and professional growth. To find out how Symbiotic Security's AI can help, check out our solutions page here.

About the author
Vincent Cannone
Growth Marketing Manager - Symbiotic
Icon line
See all articles

Book a demo

See how our solution empowers teams to grow their security maturity and to code securely & efficiently.
Icon line
Book a demo
Demo illustration