[{"data":1,"prerenderedAt":384},["ShallowReactive",2],{"blog-vulnerabilities/sql-injection":3},{"id":4,"title":5,"body":6,"category":360,"date":361,"dateModified":361,"description":362,"draft":363,"extension":364,"faq":365,"featured":363,"headerVariant":369,"image":370,"keywords":370,"meta":371,"navigation":372,"ogDescription":373,"ogTitle":370,"path":374,"readTime":375,"schemaOrg":376,"schemaType":377,"seo":378,"sitemap":379,"stem":380,"tags":381,"twitterCard":382,"__hash__":383},"blog/blog/vulnerabilities/sql-injection.md","SQL Injection Explained: How Attackers Manipulate Your Database",{"type":7,"value":8,"toc":339},"minimark",[9,16,21,24,27,42,46,51,54,63,67,134,138,142,151,155,164,174,178,182,185,194,198,201,210,227,231,240,244,247,256,260,263,272,281,309,327],[10,11,12],"tldr",{},[13,14,15],"p",{},"SQL injection happens when user input gets treated as SQL code instead of data. Attackers can read your entire database, modify records, or delete everything. The fix is simple: use parameterized queries or prepared statements. Never build SQL strings by concatenating user input. Modern ORMs like Prisma handle this automatically.",[17,18,20],"h2",{"id":19},"what-is-sql-injection","What Is SQL Injection?",[13,22,23],{},"SQL injection (SQLi) is a vulnerability where attackers can execute arbitrary SQL commands on your database by manipulating user input. It's been on the OWASP Top 10 for over 20 years because it's both common and devastating.",[13,25,26],{},"Here's the classic example:",[28,29,31],"code-block",{"label":30},"Vulnerable login query",[32,33,38],"pre",{"className":34,"code":36,"language":37},[35],"language-text","// User enters: admin' --\nconst query = `SELECT * FROM users WHERE username = '${username}' AND password = '${password}'`;\n\n// The query becomes:\nSELECT * FROM users WHERE username = 'admin' --' AND password = 'anything'\n\n// Everything after -- is a comment, so password check is skipped!\n// Attacker logs in as admin without knowing the password\n","text",[39,40,36],"code",{"__ignoreMap":41},"",[17,43,45],{"id":44},"how-sql-injection-works","How SQL Injection Works",[47,48,50],"h3",{"id":49},"the-vulnerable-pattern","The Vulnerable Pattern",[13,52,53],{},"SQL injection is possible when you build queries using string concatenation:",[28,55,57],{"label":56},"Vulnerable code patterns",[32,58,61],{"className":59,"code":60,"language":37},[35],"// VULNERABLE: String concatenation\nconst query = `SELECT * FROM products WHERE id = ${productId}`;\nconst query = \"SELECT * FROM users WHERE email = '\" + email + \"'\";\n\n// VULNERABLE: Template literals with user input\nconst query = `DELETE FROM orders WHERE id = ${req.params.id}`;\n",[39,62,60],{"__ignoreMap":41},[47,64,66],{"id":65},"common-attack-payloads","Common Attack Payloads",[68,69,70,86],"table",{},[71,72,73],"thead",{},[74,75,76,80,83],"tr",{},[77,78,79],"th",{},"Payload",[77,81,82],{},"Purpose",[77,84,85],{},"Result",[87,88,89,101,112,123],"tbody",{},[74,90,91,95,98],{},[92,93,94],"td",{},"' OR '1'='1",[92,96,97],{},"Always true condition",[92,99,100],{},"Bypass authentication",[74,102,103,106,109],{},[92,104,105],{},"'; DROP TABLE users; --",[92,107,108],{},"Execute additional statement",[92,110,111],{},"Delete entire table",[74,113,114,117,120],{},[92,115,116],{},"' UNION SELECT * FROM passwords --",[92,118,119],{},"Combine with another query",[92,121,122],{},"Extract sensitive data",[74,124,125,128,131],{},[92,126,127],{},"1; UPDATE users SET admin=1 WHERE id=5",[92,129,130],{},"Modify data",[92,132,133],{},"Privilege escalation",[17,135,137],{"id":136},"real-world-sql-injection-attacks","Real-World SQL Injection Attacks",[47,139,141],{"id":140},"data-extraction","Data Extraction",[28,143,145],{"label":144},"Attacker extracts all user data",[32,146,149],{"className":147,"code":148,"language":37},[35],"// Search field input: ' UNION SELECT username, password, email FROM users --\n\n// Original query:\nSELECT name, description FROM products WHERE name LIKE '%search%'\n\n// Becomes:\nSELECT name, description FROM products WHERE name LIKE '%'\nUNION SELECT username, password, email FROM users --%'\n\n// Returns all usernames, passwords, and emails!\n",[39,150,148],{"__ignoreMap":41},[47,152,154],{"id":153},"authentication-bypass","Authentication Bypass",[28,156,158],{"label":157},"Logging in without credentials",[32,159,162],{"className":160,"code":161,"language":37},[35],"// Login form:\nUsername: admin' --\nPassword: (anything)\n\n// Query becomes:\nSELECT * FROM users WHERE username = 'admin' --' AND password = 'x'\n\n// The password check is commented out\n",[39,163,161],{"__ignoreMap":41},[165,166,167],"danger-box",{},[13,168,169,173],{},[170,171,172],"strong",{},"Impact:"," In 2023, SQL injection was responsible for 33% of critical web application vulnerabilities. A single SQLi vulnerability can expose your entire database.",[17,175,177],{"id":176},"how-to-prevent-sql-injection","How to Prevent SQL Injection",[47,179,181],{"id":180},"_1-use-parameterized-queries","1. Use Parameterized Queries",[13,183,184],{},"Parameterized queries (prepared statements) separate SQL code from data:",[28,186,188],{"label":187},"Safe parameterized queries",[32,189,192],{"className":190,"code":191,"language":37},[35],"// PostgreSQL with node-postgres\nconst query = 'SELECT * FROM users WHERE email = $1 AND password = $2';\nconst result = await pool.query(query, [email, password]);\n\n// MySQL with mysql2\nconst query = 'SELECT * FROM users WHERE email = ? AND password = ?';\nconst [rows] = await connection.execute(query, [email, password]);\n\n// SQLite with better-sqlite3\nconst stmt = db.prepare('SELECT * FROM users WHERE id = ?');\nconst user = stmt.get(userId);\n",[39,193,191],{"__ignoreMap":41},[47,195,197],{"id":196},"_2-use-an-orm","2. Use an ORM",[13,199,200],{},"ORMs use parameterized queries automatically:",[28,202,204],{"label":203},"Safe ORM usage",[32,205,208],{"className":206,"code":207,"language":37},[35],"// Prisma (safe by default)\nconst user = await prisma.user.findUnique({\n  where: { email: userInput }\n});\n\n// Drizzle (safe by default)\nconst users = await db.select().from(users).where(eq(users.email, userInput));\n\n// Sequelize (safe by default)\nconst user = await User.findOne({ where: { email: userInput } });\n",[39,209,207],{"__ignoreMap":41},[211,212,213],"warning-box",{},[13,214,215,218,219,222,223,226],{},[170,216,217],{},"ORM raw queries are still dangerous:"," Methods like ",[39,220,221],{},"prisma.$queryRaw",", ",[39,224,225],{},"sequelize.query()",", or Knex raw queries can still be vulnerable if you concatenate user input. Always use the parameterized versions.",[47,228,230],{"id":229},"_3-validate-and-sanitize-input","3. Validate and Sanitize Input",[28,232,234],{"label":233},"Input validation before queries",[32,235,238],{"className":236,"code":237,"language":37},[35],"import { z } from 'zod';\n\n// Define expected input format\nconst userIdSchema = z.string().uuid();\nconst emailSchema = z.string().email();\n\n// Validate before using in queries\nconst validatedId = userIdSchema.parse(req.params.id);\nconst validatedEmail = emailSchema.parse(req.body.email);\n\n// Now safe to use (though still use parameterized queries!)\n",[39,239,237],{"__ignoreMap":41},[47,241,243],{"id":242},"_4-use-least-privilege-database-accounts","4. Use Least Privilege Database Accounts",[13,245,246],{},"Limit the damage if SQLi occurs by restricting database permissions:",[28,248,250],{"label":249},"Database user permissions",[32,251,254],{"className":252,"code":253,"language":37},[35],"-- Create a limited user for the application\nCREATE USER app_user WITH PASSWORD 'secure_password';\n\n-- Only grant necessary permissions\nGRANT SELECT, INSERT, UPDATE ON products TO app_user;\nGRANT SELECT ON categories TO app_user;\n\n-- Don't grant DELETE or DROP permissions unless needed\n-- NEVER use the database superuser for your app\n",[39,255,253],{"__ignoreMap":41},[17,257,259],{"id":258},"sql-injection-in-ai-generated-code","SQL Injection in AI-Generated Code",[13,261,262],{},"AI assistants often generate SQL injection vulnerabilities:",[28,264,266],{"label":265},"What AI might generate",[32,267,270],{"className":268,"code":269,"language":37},[35],"// AI often produces string concatenation\napp.get('/user/:id', async (req, res) => {\n  const query = `SELECT * FROM users WHERE id = ${req.params.id}`;\n  const result = await db.query(query);\n  res.json(result);\n});\n\n// What you should ask for:\napp.get('/user/:id', async (req, res) => {\n  const query = 'SELECT * FROM users WHERE id = $1';\n  const result = await db.query(query, [req.params.id]);\n  res.json(result);\n});\n",[39,271,269],{"__ignoreMap":41},[273,274,275],"success-box",{},[13,276,277,280],{},[170,278,279],{},"Prompt tip:"," When asking AI to generate database queries, specifically request \"parameterized queries\" or \"prepared statements\" to get secure code.",[282,283,284,291,297,303],"faq-section",{},[285,286,288],"faq-item",{"question":287},"What is SQL injection?",[13,289,290],{},"SQL injection is a vulnerability where attackers insert malicious SQL code through user input fields. If the application builds SQL queries by concatenating user input directly, attackers can manipulate the query to access, modify, or delete database data.",[285,292,294],{"question":293},"Does using an ORM prevent SQL injection?",[13,295,296],{},"ORMs like Prisma, Drizzle, and Sequelize use parameterized queries by default, which prevents most SQL injection. However, raw query methods (like prisma.$queryRaw) can still be vulnerable if you concatenate user input directly.",[285,298,300],{"question":299},"Can NoSQL databases have injection vulnerabilities?",[13,301,302],{},"Yes, NoSQL databases like MongoDB can have injection vulnerabilities called NoSQL injection. Attackers can manipulate queries using operators like $gt or $ne if user input isn't properly validated.",[285,304,306],{"question":305},"Is input validation enough to prevent SQL injection?",[13,307,308],{},"Input validation helps but isn't sufficient alone. Always use parameterized queries as your primary defense. Validation is a secondary layer that catches obvious malicious input and prevents other types of attacks.",[310,311,312,317,322],"related-articles",{},[313,314],"related-card",{"description":315,"href":316,"title":177},"Complete prevention guide","/blog/how-to/prevent-sql-injection",[313,318],{"description":319,"href":320,"title":321},"Similar injection vulnerability","/blog/vulnerabilities/command-injection","Command Injection",[313,323],{"description":324,"href":325,"title":326},"Secure database setup","/blog/guides/supabase","Supabase Security Guide",[328,329,332,336],"cta-box",{"href":330,"label":331},"/","Start Free Scan",[17,333,335],{"id":334},"scan-for-sql-injection-vulnerabilities","Scan for SQL Injection Vulnerabilities",[13,337,338],{},"Our scanner detects SQLi patterns in your code and tests your endpoints.",{"title":41,"searchDepth":340,"depth":340,"links":341},2,[342,343,348,352,358,359],{"id":19,"depth":340,"text":20},{"id":44,"depth":340,"text":45,"children":344},[345,347],{"id":49,"depth":346,"text":50},3,{"id":65,"depth":346,"text":66},{"id":136,"depth":340,"text":137,"children":349},[350,351],{"id":140,"depth":346,"text":141},{"id":153,"depth":346,"text":154},{"id":176,"depth":340,"text":177,"children":353},[354,355,356,357],{"id":180,"depth":346,"text":181},{"id":196,"depth":346,"text":197},{"id":229,"depth":346,"text":230},{"id":242,"depth":346,"text":243},{"id":258,"depth":340,"text":259},{"id":334,"depth":340,"text":335},"vulnerabilities","2026-01-26","SQL injection lets attackers read, modify, or delete your database through input fields. Learn how SQLi works and how to protect your vibe-coded app with parameterized queries.",false,"md",[366,367,368],{"question":287,"answer":290},{"question":293,"answer":296},{"question":299,"answer":302},"red",null,{},true,"Learn how SQL injection works and protect your database from this critical vulnerability.","/blog/vulnerabilities/sql-injection","9 min read","[object Object]","TechArticle",{"title":5,"description":362},{"loc":374},"blog/vulnerabilities/sql-injection",[],"summary_large_image","Evwk2Ye6yBu-hEsTNCz2DWxeQZGMHZltyPG5Rdcagmo",1775843918547]