Skip to content
Back to skills

Sql Injection Defense

ASecurity

Eliminate SQL injection by sending data as bound parameters instead of concatenated query text, and auditing every raw-SQL escape hatch. Use when writing or reviewing code that builds database queries from variables.

  • 7 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 5, 2026
ai-agentsgosqlapidatabase

Works with

  • api

Security analysis

A100/100

Scanned September 5, 2026

npx -y skills add Amey-Thakur/AI-SKILLS --skill sql-injection-defense --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Sql Injection Defense?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Sql Injection Defense
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/amey-thakur-sql-injection-defense/badge)](https://www.skillsdirectory.com/skills/amey-thakur-sql-injection-defense)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: sql-injection-defense
description: Eliminate SQL injection by sending data as bound parameters instead of concatenated query text, and auditing every raw-SQL escape hatch. Use when writing or reviewing code that builds database queries from variables.
---

# SQL injection defense

SQL injection persists because building a query with string concatenation
feels natural and works in every demo. The fix is not smarter escaping: it
is refusing to put data in the query string at all. Send the query and the
data on separate channels and the whole class disappears.

## Method

1. **Use parameterized queries for every value, always.** Write
   `WHERE email = ?` or `WHERE email = %s` / `:email` and pass the value as
   a bound parameter. The driver sends SQL and data separately, so input can
   never change the query's structure. This is the entire defense; the rest
   is enforcing it.
2. **Never concatenate or interpolate user data into SQL.**
   `f"... WHERE id = {id}"`, `"... = '" + name + "'"`, and template literals
   into a query are the bug. Ban them in review. Manual escaping and quoting
   are not a substitute: you will miss a code path or an encoding.
3. **Parameterize through the ORM, and audit its escape hatches.** Prefer
   the query builder (`User.objects.filter(email=x)`,
   `session.query(...).filter(User.email == x)`). Then grep the raw doors:
   `.raw(`, `execute(`, `text(`, `.extra(`, `sequelize.query`, `knex.raw`.
   Each raw call must still bind parameters, not format a string.
4. **Parameterize identifiers differently from values.** Placeholders bind
   values, not table or column names or `ASC`/`DESC`. When a column or sort
   direction comes from input, match it against a hardcoded allowlist of
   legal names; never interpolate it raw.
5. **Grant the app database account least privilege.** The runtime user
   needs `SELECT/INSERT/UPDATE/DELETE` on its own tables, not `DROP`,
   `GRANT`, or other schemas. Least privilege caps the blast radius when a
   query is compromised through some other flaw.
6. **Add a lint gate against string-built SQL.** Turn on a rule (Bandit
   B608, a Semgrep sql-injection pattern, CodeQL) in CI that flags formatted
   query strings. A rule catches the regression a reviewer skims past at
   5 p.m.

## Litmus tests

- Does every query send user data as a bound parameter, with zero values in
  the SQL text?
- Have you grepped for raw-query methods and confirmed each still binds?
- Are dynamic column and sort inputs checked against an allowlist rather
  than interpolated?

## Boundaries

This skill covers relational SQL. NoSQL query injection (MongoDB `$where`,
JSON operators) follows the same principle with different mechanics. Stored
procedures help only if they use parameters internally; dynamic SQL built
inside a procedure is just as vulnerable.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…