Improper Neutralization of Special Elements used in an SQL Command
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.
이 취약점을 수정하는 방법
7개의 Shoulder 탐지 규칙을 기반으로 한 SQL Injection 예방 전략.
Use parameterized queries with $1 (PostgreSQL) or ? (MySQL/SQLite) placeholders
func getUser(w http.ResponseWriter, r *http.Request) { userID := r.URL.Query().Get("id") - query := "SELECT * FROM users WHERE id = " + userID - rows, err := db.Query(query) + rows, err := db.Query("SELECT * FROM users WHERE id = $1", userID) // ... }
Use parameterized queries with placeholder syntax
- const query = `SELECT * FROM users WHERE id = '${req.params.id}'`; - await db.query(query); + const query = 'SELECT * FROM users WHERE id = $1'; + await db.query(query, [req.params.id]);
Use Prisma.sql tagged template for parameterized raw queries instead of regular template literals
- import { PrismaClient } from '@prisma/client'; - const prisma = new PrismaClient(); - - app.get('/api/users/search', async (req, res) => { - const { name } = req.query; - const users = await prisma.$queryRaw` - SELECT * FROM "User" WHERE name LIKE '%${name}%' - `; - res.json(users); - }); - // Attacker sends: name=' OR 1=1 -- + import { PrismaClient, Prisma } from '@prisma/client'; + const prisma = new PrismaClient(); + + app.get('/api/users/search', async (req, res) => { + const { name } = req.query; + const users = await prisma.$queryRaw( + Prisma.sql`SELECT * FROM "User" WHERE name LIKE ${`%${name}%`}` + ); + res.json(users); + });
Use parameterized queries with positional (?) or named (:param) placeholders instead of string interpolation
import { getManager } from 'typeorm'; app.get('/api/users/search', async (req, res) => { const { name, role } = req.query; const manager = getManager(); const users = await manager.query( - `SELECT * FROM users WHERE name = '${name}' AND role = '${role}'` - ); - res.json(users); - }); - // Attacker sends: name=' OR '1'='1' -- + 'SELECT * FROM users WHERE name = $1 AND role = $2', + [name, role] + ); + res.json(users); + });
Use parameterized GraphQL queries with variables instead of string formatting
from flask import request import graphene @app.route('/graphql', methods=['POST']) def graphql_endpoint(): - user_id = request.json.get('id') - query = f'{{ user(id: "{user_id}") {{ name email }} }}' - result = schema.execute(query) + query = request.json.get('query') + variables = request.json.get('variables', {}) + result = schema.execute(query, variables=variables) return jsonify(result.data)
코드에서 취약점 찾기
Shoulder를 사용하여 코드에서 SQL Injection 패턴을 스캔하세요. 7 규칙.
# Scan with Shoulder CLI npx @shoulderdev/cli trust --cwe=89 # Or scan entire project npx @shoulderdev/cli trust .
탐지 규칙 (7)
코드 리뷰에서 주의할 점
이 패턴은 잠재적인 SQL Injection 취약점을 나타냅니다. 코드 리뷰와 보안 감사 중에 찾아보세요.
수동 검토 패턴
코드를 수동으로 검토할 때, 이러한 위험한 패턴을 찾으세요.
query = + 문자열 연결execute(f"... or execute("..." +raw_query, rawQuery, executeRaw${ or #{ SQL 문자열 내부보안 전문가의 사고방식
보안 전문가가 이 취약점을 검토할 때 사용하는 사고 모델.
진입점 매핑
URL 파라미터, POST 본문, 헤더, 쿠키, 파일 업로드.
데이터 흐름 추적
입력을 코드 전체에서 추적하세요. 정제되나요?
싱크 식별
Where queries are executed: execute(), query()
신뢰 경계 확인
쿼리에 사용되는 저장된 데이터를 주의하세요.