# 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. **Stack:** JavaScript - 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 4개의 Shoulder 탐지 규칙을 기반으로 한 SQL Injection 예방 전략. ### JavaScript 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 **Vulnerable code:** ``` // VULNERABLE: String concatenation const query = "SELECT * FROM users WHERE id = '" + userId + "'"; db.query(query); ``` **Secure code:** ``` // SAFE: Parameterized query const query = "SELECT * FROM users WHERE id = $1"; db.query(query, [userId]); ``` ## Warning Signs - [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] 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. ## Audit Steps 1. Search for db.query(), pool.query(), connection.query() 2. Check for string concatenation or template literals in queries 3. Verify all user input goes through parameterized queries 4. Review ORM usage (Sequelize, Prisma) for raw query escapes ## Consequences - 애플리케이션 데이터 읽기 - 애플리케이션 데이터 수정 - 보호 메커니즘 우회 - 승인되지 않은 명령 실행 ## Mitigations - 파라미터화된 쿼리 또는 준비된 문(prepared statements)을 사용하세요 - 파라미터화된 쿼리와 함께 저장 프로시저를 사용하세요 - 모든 사용자 입력은 사용 중인 데이터베이스 전용 이스케이프 루틴으로 이스케이프하세요 ## 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