SQL Injections in Node.js

SQL Injection is a security problem that takes advantage of how an application talks to its database to run SQL commands it shouldn't.

This part shows first how SQL injections can happen in Node.js applications and then gives examples of how to protect against these dangers.

How SQL Injections Happen

SQL injections happen when user input is itself a SQL query and our application lacks proper validation or sanitation. This allows attackers to manipulate our databases.

This will make more sense as an example:

const mysql = require('mysql');
const connection = mysql.createConnection({
    host     : 'example.com',
    user     : 'dbuser',
    password : 'dbpassword',
    database : 'mydb'
});

const userInput = "'; DROP TABLE users; --"; // Malicious user input
const query = "SELECT * FROM users WHERE email = '" + userInput + "'";

connection.query(query, function (error, results, fields) {
    if (error) throw error;
    // process results
});

Here I'm using userInput as an example attacker input. This will inject a malicious payload ("'; DROP TABLE users; --") that alters the query to delete the users table.

Pretty fun, right?

How Do I Stop Them?

There are a few ways you can prevent SQL injections.

Here's a few of the easiest ways with examples:

Use Parameterized Queries

Using "parameterized queries" is a primary defense against SQL injections.

This method ensures that user inputs are treated as data, not as part of the SQL command.

Here's an example with pg (PostgreSQL):

const { Pool } = require('pg');
const pool = new Pool();

// Correct way to handle user input
const text = 'SELECT * FROM users WHERE id = $1';
const values = [userId]; // Assume 'userId' is user input

// Execute query
pool.query(text, values, (err, res) => {
  if (err) {
    console.log(err.stack);
  } else {
    console.log(res.rows[0]);
  }
});

Sanitize User Input

Like most backend code, validating and sanitizing inputs ensure they conform to expected formats, further reducing injection risks.

My favorite library at the minute to help me with sanitation is Zod, so here's a quick example so you can see how you can validate data:

const zod = require('zod');

// Define a schema for the user input
const UserInputSchema = zod.object({
  username: zod.string().min(1).regex(/^[a-zA-Z0-9]+$/), // Alphanumeric characters only
  // Add more fields as necessary
});

// Example user input
let userInput = {
  username: "someUserInput" // Assume this comes from the user
};

try {
  // Validate the user input against the schema
  UserInputSchema.parse(userInput);
  
  // If the input is valid, proceed with processing
  console.log("User input is valid.");
  // Proceed with your SQL query with sanitized input
} catch (error) {
  // Handle the case where the input does not conform to the schema
  console.error("Invalid input detected:", error.errors);
}

Use ORM Libraries

ORMs (Object-Relational Mapping) like Sequelize abstract SQL operations into JavaScript, minimizing direct SQL string manipulations. Plenty of options here, so you can quickly search and find your favorite flavor.

But here's an example with Sequelize:

const { Sequelize, Model, DataTypes } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');

class User extends Model {}

User.init({
  username: DataTypes.STRING,
  birthday: DataTypes.DATE
}, { sequelize, modelName: 'user' });

// Using Sequelize to find a user securely
const safeFindUser = async (userId) => {
  const user = await User.findByPk(userId);
  console.log(user);
}

safeFindUser(someUserId); // someUserId is user-provided input

Now with this knowledge you should be able to quickly and easily plug those vulnerabilities.

SqlJavaScript
Avatar for Niall Maher

Written by Niall Maher

Founder of Codú - The web developer community! I've worked in nearly every corner of technology businesses; Lead Developer, Software Architect, Product Manager, CTO and now happily a Founder.

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.