I would like to invoke array_prepend into a json[] using a parameterized query. I am using pg-promise npm package but this uses the normal node-postgres adapter under the hood.
My query is:
db.query(`update ${schema}.chats set messages =
array_prepend('{"sender":"${sender}","tstamp":${lib.ustamp()},"body":$1}',messages) where chat_id = ${chat_id}`
, message));
Same with "$1".
It works with a non-parameterized query.
Above code produces :
{ [error: syntax error at or near "hiya"]
Main reason for this is to avoid sql injection (docs say that they escape adequately when using the parameterized queries).
There are 2 problems in your query.
The first one is that you are using ES6 template strings, while also using sql formatting with ${propName} syntax.
From the library's documentation:
Named Parameters are defined using syntax $*propName*, where * is any of the following open-close pairs: {}, (), [], <>, //, so you can use one to your liking, but remember that {} are also used for expressions within ES6 template strings.
So you either change from ES6 template strings to standard strings, or simply switch to a different variable syntax, like $/propName/ or $[propName], this way you will avoid the conflict.
The second problem is as I pointed earlier in the comments, when generating the proper SQL names, use what is documented as SQL Names.
Below is a cleaner approach to the query formatting:
db.query('update ${schema~}.chats set messages = array_prepend(${message}, messages) where chat_id = ${chatId}', {
schema: 'your schema name',
chatId: 'your chat id',
message: {
sender: 'set the sender here',
tstamp: 'set the time you need',
body: 'set the body as needed'
}
}
);
When in doubt about what kind of query you are trying to execute, the quickest way to peek at it is via pgp.as.format(query, values), which will give you the exact query string.
And if you still want to use ES6 template strings for something else, then you can change the string to:
`update $/schema~/.chats set messages = array_prepend($/message/, messages) where chat_id = $/chatId/`
That's only one example, the syntax is flexible. Just remember not to use ES6 template string formatting to inject values into queries, because ES6 templates have no knowledge about how to properly format JavaScript types to comply with PostgreSQL, only the library knows it.
Related
In my node.js serverless app (aws-lambda function), I got input validation using jsonschema.
I want to have the ability to avoid making SQL injection to my server side code.
Is there any option to use jsonschema to validate such cases ?
Alternatively, which minimal regex could help us check this case ?
I will state unequivocally that validating a JSON document with jsonschema has nothing to do with SQL injection defense.
In other words, a JSON document can pass jsonschema validation but still present a SQL injection risk.
Here's a valid JSON document:
{
"film": "Singin' in the Rain",
"year": 1952
}
This would pass a jsonschema that requires the keys "film" and "year" in the object.
But it's unsafe to use in an SQL expression, because of the apostrophe character in "Singin' in the Rain".
Suppose you were to use this JSON content unsafely, by interpolating it directly into an SQL query string:
// UNSAFE!
var sqlQuery = `INSERT INTO Films SET attributes = '${jsonDocument}'`
This will result in imbalanced quotes, and at best this causes an SQL syntax error when you execute the query.
INSERT INTO Films SET attributes = '{ "film": "Singin' in the Rain, "year": 1952 }'
^ error
There might be an opportunity for attackers to exploit it, but it's bad enough that it results in syntax errors.
The proper solution to most SQL injection problems is to use query parameters. Keep the javascript variables separate from the SQL query string. Instead, pass variables as parameters.
// SAFE!
var sqlQuery = `INSERT INTO Films SET attributes = ?`
db.query(sqlQuery, [jsonDocument]).then(...);
I am trying to build a where clause for an ArcGIS layer query in the ArcGIS JavaScript API. All the examples I can find (including in where documentation) encourage you to do things like this:
query.where = "NAME = '" + stateName + "'";
I find this surprising, because it seems like we are encouraged to write code that's susceptible to SQL injection bugs. This is especially important to me because my "stateName" is entirely not in my control. It's raw user input.
It looks like the library supports query parameterization via the parameterValues property, however I can find no examples on how to use it, or how to format my query so that it uses parameter values. Not even the documentation contains any examples.
So how do we create properly parameterized queries?
Side note: I recognize it's probably the server administrator's job to prevent bad queries from causing harm, however half the reason I'm asking this is also purely to avoid bugs and improperly parsed queries.
According to ESRI, the parameterValues property is only for using Query Layers. It's not for this context.
So the answer is, no, you can't use proper SQL parameterization when you're simply running a query on a layer.
The best way I have found to make your queries at least a little bit more bulletproof is to use code like this:
function sanitizeParameter(value) {
// We need to escape single quotes to avoid messing up SQL syntax.
// So all ' characters need to become ''
return value.split("'").join("''")
}
query.where = "NAME = '" + sanitizeParameter(stateName) + "'";
I'm currently writing an app that accesses google bigquery via their "#google-cloud/bigquery": "^2.0.6" library. In one of my queries I have a where clause where i need to pass a list of ids. If I use UNNEST like in their example and pass an array of strings, it works fine.
https://cloud.google.com/bigquery/docs/parameterized-queries
However, I have found that UNNEST can be really slow and just want to use IN on its own and pass in a string list of ids. No matter what format of string list I send, the query returns null results. I think this is because of the way they convert parameters in order to avoid sql injection. I have to use a parameter because I, myself want to avoid SQL injection attacks on my app. If i pass just one id it works fine, but if i pass a list it blows up so I figure it has something to do with formatting, but I know my format is correct in terms of what IN would normally expect i.e. IN ('', '')
Has anyone been able to just pass a param to IN and have it work? i.e. IN (#idParam)?
We declare params like this at the beginning of the script:
DECLARE var_country_ids ARRAY<INT64> DEFAULT [1,2,3];
and use like this:
WHERE if(var_country_ids is not null,p.country_id IN UNNEST(var_country_ids),true) AND ...
as you see we let NULL and array notation as well. We don't see issues with speed.
I am using mysql-node: https://github.com/mysqljs/mysql but I am a little confused about default sanitization, Mysql.Escape() vs Mysql.EscapeId() and the use of ? vs ??. The docs says?
Default Sanitization
When you pass an Object to .escape() or .query(), .escapeId() is used
to avoid SQL injection in object keys.
I see the term Object, so does that mean I should still escape queries like this?
UPDATE table SET updated_at = userInput WHERE name = userInput.
Mysql.Escape() vs Mysql.EscapeId()
What is the difference between these two functions. The docs says mysql.escape uses mysql.escapeId. I know they both sanitize input but is there a case where you use one or the other?
? vs ??
The docs use ? and ?? interchangeably. Do they mean the same thing?
The documentation describes what escape() and escapeId() do. Use escape() when you need to escape values. Use escapeId() when you need to escape identifiers (e.g., table, database, or column names).
I talked to the maintainer of mysqljs and confirmed that this package doesn't escape queries by default.
When you pass an Object to .escape() or .query(), .escapeId() is used
to avoid SQL injection in object keys.
The statement above means queries will be escaped if you use one of the following methods.
let args = { name: 'myname' otherVals: 'blah blah'};
mysql.query('Insert INTO table SET ?',args);
let name = 'test';
mysql.query('INSERT INTO table SET name = ?', [name]);
For the second part of the questions: ?? and escapeId is used for identifiers and ? and escape() is used for values.
TLDR
Is there a way to limit queryByExample to a collection in NodeJS?
Problem faced
I have a complex query with some optional fields (i.e. sometimes some search fields will be omitted). So I need to create a query dynamically, e.g. in JSON. QueryByExample seems to be the right tool to use here as it gives me that flexibility to pass a JSON. However my problem is that I would like to limit my search to only one collection or directory.
e.g. I was hoping for something like
searchJSON = {
title: { $word: "test" },
description: { $word: "desc" }
};
//query
db.documents.query(qb.where(
qb.collection("collectionName"),
qb.byExample(searchJSON)
)).result()...
In this case searchJSON could have been built dynamically, for example maybe sometimes title may be omitted from the search.
This doesn't work because the query builder only allows queryByExample to be the only query. But I'd instead like to built a dynamic search query which is limited to a collection or directory.
At present, I think you would have to express the query with QueryBuilder instead of Query By Example using
qb.and([
qb.collection('collectionName'),
qb.word('title', 'test'),
qb.word('description', 'desc')
])
See http://docs.marklogic.com/jsdoc/queryBuilder.html#word
That said, it should be possible for the Node.js API to relax that restriction based on the fixes in MarkLogic 9.0-2
Please file an issue on https://github.com/marklogic/node-client-api