# SQL Injection (CWE-89) User input is concatenated directly into SQL queries, allowing attackers to modify the query logic and access or manipulate data. This is one of the oldest and most dangerous vulnerability classes, responsible for some of the largest data breaches in history. - Prevalence: Very Common OWASP Top 10 since 2010 - Impact: Critical Data breach, auth bypass, RCE - Prevention: Well understood Parameterized queries **OWASP:** Injection (A03:2021-Injection) - #3 ## Description Without sufficient removal or quoting of SQL syntax in user-controllable inputs, the generated SQL query can cause those inputs to be interpreted as SQL instead of ordinary user data. This can be used to alter query logic to bypass security checks, or to insert additional statements that modify the back-end database. ## Prevention Prevention strategies for SQL Injection based on 7 Shoulder detection rules. ### Go Use parameterized queries with $1 (PostgreSQL) or ? (MySQL/SQLite) placeholders ### Node.js Use parameterized queries with placeholder syntax Use Prisma.sql tagged template for parameterized raw queries instead of regular template literals Use parameterized queries with positional (?) or named (:param) placeholders instead of string interpolation ### Python Use parameterized GraphQL queries with variables instead of string formatting ## Warning Signs - [HIGH] unsafe GraphQL query construction with user input, missing query depth limiting, or disabled introsp - [CRITICAL] user input flowing to SQL queries without parameterization - [CRITICAL] user input flowing into SQL queries without parameterization - [CRITICAL] Raw SQL query uses untrusted input without proper parameterization. Use Prisma.sql`` template tag for safe parameter bin - [CRITICAL] untrusted user input flowing into SQL database queries without proper parameterization - [CRITICAL] Raw SQL query method uses untrusted input without parameterization. Use parameterized queries with ? or $1 placeholders. - [CRITICAL] QueryBuilder clause uses string concatenation with untrusted input. Use parameter binding with :name or ? placeholders. ## Consequences - Read Application Data - Modify Application Data - Bypass Protection Mechanism - Execute Unauthorized Commands ## Mitigations - Use parameterized queries or prepared statements - Use stored procedures with parameterized queries - Escape all user-supplied input using the specific escape routine for your database ## Detection - Total rules: 7 - Critical: 6 - Languages: go, javascript, typescript, python ## Rules by Language ### Javascript (4 rules) - **SQL Injection via Database Queries** [CRITICAL]: Detects user input flowing into SQL queries without parameterization. - Remediation: Use parameterized queries with placeholders. ```javascript db.query('SELECT * FROM users WHERE id = ?', [userId]); ``` Learn more: https://shoulder.dev/learn/javascript/cwe-89/sql-injection - **Prisma Raw Query SQL Injection** [CRITICAL]: Using template literals instead of Prisma.sql`` in $queryRaw bypasses parameter binding and enables SQL injection. - Remediation: Use Prisma.sql`` template tag for parameterized raw queries. ```typescript import { Prisma } from '@prisma/client'; const users = await prisma.$queryRaw( Prisma.sql`SELECT * FROM "User" WHERE id = ${userId}` ); ``` Learn more: https://shoulder.dev/learn/typescript/cwe-89/raw-query-injection - **TypeORM SQL Injection in Raw Query** [CRITICAL]: Raw SQL queries with string concatenation or template literals bypass TypeORM's parameterization, enabling SQL injection attacks. - Remediation: Use parameterized queries with placeholder syntax. ```typescript const users = await manager.query( 'SELECT * FROM users WHERE id = ?', [userId] ); ``` Learn more: https://shoulder.dev/learn/typescript/cwe-89/sql-injection-raw-query - **TypeORM Query Builder SQL Injection** [CRITICAL]: QueryBuilder where clauses with template literals or concatenation bypass parameter binding, enabling SQL injection. - Remediation: Use named parameters in QueryBuilder where clauses. ```typescript const users = await repository .createQueryBuilder('user') .where('user.role = :role', { role }) .getMany(); ``` Learn more: https://shoulder.dev/learn/typescript/cwe-89/unsafe-query-builder ### Typescript (4 rules) - **SQL Injection via Database Queries** [CRITICAL]: Detects user input flowing into SQL queries without parameterization. - Remediation: Use parameterized queries with placeholders. ```javascript db.query('SELECT * FROM users WHERE id = ?', [userId]); ``` Learn more: https://shoulder.dev/learn/javascript/cwe-89/sql-injection - **Prisma Raw Query SQL Injection** [CRITICAL]: Using template literals instead of Prisma.sql`` in $queryRaw bypasses parameter binding and enables SQL injection. - Remediation: Use Prisma.sql`` template tag for parameterized raw queries. ```typescript import { Prisma } from '@prisma/client'; const users = await prisma.$queryRaw( Prisma.sql`SELECT * FROM "User" WHERE id = ${userId}` ); ``` Learn more: https://shoulder.dev/learn/typescript/cwe-89/raw-query-injection - **TypeORM SQL Injection in Raw Query** [CRITICAL]: Raw SQL queries with string concatenation or template literals bypass TypeORM's parameterization, enabling SQL injection attacks. - Remediation: Use parameterized queries with placeholder syntax. ```typescript const users = await manager.query( 'SELECT * FROM users WHERE id = ?', [userId] ); ``` Learn more: https://shoulder.dev/learn/typescript/cwe-89/sql-injection-raw-query - **TypeORM Query Builder SQL Injection** [CRITICAL]: QueryBuilder where clauses with template literals or concatenation bypass parameter binding, enabling SQL injection. - Remediation: Use named parameters in QueryBuilder where clauses. ```typescript const users = await repository .createQueryBuilder('user') .where('user.role = :role', { role }) .getMany(); ``` Learn more: https://shoulder.dev/learn/typescript/cwe-89/unsafe-query-builder ### Python (2 rules) - **GraphQL Injection / Unsafe Query Construction** [HIGH]: Detects unsafe GraphQL query construction with user input, missing query depth limiting, or disabled introspection in production. These can lead to injection attacks, DoS via deeply nested queries, or information disclosure. - Remediation: Use parameterized queries with variables instead of string formatting; disable introspection in production. ```python import graphene class Query(graphene.ObjectType): user = graphene.Field(User, id=graphene.String(required=True)) def resolve_user(self, info, id): return User.objects.get(pk=id) # Use parameter, not f-string # Client sends: query GetUser($id: String!) { user(id: $id) { name } } # With variables: {"id": "123"} ``` Learn more: https://shoulder.dev/learn/python/cwe-89/graphql-injection - **SQL Injection via Database Queries** [CRITICAL]: Detects untrusted user input flowing into SQL database queries without proper parameterization. - Remediation: Use parameterized queries with placeholders. ```python cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) ``` Learn more: https://shoulder.dev/learn/python/cwe-89/sql-injection ### Go (1 rules) - **SQL Injection via Database Queries** [CRITICAL]: Detects user input flowing to SQL queries without parameterization. - Remediation: Use parameterized queries with placeholders instead of string concatenation. ```go rows, err := db.Query("SELECT * FROM users WHERE id = $1", userID) ``` Learn more: https://shoulder.dev/learn/go/cwe-89/sql-injection