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 ボディ、ヘッダー、Cookie、ファイルアップロード。
データフローを追跡
入力をコード内で追跡しましょう。サニタイズされていますか?
シンクを特定
Where queries are executed: execute(), query()
信頼境界を確認
クエリで使用される保存データに注意。