How safe is this generated query from SQL injection? - sql

I am trying to make a search bar which works with multiple words, but I am worried about SQL injection.
I am using node express with the npm mssql package.
Here's the code which gets the criteria, generates the SQL and runs it:
router
.get('/search/:criteria', function (req, res) {
var criteria = req.params.criteria;
var words = criteria.split(" ");
var x = ""
words.map(word => x += `name like '%${word}%' and `);
x = x.substring(0, x.length - 5); // Remove trailing 'and'
var query = `SELECT * FROM table WHERE ${x}`
new sql.ConnectionPool(db).connect().then(pool => {
return pool.request().query(query)
}).then(result => {
})
});
A search for something to search would result in this query:
SELECT * FROM table
WHERE
name like '%something%'
and name like '%to%'
and name like '%search%'
I tried some SQL injections myself, but none of them seem to work.
Note: I am aware that we should always use inputs for this. It works fine for one word, but I don't know how to use inputs for many words. Ex:
new sql.ConnectionPool(db).connect().then(pool => {
return pool.request()
.input('input', '%'+criteria+'%')
.query(query)
})

The answer is: It's not safe. Your code does exactly nothing to make it safe, either. Don't build SQL by concatenating/interpolating user-supplied data into the statement.
In addition, you don't do any escaping for LIKE itself, either, so that is just as unclean.
If you need dynamic SQL, build a prepared SQL statement with the expected number of placeholders and then bind user-supplied values to those placeholders.
router.get('/search/:criteria', (req, res) => {
const ps = new sql.PreparedStatement();
const sqlConditions = [];
const escapedValues = {};
// set up escaped values, safe SQL bits, PS parameters
req.params.criteria.split(" ").forEach((v, i) => {
const paramName = 'val' + i;
escapedValues[paramName] = v.replace(/[\\%_]/g, '\\$&');
sqlConditions.push(`name LIKE '%' + #${paramName} + '%' ESCAPE '\'`);
ps.input(paramName, sql.VarChar);
});
// build safe SQL string, prepare statement
const sql = 'SELECT * FROM table WHERE ' + sqlConditions.join(' AND ');
ps.prepare(sql);
// connect, execute, return
ps.execute(escapedValues).then(result => {
res(result)
});
});
(Disclaimer: code is untested, as I have no SQL Server available right now, but you get the idea.)

Related

How to build query as variable (from user Input) in prisma.queryRaw without using queryRawUnsafe

I was trying to query my (postgres) db with a customizable statement built front end.
My resolver gets the built query inside the input param, but when I use the queryRaw method I get this error:
`"\nInvalid `prisma.queryRaw()` invocation:\n\n\n Raw query failed. Code: `42601`. Message: `db error: ERROR: syntax error at or near \"$1\"`"`
Is there a way to build a custom query and pass it like the input variable WITHOUT USING queryRawUnsafe to prisma? (queryRawUnsafe works fine, but well.. it's unsafe XD) Thanks <3
Here is my code.
getCars: (_parent, { input }, { prisma }) => {
if(input){
console.log(input) // --> SELECT * FROM car WHERE car."plate" ILIKE '%123%' //type String
const differentInput = '%123%'
// const result = prisma.$queryRaw`SELECT * FROM car WHERE car."plate" ILIKE '%123%'` // works
// const result = prisma.$queryRaw`SELECT * FROM car WHERE car."plate" ILIKE ${differentInput}` // works
// const result = prisma.$queryRawUnsafe(input) // works
const result = prisma.$queryRaw`${input}` // Doesn`t work
return result
}
// ... Other code
}
prisma.$queryRaw only accepts templated strings, not just strings. You can use the Prisma.sql helper to generate those templated strings to get the expected results. That might look like:
const sql = Prisma.sql`SELECT * FROM car WHERE car."plate" ILIKE '%123%'`
const result = prisma.$queryRaw`${sql}`
The queryRaw documentation mentions Prisma.sql with other examples but doesn't show any examples of what you are trying to do.

NodeJS SQLite3 prepared statement with IN and AND

I have this API setup in Express but I cannot figure out how to correctly prepare the language filter with the IN statement using SQLite3 in node.js.
The first query applies the language filter correctly. I get the correct results but this could pose a sql injection risk.
The second query finds no results because of the language filter.
How do I correctly setup the db.prepare statement to accept both words, and language?
app.post('/api/languages/:language/getTextWords', (req, res) => {
let words = req.body.map(word => word.toLowerCase())
let wordMap = words.map(() => "?").join(',')
let language = req.params.language.toLowerCase();
// the language filter is applied correctly I get results but the statement is not fully prepared
// let query = db.prepare(`SELECT * FROM words WHERE word IN (${wordMap}) AND language = '${language}'`, words)
// no results are found because of the language filter
let query = db.prepare(`SELECT * FROM words WHERE word IN (${wordMap}) AND language = '?'`, words, language)
query.all((err, rows) => {
if (err) {
res.status(500).send(err.message)
throw err;
}
if (!rows) {
res.json([])
}
if (rows) {
res.json(rows)
}
})
})
Here is a screenshot of the DB table in question:
words table
pass values as an array:
let query = db.prepare(`SELECT * FROM words WHERE word IN (${wordMap}) AND language = ?`, [...words, language]);

Do strings need to be escaped inside parametrized queries?

I'm discovering Express by creating a simple CRUD without ORM.
Issue is, I'm not able to find any record through the Model.findBy() function
model User {
static async findBy(payload) {
try {
let attr = Object.keys(payload)[0]
let value = Object.values(payload)[0]
let user = await pool.query(
`SELECT * from users WHERE $1::text = $2::text LIMIT 1;`,
[attr, value]
);
return user.rows; // empty :-(
} catch (err) {
throw err
}
}
}
User.findBy({ email: 'foo#bar.baz' }).then(console.log);
User.findBy({ name: 'Foo' }).then(console.log);
I've no issue using psql if I surround $2::text by single quote ' like:
SELECT * FROM users WHERE email = 'foo#bar.baz' LIMIT 1;
Though that's not possible inside parametrized queries. I've tried stuff like '($2::text)' (and escaped variations), but that looks far from what the documentation recommends.
I must be missing something. Is the emptiness of user.rows related to the way I fetch attr & value ? Or maybe, is some kind of escape required when passing string parameters ?
"Answer":
As stated in the comment section, issue isn't related to string escape, but to dynamic column names.
Column names are not identifiers, and therefore cannot be dynamically set using a query parameter.
See: https://stackoverflow.com/a/50813577/11509906

How to Insert in SQL with NodeJs and mssql library with params

I have 2 arrays, the first one contains the columns for the insert and the second one contains de values
const columns = ['columnName1','columnName2','columnName3','columnName4','columnName5','columnName6'];
const values2 = ['test1', 'test2', 27, 1, 'an address', null];
module.exports = {columns, values2}
And I'm trying to do the insert using mmsql library but not even the documentation contains a sample with multiple columns to insert values to.
This is a sample of my insert
let pool = await sql.connect(config)
const result = await pool.request().query(`INSERT INTO [Test].[TableName] (${testData.columns}) VALUES ?`, [[testData.values2]]);
console.dir(result);
And I'm getting the following error:
RequestError: Incorrect syntax near '?'.
I was able to select to the db with no issues but there's something about the syntax that it doesn't seem to like..
At least you forgot to wrap ? with round brackets:
const result = await pool.request().query(`INSERT INTO [Test].[TableName] (${testData.columns}) VALUES (?)`, [[testData.values2]]);
As I can't find a single example, where you pass the parameters of your sql-query to the query function or use ? as a placeholder in their documentation, I'm not sure, whether node-mssql supports this. But if it does, you should at least create as many ? in the sql statement as you add values.
var cols = [...]; //your columns
const sqlquery = `insert into sometable (${cols}) values (${new Array(cols.length).fill('?')})`;
The documented way would be using input parameters
await pool.request()
.input('p1', sql.Int, value1)
.input('p2', sql.VarChar(50), value2)
.query('insert into sometable(col1, col2) values(#p1, #p2)');
To keep this flexible you could use something like the following
var cols = ['col1', 'col2'];
var vals = ['a string', 23];
const request = pool.request();
for (let i = 0; i < vals.length; i++)
request.input(`param_${i}`, vals[i]); //if you skip the parametertype, mssql will guess it from the value
const sqlquery = `insert into sometable(${cols}) values (${vals.map((_,i) => `#param_${i}`)`;
const result = await request.query(sqlquery);

How to structure syntax for `SELECT` with `IN` SQL Server Query from Node via `mssql` package

I want to make use of SELECT and IN to match on values between tables from two different SQL databases in my Node app that makes use of the mssql package:
My question is, if I am passing a variable representing that array of values in my Node app, that looks like this:
const arr = ["1323", "2311", "1234"];
would I do this?:
N'[arr]'
or this?
N'arr'
Or is there some other syntax I should use?
Right now, this is the full query I'm passing:
// An array *like* this, saved in a variable name:
const sourceIdArr = ["1323", "2311", "1234"];
const selectInQuery = `
DECLARE #sourceIdArr NVARCHAR(4000) = N 'sourceIdArr'
SELECT NDID FROM SR_Empsheets
WHERE NDID IN ( SELECT value from openjson(#sourceIdArr) )
`;
Will this work, or should I approach it differently?
So something like
const sourceIdArr = ["1323", "2311", "1234"];
const selectInQuery = `
SELECT NDID FROM SR_Empsheets
WHERE NDID IN ( SELECT value from openjson(#sourceIdArr) )
`;
new sql.Request()
.input("sourceIdArr", sql.VarChar(sql.MAX), JSON.stringify(sourceIdArr) )
.execute(selectInQuery , (err, result) => {
// ... error checks
console.dir(result)
});