How a Security Scan Changed My Approach to write Secure Code

It started like any normal day.

The application was stable. Features were delivered on time. QA had signed off. From the outside, everything looked complete.

Then the security scan report arrived.

Pages of findings.High severity. Medium severity. Low severity.

Nothing was broken. No alerts were firing. No users had reported issues.Yet the report told a different story — not about what had failed, but about what could fail.

That was the moment I realized something important:

Security is not about what breaks today.

It is about what attackers can exploit tomorrow.

When “Working Code” Is Not Secure Code

Like many developers, I once believed that if the application worked and passed tests, it was good enough.

Security analysis challenged that assumption.

It did not follow the happy path.It looked for misuse, abuse, and edge cases:

What happens if input is malicious?

What if a token is leaked?

What if a dependency is compromised?

Suddenly, “working” was no longer the same as “safe.”

1 . The First Wake-Up Call: Cross-Site Scripting (XSS)

One of the first issues was a DOM-based XSS vulnerability.

I remembered the code clearly. It simply rendered user input into the UI. The logic was clean. The feature worked exactly as expected.

But the scan showed the other side:

A crafted payload

A script injected into the DOM

Code execution in another user’s browser

What looked harmless became an entry point for session theft and data exposure.

What I Learned

User input is never trustworthy — even when it looks harmless.

dangerouslySetInnerHTML should only be used with sanitized content

When HTML rendering is necessary, always use a sanitization library like DOMPurify

What Changed

Raw HTML rendering was avoided or Sanitized

Output was escaped by default

Input validation existed on both frontend and backend

The Bottom Line:

React protects you by default for normal text rendering. Use DOMPurify when you need to render HTML or as an extra security layer when sending data to your backend. Remember: validation checks format, sanitization removes threats.

2 . Injection Attacks: A Backend Reality Check

Next came injection vulnerabilities.

The frontend had validations. Inputs were constrained. Errors were handled.

But the attacker never uses the UI.

The scan focused on the backend — where user input meets the database.

The Vulnerable Code (Python):

# User submits username and password

username = request.form.get(“username”)

password = request.form.get(“password”)

# Building SQL query with string formatting – DANGEROUS!

query = f”SELECT * FROM users WHERE username='{username}’ AND password='{password}’”

cursor.execute(query)

What Could Go Wrong:

An attacker enters as username:

‘admin’ OR ‘1’=’1

The resulting query becomes:​

SELECT * FROM users WHERE username=’admin’ OR ‘1’=’1′ AND password=’anything’

Since ‘1’=’1′ is always true, the attacker bypasses authentication entirely and gains access to the admin account.​

The Secure Fix:

# Using parameterized queries – SAFE!

username = request.form.get(“username”)

password = request.form.get(“password”)

# Placeholders (%s) separate code from data

query = “SELECT * FROM users WHERE username=%s AND password=%s”

cursor.execute(query, (username, password))

Why This Works:

Parameterized queries treat user input as data, not executable code. Even if an attacker tries admin’ OR ‘1’=’1, the database searches for a user literally named “admin’ OR ‘1’=’1” instead of executing the malicious logic.

Example with FastAPI:

That was the shift:

Frontend validation improves experience.

Backend validation protects systems.

The Fix

Parameterized queries replaced dynamic ones

ORMs were used properly instead of bypassed

Database access was limited to what was strictly required

Injection attacks stopped being theoretical risks and became concrete threats.

3 . The Risk I Didn’t Write: Dependencies

Some vulnerabilities were not in my code at all.

They lived inside third-party libraries — deep in the dependency tree.

Outdated packages. Unmaintained modules. Known security issues.

This was the hardest lesson:Modern applications inherit risk from their dependencies.

New Habits

Regular dependency audits

Removing unused packages

Treating updates as a security task, not optional maintenance

4 . Security Misconfiguration: The Invisible Weakness

Debug flags in production.Overly permissive CORS rules.Verbose error messages.

None of these caused immediate failures.All of them widened the attack surface.

Configuration became something I reviewed with the same seriousness as code.

What Security Scanning Really Teaches

At first, the scan felt like criticism.

Over time, it felt more like guidance.

Every input field.Every API endpoint.Every dependency.Every configuration.

Each one can either reduce risk — or silently introduce it.

It did not accuse.It exposed assumptions.It highlighted blind spots.

Security scanning forces developers to think like attackers.

That security scan changed how I write code.

Not because it found issues — 

but because it changed how I think.

The post How a Security Scan Changed My Approach to write Secure Code appeared first on Spritle software.