Secure Code Review
Development Security
Amartya Jha
• 10 July 2025
Understanding the Real Challenges in Secure Programming Practices
No developer thinks, okaylet’s write insecure code today, yet these vulnerabilities keep happening in production. But why? Let's see them below.
The Human Factor Problem:
Here’s a harsh truth: Hackers often hack into software by taking advantage of the mistakes or gaps in the code that developers like us do not because we are bad, but because we’ve all been there, rushing to meet a deadline, assuming someone else has double-checked the API security.
Most security flaws start with simple assumptions, like:
“This API is for internal use, so we don’t need any authentication here.”
“The frontend already validates this input.”
“This script is just for testing, I’ll remove the secret later.”
These are honest mistakes. But they’re also how real-world breaches happen.
According to a 2024 OpenSSF report, 75% of new developers are unfamiliar with secure coding practices, a gap that leads directly to security flaws in production.
That’s the gap we need to close, and it starts with awareness
Tool Limitations in Practice:
There were several tools like SonarQube, CodeQL, Snyk, and ESLint to spot the errors, and they are incredibly useful, but are they alone enough to handle all cases?
For example:
A linter might flag bad syntax, but not know that an unchecked userId in your SQL string is dangerous.
A broken business logic.
These are the things that can’t be done by a tool. We often need human reasoning, critical thinking, and good practices on secure coding techniques to tackle such issues.
Business Logic Vulnerabilities
Not all vulnerabilities are technical. Some are logical.
For Example:
You create a payment API that checks if the user has items in their cart. But you forgot to verify that the cart was paid for. The user figures out they can skip payment and still get the order confirmation.
It's like saying ”Hey user! You just put the items in the cart and that's it, you own them.”
This is what we call a business logic flaw, a bug in how we designed our application.
The only way to catch these for developers have to think critically about edge cases, abuse paths, and what’s logically allowed.
Building Security into Development from Day One
Do You Know?
CERT-In recently issued a warning about a leak of 16 billion login credentials, including usernames and passwords, from various platforms. This data exposure is attributed to infostealer malware and misconfigured databases.
Why are such incidents happening? Do you know how to ensure secure code?
Most of us think about security only after the code is written or already deployed.
If you want security that works, it needs to be part of the blueprint, not something you add on after launch. This is called “Security by Design”, and here’s what it looks like in a developer’s world.
Shift-left security approach:
The earlier you catch a security issue, the cheaper (and easier) it is to fix. That’s the whole idea behind shift-left security.
Here’s how to do that in practice:
During sprint planning, ask: What could go wrong if this feature is misused?
While designing APIs: Are we checking authentication and validating input?”
What data is being collected, stored, or shown?
Threat modeling:
The term ‘Threat modeling’ sounds complex, but it’s just asking:
“If I were an attacker, how would I break this?”
Here’s a quick 4-question approach:
What are we building?
What can go wrong?
What are we doing to prevent that?
Did we cover all the risky parts?
Even spending 10 minutes asking these questions before you code can prevent major issues later.
Make Security a Team Responsibility:
Securing the application is as important as developing it. So, account for the security of the application you and your team build.
Rotate who reviews for security during PRs.
Add security checks to your CI/CD pipeline.
Celebrate developers who spot potential issues (not just those who ship fast).
Risk-Based Prioritization for Secure Coding Techniques
There’s no shortage of secure coding best practices out there.
You’ve probably seen the OWASP Top 10, dozens of checklist articles, and 100+ page PDFs.
But the truth is, most teams don’t have time to do everything at once, and trying to do it all can lead to doing nothing well.
That’s why we’re taking a pragmatic approach.
Tier 1: Secure Coding Fundamentals (Start Here)
These are the core security habits every team should implement immediately, no excuses, no “we’ll do it later.”
Input Validation and Sanitization
Always validate and sanitize user inputs to prevent injection attacks. For example, never trust data coming from forms, URLs, or APIs. Use strict input rules, regex validation, and allow lists.
JavaScript Code without Validation:
With Validation:
Secrets Management
Hardcoding secrets is like writing your password directly is a very bad practice.
Store secrets in .env files (locally)
Using vault tools in production: AWS Secrets Manager, HashiCorp Vault, etc.
Never commit secrets to Git, use pre-commit hooks and scanners (like gitleaks)
Javascript:
Secure Authentication and Authorization
Make sure to:
Use secure password hashing (bcrypt, Argon2)
Use JSON Web Tokens (JWT) for authentication.
Basic auth mistakes can lead to full account takeovers.
Enforce Multi Factor Authentication (MFA) wherever possible
Set short-lived sessions with HttpOnly, Secure, SameSite=Strict cookies
Implement role-based access control
Python:
JavaScript:
Tier 2: Essential Protections (Implement Next)
Once the basics are in place, these are the next layers to secure. They're not always "visible" until you need them, but when you do, you need them.
SQL injection prevention
Yes, it's 2025, and yes, people still fall victim to SQL injection. Why? Because string concatenation is fast, familiar, and deceptively simple.
Python:
Cross-Site Scripting (XSS) Protection
XSS is tricky because it hides in plain sight; a simple comment box or user bio field is enough. Always encode or escape user-generated content before rendering it in HTML, JavaScript, or attributes.
Use built-in sanitizers like:
DOMPurify (JavaScript)
HtmlSanitizer (C#)
Bleach (Python)
And consider setting Content Security Policy (CSP) headers to reduce damage if something slips through.
CSRF Protection
Cross-Site Request Forgery often goes unnoticed until an attacker manipulates a user into clicking a link, resulting in an unintended password reset.
Modern frameworks usually offer CSRF protection out of the box.
How to prevent it:
Use CSRF tokens (automatically included and verified).
Use SameSite cookies.
Avoid storing auth tokens in localStorage, use HttpOnly cookies.
Dependency Vulnerability Scanning
If you’re building on open source (and who isn’t?), you’re inheriting other people’s bugs.
Use tools that automatically scan your dependencies and alert you about vulnerabilities:
OWASP Dependency-Check
npm audit / pip-audit
Snyk, Dependabot, or GitHub security alerts
And please lock your dependency versions. One surprise update shouldn’t bring in a critical vulnerability.
Tier 3: Advanced Measures (For teams ready to go further)
These are mature security habits that reduce long-term risk
Advanced Cryptographic Practices
If you’re implementing crypto yourself, stop.
Use libraries that:
Implement AES, RSA, and ECC correctly
Handle key storage securely
Offer proper padding and modes (e.g., GCM, not ECB)
Use:
libosodium for C/C++
Crypto in Node.js
pyca/cryptography in Python
Python:
JavaScript:
Logging & Monitoring
As developers, we want to know each step happening in our application to predict what's happening, what went wrong, and why, so maintaining a Logging and Monitoring system is a must to have in an application.
What should you log?
Failed logins
Unexpected input
Errors and exceptions (sanitized!)
Avoid logging sensitive info (passwords, secrets, tokens).
Make sure logs are tamper-proof. Tools like ELK stack, Datadog, or Wazuh help you visualize and alert on suspicious activity.
Essential Secure Coding Best Practices for Common Threats
Do you know what common vulnerabilities to watch out for?
Have you come across terms like Input Validation, SQL Injection?
Curious to know what input validation is and why it is critical?
How can you prevent injection attacks like SQL and XSS?
These are the most common mistakes happening all the time, and the mistakes still cost millions every year. And the worst part? They’re all preventable. Let's look at all those below.
Input Validation and Injection Prevention
Input Validation
Like we discussed before, any input that is coming from outside should be validated and sanitized for better security.
Don’t just check if an input is present. Check:
Type (is it an integer?)
Format (does the email field match regex?)
Length (prevent buffer overflow or memory exhaustion)
Use built-in validation libraries like:
express-validator (Node)
Marshmallow (Python)
FluentValidation (C#)
Best Practices:
Validate on both the client and server sides
Use regex or strong validators
Sanitize before using inputs in queries or rendering
Python:
JavaScript:
SQL Injection, Again (Because It’s Still a Thing)
Already covered it above, but here’s a reminder: if you’re not using parameterized queries, you’re doing it wrong.
NoSQL injection considerations
if someone sends { "username": { "$ne": null } } MongoDB interprets that as:
Cross-Site Scripting (XSS)
XSS happens when you accidentally allow someone to inject and run JavaScript on your site. That means attackers can:
Steal your users' cookies
Change the way your site looks
Or worse, act like the user and perform actions on their behalf
There are a few different types, but here’s a quick summary:
Stored XSS - A Malicious script is stored in the database and shown to other users
Reflected XSS - The Script is reflected in the URL or query parameter and executed immediately
DOM-based XSS - Script is injected via client-side JS without involving the server
More than 50% of web vulnerabilities reported in recent years involve XSS. Even now.
Example:
And user types [user.bio](<http://user.bio>) = “<script>alert(”hacked”)</script>, this script runs when the page loads.
Result: User gets a hacked alert message
How to Prevent this?
JavaScript (React — safe by default):
Use Text Rendering:
Sanitizer:
Sometimes you want users to use safe HTML like <b>, <i>, or links. Then you need a sanitizer to remove scripts, event handlers, and dangerous tags.
Here are the golden rules:
Encode output, not input.
Use textContent instead of innerHTML in JS.
Sanitize user-generated HTML with tools like DOMPurify.
Set a Content Security Policy (CSP).
Context-Aware Output Encoding:
Let’s say you’ve got a user-generated input like a comment, a bio, or a form field. You know it might contain something sketchy like <script>alert('hacked')</script>, so naturally, you want to make sure it can’t break your page or run any scripts.
The first instinct is usually: “I’ll just escape it.”
But here’s the catch: where you’re placing that user input in your HTML matters a lot.
That’s what context-aware output encoding is all about.
What Does “Context-Aware” Mean?
It simply means: the way you encode user input depends on where you're putting it in your HTML.
Different parts of your page have different rules, and blindly escaping everything the same way might not be enough.
Examples:
Inside HTML Elements (Content Context):
You need to make sure that special characters like <, >, and & are turned into their safe versions:
If you're using a framework like React, Vue, or Django templates, they usually escape output by default in content and attribute contexts. But you still need to be careful with raw HTML rendering (dangerouslySetInnerHTML,
etc.).
Content Security Policy (CSP):
A CSP is like a browser-level security guard. It blocks any script that isn’t from a trusted source.
Now, even if someone injects a <script> tag, the browser won’t run it because it's not on the "allowed" list.
Is there any HTML sanitization Library to work with?
JavaScript (Frontend/Node.js)
DOMPurify
Works in the browser and Node.js
Fast, lightweight, and very secure
Maintains formatting (like <b>, <ul>, <a>)
Great for React, Angular, Vue, etc.
Sanitize-html:
Node.js-focused
Lets you configure allowed tags and attributes
Useful for backend rendering or server-side sanitization
Python:
Bleach (by Mozilla) is the go-to sanitizer in the Python world
Safe by default
Easy to allow custom tag sets
Great for Django, Flask, or static site rendering
Authentication and Authorization
Secure password handling
We’ve all seen this (or done it once early in our careers)
Python:
Session Management Best Practices
When someone logs in, you give them a session, basically a way to prove they’re still who they say they are on each request.
But insecure session handling leads to:
Session hijacking (stealing a token/cookie and reusing it)
Session fixation (forcing a session ID on a user)
Tips for secure session handling:
Use HttpOnly cookies (can’t be accessed via JavaScript)
Set Secure flag (only over HTTPS)
Enable SameSite=Strict to prevent CSRF
Rotate session tokens after login and logout
Expire sessions after inactivity or logout
JavaScript:
JWT Security Considerations:
JWTs (JSON Web Tokens) are super popular for auth in modern apps, especially APIs and SPAs.
But they’re also easy to mess up if you’re not careful.
Common JWT mistakes:
Using none as the signing algorithm (yes, really)
Storing sensitive data (like passwords or credit cards) in the token
Not setting token expiry (exp)
Accepting tokens without verifying their signature
JWT best practices:
Always sign tokens using HS256 or RS256
Set short lifetimes (exp), e.g., 15–30 minutes
Store them securely (e.g., HttpOnly cookies if used in browsers)
Don’t trust tokens; blindly verify the signature and claims
Don’t use JWTs if you need easy revocation, use sessions instead
Multi-Factor Authentication (MFA)
You’ve probably used MFA even if you didn’t know the name:
Get a code via SMS or email
Use an app like Google Authenticator or Authy
Tap a security key (like YubiKey)
What is MFA?
MFA = something you know (password) + something you have (phone, device, code)
So even if an attacker steals your password, they still can’t log in without your second factor.
Ways to Implement MFA:
TOTP (Time-based one-time password) - Generates 6-digit codes every 30 seconds
Email/SMS - Send a one-time code to the user
Push Notification - Approve login from a trusted device
Node.js MFA with TOTP (speakeasy)
What is MFA?
Cryptography in Real Life
You don’t need to know the math, but you do need to:
Use TLS (HTTPS) for all traffic.
Store sensitive data encrypted with rotating keys.
Hash only when storing, encrypt when you need to read again.
Avoid reinventing encryption (just… don’t).
Integrating Secure Coding Techniques into Development Workflows
There was a time when teams used to deploy code manually, do a quick last-minute review, and just hope nothing broke in production. But that doesn’t cut it anymore.
Using something like Jenkins without real security checks is kind of like “locking your front door... but leaving all the windows wide open”. You're giving attackers an easy way in.
Today, secure coding must be built into the process, not checked afterward. That’s where tools like SonarQube or CodeQL help. They catch issues as the code is written, not after it’s shipped.
How to Select the Right Tool for Automation?
Not all tools are equal, and you don’t need to choose the "perfect" one, just the one that works with your stack and is easy to maintain.
CodeQL is developer-friendly and integrates well with GitHub Actions.
SonarQube is great for on-prem teams with language variety.
Semgrep is lightweight and super customizable.
CI/CD Pipeline Integration
The real magic happens when SAST is part of your CI/CD pipeline. You want your checks to run automatically:
On every pull request
Before merging with the main
Or as a nightly scheduled scan
That way, you’re not relying on someone to remember to “run the scanner.”
Creating Custom Rules
Most SAST tools let you define custom rules for things unique to your codebase, like:
“This internal API should never be called from outside.”
“This function must always sanitize input first.”
These rules help catch mistakes specific to your app, not just generic bugs.
Golden Tips To Follow:
Automated checks in the CI/CD pipeline reduce human error, save time, and flag hidden risks.
Dependency scanners ensure you're not using outdated or vulnerable libraries.
Security-focused code reviews, backed by checklists and some automation, make sure developers don't miss critical flaws.
Impact
Back in 2017, Equifax, a major credit bureau, experienced a significant data breach impacting 147 million individuals, all due to an unpatched vulnerability in a third-party software library. Their system lacked automated dependency scanning and didn’t flag the issue during deployment.
After the breach, many companies, including Equifax itself, started adopting strict CI/CD practices with tools like Snyk and SonarQube to catch such problems early. This incident became a turning point in how businesses view automated security in development pipelines.
It's a question of time: Ask yourself, What best practices do I follow for secure code review?
Building a Security-Conscious Development Culture
Security isn’t just a tool; it’s a mindset. Many teams still treat it as someone else’s job, leading to gaps and blame games.
But companies must build a culture where every developer feels responsible for secure code.
This starts with regular training, clear documentation, and open sharing of security learnings.
Making security part of the daily workflow, without slowing developers is key.
Programs like Security Champions (where one developer in each team owns security awareness) help bridge gaps.
For older codebases, companies must plan gradual fixes and assess risks realistically, rather than ignoring them.
Security culture isn’t optional anymore; it's the only way to stay ahead.
Example:
Adobe faced challenges retrofitting security into older Flash-era products. They launched a Security Champion program across teams and invested heavily in developer training. Over time, this helped shift their mindset from reactive fixes to proactive secure design. Legacy risks were gradually reduced, and newer products like Adobe Cloud were built with security-first thinking from day one.
Tracking and Improving Your Secure Programming Practices
Tracking and improving your secure programming practices
What to Measure:
How many vulnerabilities are we finding? A steady drop over time shows progress.
How fast are we fixing them? Delays increase risk. Track time from detection to resolution.
Are security checks part of every code review? If not, gaps will keep slipping through.
Is the team getting better at security? Run short assessments or observe through real-world handling of issues.
How to Keep Improving:
Review your security practices regularly, not just after incidents.
Keep up with new attack methods; yesterday’s fix might not work tomorrow.
Reassess your tools often. If they’re noisy or outdated, they’ll be ignored.
Make small, regular process tweaks rather than big overhauls after a breach.
Pay close attention. Learn how recent attacks are happening, and make sure the same tricks can’t be used against your systems.
Example: Netflix is known for using internal dashboards to track how quickly security issues are fixed and how often they recur. Their teams go through regular training refreshers, and tools are updated based on new threats. This helps them stay sharp without slowing down development.
Conclusion and Next Steps
Implementing Secure Coding Best Practices: Your Action Plan
Let’s be honest, secure coding can feel like a lot. But like most things in software, the trick is to take it one step at a time.
Think of security as part of your development craft, not something extra, or someone else’s job. It’s just writing code that protects your users and your app.
Key Takeaways:
Security is a journey, not a one-time task. You don’t "complete" it, you keep improving it as your app and team grow.
Start with the easy wins. High-impact, low-effort changes like input validation, proper password hashing, and dependency scanning go a long way.
It’s not just about code, it’s about culture. When security becomes part of your team’s habits, reviews, and planning sessions, everything else gets easier.
Keep learning. New threats pop up all the time. Stay curious. Share what you learn. Adapt as you go.
Your Next Steps:
Review your current setup, what’s secure, what’s not.
Prioritize Tier 1 best practices and roll them out.
Add static code analysis and dependency scans to CI.
Make security a part of every pull request.
Schedule regular reviews, learn, adapt, and improve.
If you want to know more about secure coding best practices, I highly recommend that you read all the resources provided below and also read about the recent data breaches you will be amazed.