Security

SQL Injection in Modern Frameworks: What Code Reviewers Still Miss

Tony Dong
June 18, 2025
11 min read
Share:
Featured image for: SQL Injection in Modern Frameworks: What Code Reviewers Still Miss

SQL injection remains one of the most dangerous web vulnerabilities, consistently ranking in the OWASP Top 10. While modern frameworks promise protection through ORMs and prepared statements, subtle injection patterns continue to slip through code reviews. This guide reveals the advanced SQL injection techniques that reviewers often miss in Rails, Django, Spring Boot, and other popular frameworks.

Key Takeaways

  • ORMs aren't bulletproof: Framework-specific injection patterns exist in ActiveRecord, Django ORM, and JPA that bypass standard protections.
  • Focus on dynamic query construction: Look for user input in ORDER BY, WHERE conditions, and raw SQL fragments that escape parameterization.
  • Implement defense in depth: Combine parameterized queries, input validation, least privilege database access, and automated security testing.

Why SQL Injection Still Happens in Modern Frameworks

Modern web frameworks have significantly reduced SQL injection vulnerabilities through Object-Relational Mapping (ORM) layers and prepared statements. However, developers often need to drop down to raw SQL for performance optimization, complex queries, or features not supported by the ORM. This is where vulnerabilities creep in.

⚠️ Common Misconception

"We use an ORM, so we're safe from SQL injection." This false sense of security leads to inadequate code review practices when dealing with dynamic queries and raw SQL.

The Attack Surface in Modern Applications

🎯 High-Risk Areas

  • • Dynamic ORDER BY clauses
  • • Raw SQL fragments in ORM queries
  • • Search functionality with LIKE operators
  • • Report generation with user filters
  • • Database view and stored procedure calls
  • • Bulk data operations

🔍 Common Code Review Blind Spots

  • • String concatenation in query builders
  • • User input in column/table names
  • • JSON/NoSQL query injection
  • • Second-order injections
  • • Time-based blind injections
  • • Error-based information disclosure

Framework-Specific Injection Patterns

Each framework has its own quirks and dangerous patterns. Understanding these framework-specific vulnerabilities is crucial for effective code review.

Rails (ActiveRecord) Vulnerabilities

Ruby on Rails' ActiveRecord provides excellent protection when used correctly, but several methods can introduce vulnerabilities when user input is not properly sanitized.

🚫 Dangerous Pattern: Dynamic ORDER BY

# VULNERABLE - Direct user input in order clause
User.order(params[:sort_by])

# VULNERABLE - String interpolation in where clause
User.where("name = '#{params[:name]}'")

# VULNERABLE - SQL fragment injection
User.where("age > ? AND #{params[:condition]}", 18)

✅ Secure Alternatives

# SAFE - Whitelist approach for sorting
allowed_sorts = %w[name email created_at]
sort_column = allowed_sorts.include?(params[:sort_by]) ? params[:sort_by] : 'id'
User.order(sort_column)

# SAFE - Parameterized queries
User.where("name = ?", params[:name])

# SAFE - ActiveRecord methods with hash syntax
User.where(name: params[:name], age: params[:age])

Django ORM Security Issues

Django's ORM is generally secure, but certain methods and practices can introduce vulnerabilities, especially when building dynamic queries.

🚫 Django Vulnerable Patterns

# VULNERABLE - Raw SQL with string formatting
User.objects.raw("SELECT * FROM users WHERE name = '%s'" % user_input)

# VULNERABLE - Extra() method with user input
User.objects.extra(where=["name = '%s'" % user_input])

# VULNERABLE - Dynamic field lookups
filter_dict = {request.GET['field']: request.GET['value']}
User.objects.filter(**filter_dict)

✅ Django Secure Patterns

# SAFE - Parameterized raw queries
User.objects.raw("SELECT * FROM users WHERE name = %s", [user_input])

# SAFE - Q objects for complex queries
from django.db.models import Q
User.objects.filter(Q(name=user_input) | Q(email=user_input))

# SAFE - Field validation before dynamic lookups
ALLOWED_FIELDS = ['name', 'email', 'username']
field_name = request.GET.get('field')
if field_name in ALLOWED_FIELDS:
    User.objects.filter(**{field_name: request.GET['value']})

Spring Boot / JPA Injection Risks

Spring Boot applications using JPA are vulnerable when developers use native queries or construct JPQL dynamically with user input.

🚫 Spring Boot Vulnerabilities

// VULNERABLE - String concatenation in native queries
@Query(value = "SELECT * FROM users WHERE name = '" + 
       "#{#userInput}" + "'", nativeQuery = true)
List<User> findByName(@Param("userInput") String name);

// VULNERABLE - Dynamic JPQL construction
String jpql = "SELECT u FROM User u WHERE u.status = '" + userStatus + "'";
return entityManager.createQuery(jpql, User.class).getResultList();

// VULNERABLE - Criteria API with string building
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> root = query.from(User.class);
query.where(cb.equal(root.get(userProvidedField), value));

✅ Spring Boot Secure Patterns

// SAFE - Named parameters in native queries
@Query(value = "SELECT * FROM users WHERE name = :name", nativeQuery = true)
List<User> findByName(@Param("name") String name);

// SAFE - JPA Repository methods
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByNameAndStatus(String name, String status);
}

// SAFE - Parameterized JPQL
TypedQuery<User> query = entityManager.createQuery(
    "SELECT u FROM User u WHERE u.status = :status", User.class);
query.setParameter("status", userStatus);

Advanced Injection Techniques to Watch For

Modern SQL injection attacks have evolved beyond simple quote escaping. Code reviewers need to recognize these sophisticated patterns.

Second-Order SQL Injection

Second-order injections occur when malicious input is stored in the database and later used unsafely in a different query. These are particularly dangerous because they can persist and be triggered by other users.

🔍 Review Pattern: Data Flow Analysis

Track user input from storage to query construction. Look for data that gets saved to the database and later retrieved to build dynamic queries without re-sanitization.

NoSQL and JSON Injection

Applications using PostgreSQL's JSON features, MongoDB queries, or other NoSQL databases are susceptible to similar injection attacks through malformed JSON objects.

// VULNERABLE - MongoDB query injection
db.users.find(JSON.parse(userInput));

// VULNERABLE - PostgreSQL JSON injection
SELECT * FROM users WHERE profile::json @> '${userInput}';

// SAFE - Parameterized NoSQL queries
const filter = { name: userInput, active: true };
db.users.find(filter);

Time-Based Blind Injection

Even when error messages are suppressed and data isn't returned, attackers can exploit time delays to extract information. Look for queries where user input affects performance characteristics.

Code Review Checklist for SQL Injection

🔍 Security Review Checklist

Dynamic Query Construction: Check for string concatenation, interpolation, or formatting in SQL queries
User Input in Column/Table Names: Verify whitelist validation for dynamic field references
ORDER BY and GROUP BY Clauses: Ensure user input is validated against allowed values
Raw SQL Usage: Verify parameterized queries are used consistently
LIKE Operator Usage: Check for proper escaping of wildcard characters
Stored Procedure Calls: Validate parameters passed to database procedures
Error Handling: Ensure database errors don't leak sensitive information

Automated Detection Tools

While manual code review is essential, automated tools can help catch common SQL injection patterns and reduce reviewer workload.

Static Analysis Security Testing (SAST)

SemGrep

Pattern-based code analysis

• Custom rule creation

• Framework-specific patterns

• CI/CD integration

CodeQL

Semantic code analysis platform

• Deep dataflow analysis

• GitHub integration

• Custom query language

Brakeman

Rails-specific security scanner

• ActiveRecord pattern detection

• Confidence scoring

• False positive filtering

Defense in Depth Strategy

Preventing SQL injection requires multiple layers of protection. No single technique is sufficient on its own.

Multi-Layer Security Approach

🛡️

Input Validation

Validate and sanitize all user input at application boundaries

🔒

Parameterized Queries

Use prepared statements and parameterized queries exclusively

👤

Least Privilege

Database users should have minimal necessary permissions

🔍

Runtime Monitoring

Monitor for suspicious database activity and query patterns

Database Security Configuration

Security ControlImplementationEffectiveness
Separate Database UsersRead-only for reporting, limited permissions for appHigh
Network SegmentationDatabase access only from application serversHigh
Query LoggingLog all queries for security monitoringMedium
WAF RulesWeb Application Firewall with SQL injection rulesMedium

Building SQL Injection Awareness

Creating a security-conscious development culture requires ongoing education and practical training for your engineering team.

Team Training Recommendations

📚 Educational Resources

  • • OWASP SQL Injection Prevention Cheat Sheet
  • • Framework-specific security guides
  • • Hands-on security coding challenges
  • • Regular security lunch-and-learns
  • • Code review security checklists

🎯 Practical Exercises

  • • Vulnerable code identification drills
  • • Secure coding pattern workshops
  • • Penetration testing basics
  • • Security tool integration training
  • • Incident response simulations

💡 The best defense against SQL injection is a well-trained development team that thinks security-first.

Catch SQL Injection Vulnerabilities in Code Review

Propel's AI security scanning automatically detects SQL injection patterns across all modern frameworks during code review.

Explore More

Propel AI Code Review Platform LogoPROPEL

The AI Tech Lead that reviews, fixes, and guides your development team.

SOC 2 Type II Compliance Badge - Propel meets high security standards

Company

© 2025 Propel Platform, Inc. All rights reserved.