惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Heimdal Security Blog
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
Google DeepMind News
Google DeepMind News
大猫的无限游戏
大猫的无限游戏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
宝玉的分享
宝玉的分享
博客园 - 聂微东
月光博客
月光博客
Cyberwarzone
Cyberwarzone
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
Project Zero
Project Zero
T
Tor Project blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
L
LINUX DO - 最新话题
博客园 - 叶小钗
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Attack and Defense Labs
Attack and Defense Labs
Spread Privacy
Spread Privacy
Forbes - Security
Forbes - Security
Simon Willison's Weblog
Simon Willison's Weblog
N
Netflix TechBlog - Medium
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
Hacker News: Ask HN
Hacker News: Ask HN
I
InfoQ
M
MIT News - Artificial intelligence
AI
AI
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
C
Check Point Blog
The Hacker News
The Hacker News
C
Cyber Attacks, Cyber Crime and Cyber Security
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
Blog — PlanetScale
Blog — PlanetScale
美团技术团队
D
Darknet – Hacking Tools, Hacker News & Cyber Security
GbyAI
GbyAI
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
K
Kaspersky official blog

Blog — PlanetScale

Keeping a Postgres queue healthy — PlanetScale Patterns for Postgres Traffic Control — PlanetScale Graceful degradation in Postgres — PlanetScale High memory usage in Postgres is good, actually — PlanetScale Stripe Projects partnership: Provision PlanetScale Postgres and MySQL databases from the Stripe CLI — PlanetScale Enhanced tagging in Postgres Query Insights — PlanetScale Behind the scenes: How Database Traffic Control works — PlanetScale Introducing Database Traffic Control — PlanetScale Scaling Postgres connections with PgBouncer — PlanetScale Drizzle joins PlanetScale — PlanetScale Video Conferencing with Postgres — PlanetScale Faster PlanetScale Postgres connections with Cloudflare Hyperdrive — PlanetScale Introducing the PlanetScale MCP server — PlanetScale Database Transactions — PlanetScale Automating our changelog with Cursor commands — PlanetScale Postgres 18 is now available — PlanetScale Using MotherDuck with PlanetScale — PlanetScale $50 PlanetScale Metal is GA for Postgres — PlanetScale AI-Powered Postgres index suggestions — PlanetScale $5 PlanetScale is live — PlanetScale Announcing Vitess 23 — PlanetScale $50 PlanetScale Metal — PlanetScale Report on our investigation of the 2025-10-20 incident in AWS us-east-1 — PlanetScale $5 PlanetScale — PlanetScale Benchmarking Postgres 17 vs 18 — PlanetScale Larger than RAM Vector Indexes for Relational Databases — PlanetScale Partnering with Cloudflare to bring you the fastest globally distributed applications — PlanetScale Processes and Threads — PlanetScale PlanetScale for Postgres is now GA — PlanetScale Postgres High Availability with CDC — PlanetScale Announcing Neki — PlanetScale Caching — PlanetScale The principles of extreme fault tolerance — PlanetScale Announcing PlanetScale for Postgres — PlanetScale Benchmarking Postgres — PlanetScale Announcing Vitess 22 — PlanetScale The Real Failure Rate of EBS — PlanetScale IO devices and latency — PlanetScale Announcing PlanetScale Metal — PlanetScale PlanetScale Metal: There’s no replacement for displacement — PlanetScale Upgrading Query Insights to Metal — PlanetScale Automating cherry-picks between OSS and private forks — PlanetScale Database Sharding — PlanetScale Anatomy of a Throttler, part 3 — PlanetScale Introducing sharding on PlanetScale with workflows — PlanetScale Announcing Vitess 21 — PlanetScale Announcing the PlanetScale vectors public beta — PlanetScale Anatomy of a Throttler, part 2 — PlanetScale Instant deploy requests — PlanetScale Anatomy of a Throttler, part 1 — PlanetScale Increase IOPS and throughput with sharding — PlanetScale Tracking index usage with Insights — PlanetScale Faster backups with sharding — PlanetScale Building data pipelines with Vitess — PlanetScale The State of Online Schema Migrations in MySQL — PlanetScale Optimizing aggregation in the Vitess query planner — PlanetScale Dealing with large tables — PlanetScale Announcing Vitess 20 — PlanetScale Self-managed Vitess vs Managed Vitess with PlanetScale — PlanetScale Achieving data consistency with the consistent lookup Vindex — PlanetScale The MySQL adaptive hash index — PlanetScale Introducing global replica credentials — PlanetScale Profiling memory usage in MySQL — PlanetScale Summer 2023: Fuzzing Vitess at PlanetScale — PlanetScale How PlanetScale makes schema changes — PlanetScale Identifying and profiling problematic MySQL queries — PlanetScale The Problem with Using a UUID Primary Key in MySQL — PlanetScale Announcing Vitess 19 — PlanetScale PlanetScale forever — PlanetScale Introducing schema recommendations — PlanetScale Amazon Aurora Pricing: The many surprising costs of running an Aurora database — PlanetScale Three common MySQL database design mistakes — PlanetScale OAuth applications are now available to everyone — PlanetScale Deprecating the Scaler plan — PlanetScale PlanetScale branching vs. Amazon Aurora blue/green deployments — PlanetScale Databases at scale — PlanetScale Considerations for building a database disaster recovery plan — PlanetScale Working with Geospatial Features in MySQL — PlanetScale PlanetScale vs Amazon Aurora replication — PlanetScale Introducing the Vantage and PlanetScale integration — PlanetScale MySQL isolation levels and how they work — PlanetScale Introducing the schemadiff command line tool — PlanetScale $ pscale ping — PlanetScale Announcing foreign key constraints support — PlanetScale The challenges of supporting foreign key constraints — PlanetScale What is HTAP? — PlanetScale Introducing Insights Anomalies — PlanetScale Webhook security: a hands-on guide — PlanetScale MySQL replication: Best practices and considerations — PlanetScale A guide to HTML email with Ruby on Rails and Tailwind CSS — PlanetScale Sharding for cost-effective database management — PlanetScale PlanetScale ranks 188th in Deloitte’s top 500 fastest-growing companies — PlanetScale Announcing the Fivetran integration — PlanetScale Introducing webhooks — PlanetScale What is MySQL replication and when should you use it? — PlanetScale Sync user data between Clerk and a PlanetScale MySQL database — PlanetScale Introducing database reports — PlanetScale Distributed caching systems and MySQL — PlanetScale What is MySQL partitioning? — PlanetScale MySQL High Availability: Connection handling and concurrency — PlanetScale
How to Prevent SQL Injection Attacks in Node.js — PlanetScale
James Q Quick · 2022-03-04 · via Blog — PlanetScale

James Q Quick |

Although the tooling around databases has come a long way, it is still your responsibility to protect them against attacks. In this article, you’ll learn to prevent SQL injection attacks in Node.js using the mysql2 npm package.

What is a SQL injection attack

A SQL injection attack happens when a user injects malicious bits of SQL into your database queries. Most commonly, this happens when allowing a user to pass input to a database query without validation which can alter the original intended query. By injecting their own SQL, the user can cause harm by:

  • reading sensitive data
  • modifying sensitive data
  • deleting sensitive data

As you can probably imagine, these types of attacks can have negative impacts on your applications and your business. In fact, you’ve probably heard of some major companies being involved in data breaches in the past couple of years. This can lead to loss of customers, revenue, application uptime, and more.

Examples of SQL injection attacks

You now have a general understanding of what SQL injection attacks are, but I think it would be good to see a few specific examples.

Let’s explore a developer related-scenario where, hypothetically, you build an application that stores code repositories. Just like GitHub, these user-created repositories can be either public or private. Furthermore, a user has the ability to search public repositories by tag. For simplicity, let’s assume that each repository only has one tag.

In your application logic, you would use the user’s search term to generate the SQL query. In your Node.js code, you might be tempted to use ES6 template literal strings to interpolate that value directly to your query string like so:

const query = `SELECT * FROM Repository WHERE TAG = '${userQuery}' AND public = 1`

For a search of "javascript", your SQL query string might look like this:

SELECT * FROM Repository WHERE TAG = 'javascript' AND public = 1;

In this case, you are attempting to select all public repositories that have “javascript” as their tag. Assuming reasonable user input, this works fine, but what if the user were to search for something like this javascript';--. Now, things start to become dangerous.

The -- is the SQL code for a comment. This means that it would then shortcut the rest of the query. So, the unvalidated query would look like this.

SELECT * FROM Repository WHERE TAG = 'javascript';--' AND public = 1;

Since the part after the "--" would be ignored, the query that gets executed looks more like this.

SELECT * FROM Repository WHERE TAG = 'javascript';

As you can see, this removed the additional clause in the query which previously prevented private repositories from being included. You can imagine this being a significant problem for intellectual property.

One other type of SQL injection attack to be aware of is one that can add a secondary statement to the query. Let’s stay with the same example, but say the user searches for javascript'; DROP TABLE Repository;--. Then, the query would become:

SELECT * FROM Repository WHERE TAG = 'javascript'; DROP TABLE Repository;--' AND public = 1;

In this example, the original query is terminated with the ; but then followed by a second query that would drop the entire Repository table. NO GOOD!

For notes on a few other examples of SQL injection attacks, check the W3Schools SQL Injection page.

Configuring the mysql2 client in Node.js

For a quick reference, let’s take a look at how to set up the mysql2 client in Node.js. You’ll first want to install the package:

For an in-depth tutorial on creating an API with Node.js, mysql2, and PlanetScale, check out Create a Harry Potter API with Node.js, Express, and MySQL

Once you have this package installed, you can initialize the client.

import mysql from 'mysql2/promise'
const connection = await mysql.createConnection(process.env.DATABASE_URL)

This sample code uses environment variables for the database connection string. You’ll need to also install the dotenv package for testing this locally. It also uses the promises-based version of the library so that you can use modern async/await syntax.

From there, you can make queries like so:

    const query = 'SELECT * FROM Repository WHERE TAG = 'javascript' AND public = 1';
    const [rows] = await connection.query(query);

If you’re working with Express.js, you could then define an endpoint that accepts user input as userQuery, queries the database, and returns the repositories in JSON format.

import express from 'express';
import mysql from 'mysql2/promise';

const connection = await mysql.createConnection(process.env.DATABASE_URL);
 const app = express();

app.get('/repositories/:userQuery', async (req, res) => {
    const {userQuery} = req.params;
    const query = 'SELECT * FROM Repository WHERE TAG = '${userQuery}' AND public = 1';
    const [rows] = await connection.query(query);
   res.json(rows);
});

app.listen(3001, () =>{
  console.log('App is running');
});

Preventing SQL injection attacks

There are a few common ways to prevent SQL injection attacks:

  1. Don’t allow multiple statements
  2. Use placeholders instead of variable interpolation
  3. Validate user input
  4. Allowlist user input

Don’t allow multiple statements if you can avoid it

Conveniently, number 1 is handled by the mysql2 client (and many other database clients). It prevents multiple statements from being executed by default. So, even if the user submits an input that attempts to terminate a query and run a second one, the second one won’t run. This is the default configuration, but you can override that if you choose.

Note

Although this configuration property is available, it is typically not recommended to allow multiple statements unless absolutely necessary.

const connection = await mysql.createConnection({
  uri: process.env.DATABASE_URL,
  multipleStatements: true
})

To emphasize the need for more levels of protection, refer to the example above where injecting a comment (ex. javascript';--) into the SQL allowed the user to read from private repositories. Since that was done using only one statement, setting multipleStatements: false still wouldn’t be enough.

Use placeholders

Therefore, you should never accept raw input from a user and input it directly into your query string. Instead, you should use placeholders (?) (or parametrized queries) which would look like this (notice the ? as the placeholder):

const query = 'SELECT * FROM Repository WHERE TAG = ? AND public = 1'
const [rows] = await connection.query(query, [userQuery])

By using placeholders, the malicious SQL will be escaped and treated as a raw string, not as actual SQL code. The end result query would look like this:

  SELECT * FROM Repository WHERE TAG = `javascript';--` AND public = 1;

Thanks to using placeholders, the malicious SQL is not run and instead, is treated as a search query as intended.

Input validation

In addition to using placeholders, you can add logic in your applications to prevent invalid user input. Let’s stick with the example of querying public repositories by tag. For demo purposes, you can assume that you should not have a tag that includes special characters or numbers. In other words, tags should only use capital and lowercase letters (A-Z, a-z).

This means you can add logic to your application to validate that user input matches the correct formatting (no numbers and no special characters). To do this, you can create a regex pattern to match the user input. If it doesn’t match, return an error.

 app.get('/repositories/:userQuery', async (req, res) => {

    const {userQuery} = req.params;
    const onlyLettersPattern = /^[A-Za-z]+$/;

    if(!userQuery.match(onlyLettersPattern)){
      return res.status(400).json({ err: "No special characters and no numbers, please!"})
    }

    ...
  });

Now the code doesn’t even get to the SQL part unless a valid input is passed. You can apply this method with any sort of validation that is relevant to your data. For example, if you allow the user to query by an id property which should be a number, you can throw an error if the input isn’t a valid number.

  app.get('/repositories/:id', async (req, res) => {
    const {id} = req.params;

    if(isNaN(Number(id))) {
      return res.status(400).json({ err: "Numbers only, please!"})
    }
...

Allowlisting

One last option you have is to use allowlisting, a specific type of input validation. Allowlisting is useful if you know every possible valid user input. From there, you can easily reject anything else.

For example, let’s say for your repository tags, there are only three valid tags: “javascript”, “html”, and “css”. If that’s the case, then you can check whether or not the user input is "allowlisted" by comparing it against known valid inputs.

 app.get('/repositories/:userQuery', async (req, res) => {

    const {userQuery} = req.params;
    const validTags = ["javascript", "html", "css"];

    if(!validTags.includes(userQuery)){
      return res.status(400).json({err: "Valid tags only, please!"});
    }

    ...
  });

Yes, this example is a bit simplified with just three valid tags, but this works at scale as well. A more realistic scenario might be that you store all known tags in their own table in your database. Then, to validate the user input, you can check against all the tag records in your database.

Wrap up

Hopefully, this helped give you a good overview of what SQL injection attacks are and how to prevent them. They can be detrimental to your application and business, so it’s important to plan ahead when accepting user input for your database queries to prevent any negative side effects.