node-postgres prepared statement - sql injection - node.js

I am new to node-postgres and am unable to resolve this error when I try to ensure no sql injection is possible with my prepared statement.
Here is a snippet of the code
// the prepared statement
var preparedstatement = client.query({
text: "select ST_AsText(ST_Transform(geodata,4326)) from table_name where ST_Contains(ST_GeomFromText($1,4326),table_name.geodata)",
values: ["POINT(Lat Long)"],
name: 'where'
});
// the query
var queryresult = client.query({name: 'where', values: [msg]},["'; DROP TABLE user;"], function(err) {
if (err) {
socket.emit('query error', String(err));
}
});
Whenever I enter the geodata (as a message from the client using socket.io), the socket.emit returns an error saying
Invalid geometry
However the code works fine when I remove ["'; DROP TABLE user;"], from the code i.e.
// the query
var queryresult = client.query({name: 'where', values: [msg]}, function(err) {
if (err) {
socket.emit('query error', String(err));
}
});
(above) works perfectly. Any help in helping me understand what I am doing wrong here would be great.

var preparedstatement = client.query({
text: "select ST_AsText(ST_Transform(geodata,4326)) from table_name where ST_Contains(ST_GeomFromText($1,4326),table_name.geodata)",
values: ["POINT(Lat Long)"],
name: 'where'
});
results to SQL smth like
prepare "where" as
select ST_AsText(ST_Transform(geodata,4326))
from table_name
where ST_Contains(ST_GeomFromText($1,4326),table_name.geodata);
execute "where" (POINT(Lat Long));
which probably could work if lat nad long are table_name attributes
next:
var queryresult = client.query({name: 'where', values: [msg]}, function(err) {
if (err) {
socket.emit('query error', String(err));
}
});
does:
execute "where" (msg_value);
which probably works if they are of compatible data type
and finally:
var queryresult = client.query({name: 'where', values: [msg]},["'; DROP TABLE user;"], function(err) {
if (err) {
socket.emit('query error', String(err));
}
});
runs SQL:
execute "where" ('''; DROP TABLE user;');
which gives an error as this text is not a valid geometry...
noticable here that lient.query(text QUERY,array VALUES) here is used as lient.query(object QUERY,array VALUES) and VALUES overcame such in QUERY object, this your [msg] was "ignored"...
NB
checking if prepared statements are prune to such sql injection is pointless as this feature was implemented with idea to be safe for such injections. For example even if you would use data type text (to avoid type mismatch) and try to inject semicolon and drop statement, prepared statement would treat injection as literal value and thsus be safe. eg:
var preparedstatement = client.query({
text: "select $1::text resulting_att",
values: ['some default'],
name: 'ps_name'}
);
var queryresult = client.query({name: 'ps_name'},["'; DROP TABLE user;"], function(err,res) {
console.log(err,res.rows)
client.end()
});
logs:
null [ anonymous { resulting_att: '\'; DROP TABLE user;' } ]
and not tries to drop anything.

Related

Getting Bound Statement from Snowflake SQL Connection

I am trying to find a way to get the sql statements with the bound parameters to print.
I have a snowflake connector object that is similar to this:
snowflake.execute({
sqlText: 'SELECT * FROM TBL_A WHERE A B ?',
binds: [12],
fetchAsString: ['NUMBER'],
complete: function (err, stmt, rows) {
if (err) {
console.error('Failed to execute statement due to the following error: ' + stmt.getSqlText())
}
else {
// Do some stuff
}
}
})
When it errors out, I want it to show me the actual SQL text being executed:
'SELECT * FROM TBL_A WHERE A B 12'
However, the only thing I can get it to print is the sql with the parameter holder.

Javascript promise to iterate/include dynamic number of Arguments

I'm using the mssql npm module (with Tedious driver) to read/write to Azure Sql database from my node Server. https://www.npmjs.com/package/mssql
All the examples I've found provide an hardcoded example of the query whether to read or write records, like this:
var insertRecordIntoTable = function (callback) {
sql.connect(dbConfig).then(pool => {
return pool.request()
.input('ID', sql.Int, 210)
.input('Name', sql.NVarChar, "John Doe")
.input('EmailAddress', sql.NVarChar, "test#test.com")
.query("INSERT INTO Accounts (ID, Name, EmailAddress) VALUES (#ID, #Name, #EmailAddress)")
}).then(result => {
console.dir(result)
callback(result);
}).catch(err => {
// ... error checks
console.log("Error occured: " + err);
callback(err);
});
}
Obviously, I'd like to write one standard method to write records to any table in the database.
Now I can fetch structure of each table and use that to find how what datatype each field should be from the key in jsonRecord and write something like this:
var insertRecordIntoTable = function (jsonRecord, tableName, callback) {
let arrKeys = jsonRecord.allKeys();
let columnNames = getCommaSeparatedColumnNames(arrKeys);
let valuePlaceholders = getValuePlaceholdersForSql(arrKeys);
sql.connect(dbConfig).then(pool => {
return pool.request()
// how do I write something like this so that dynamic number of fields and values get populated in the query inside this promise.
// I'm open to methods without promise as well.
for(let x=0; x < arrKeys.length; x++){
let key = arrKeys[x];
// .input('ID', sql.Int, 210)
.input(key, getTypeForKey(key, tableName), jsonRecord[ key ] )
}
.query("INSERT INTO " + tableName + " (" + columnNames + ") VALUES (" + valuePlaceholders + ")")
}).then(result => {
console.dir(result)
callback(result);
}).catch(err => {
// ... error checks
console.log("Error occured: " + err);
callback(err);
});
}
function getTypeForKey(key){. // looks up table schema and returns keyType }
function getCommaSeparatedColumnNames(arrKeys){ return arrKeys.join(", "); }
function getValuePlaceholdersForSql(arrKeys){ // write code to append '#' before every key and then join using comma's and return that string }
I'm sure node.js writing to SQL is a fairly common functionality and there may be better ways to achieve what I'm trying to do here. Please feel free to go a different route.
P.S. - Although I should say that I prefer mssql over Tedious package. It just seems better in functionality after going through the documentation in the last several hours.
If you want to interact with your database without creating all the queries by yourself, you can use a query builder like knex to manage the data as objects:
knex('Accounts').insert({ID: 210, Name: "John Doe", EmailAddress: "test#test.com"})
Would be similar to:
insert into `Accounts` (`EmailAddress`, `ID`, `Name`) values ('test#test.com', 210, 'John Doe')
Also I see you are checking types. If you need validation, maybe a complete ORM (I like Objection.js) would be a good choice.

NodeJS and pg-promise, insert dynamically from JSON-object

I'm running NodeJS and pg-promise, and are trying to accomplish somethings like:
db.none('INSERT INTO my-table (JSON-object-keys) VALUES ($1)', [JSON-object-values])
.catch(function(err) {
console.log('Error on insert into my-table: ' + err);
});
I have JSON-objects which can look like:
{"column1":"value1", "column2":"value2", "column3":"value3"}
{"column2":"value2", "column3":"value3"}
{"column1":"value1", "column3":"value3"}
I would like to have the INSERTS automatically generated corresponding to what the JSON-object contains.
Is that possible in an elegant way?
Explained a bit more, in the 3 examples of JSON the following should be generated:
db.none('INSERT INTO my-table (column1, column2, column3) VALUES ($1, $2, $3)', [value1, value2, value3])
.catch(function(err) {
console.log('Error on insert into my-table: ' + err);
});
db.none('INSERT INTO my-table (column2, column3) VALUES ($1, $2)', [value2, value3])
.catch(function(err) {
console.log('Error on insert into my-table: ' + err);
});
db.none('INSERT INTO my-table (column1, column3) VALUES ($1, $2)', [value1, value3])
.catch(function(err) {
console.log('Error on insert into my-table: ' + err);
});
Your pgp object + the input object with all the properties:
var pgp = require('pg-promise')({
capSQL: true // capitalize all generated SQL
});
var inputObj = {
/* all your optional properties */
};
Define the raw-text type, using Custom Type Formatting:
var rawText = text => ({_rawType: true, toPostgres: () => text});
Create a generic default column, according to class Column:
var defCol = name => ({name, def: rawText('DEFAULT')});
// which is the same as:
var defCol = name => new pgp.helpers.Column({name, def: rawText('DEFAULT')});
Generate the list of default-able columns:
var cols = Object.keys(inputObj).map(defCol);
Create a ColumnSet with those columns:
var cs = new pgp.helpers.ColumnSet(cols, {table: 'my-table'});
When it is time to generate an insert query, you can do:
var insert = pgp.helpers.insert(inputObj, cs);
Recommended Approach
If you know the columns in advance, then you should just do the following:
var cs = new pgp.helpers.ColumnSet(
[defCol('column1'), defCol('column2'), defCol('column3')],
{table: 'my-table'});
A static cs object will always provide a much better performance.
This approach is also safer, because you do not need to verify whether there is at least one property in the object, 'cos if there isn't, you'll get an error saying that it is impossible to generate an insert when there are no columns.
And this approach also works with multi-row inserts, which is very important. See also: Multi-row insert with pg-promise.

PostgreSQL ERROR: relation does not exist on INSERT Statement

I am trying to have my code INSERT a row into my table called thoughtentries. It is in the public schema. I am able to run ths command while connected to my database using psql:
INSERT INTO thoughtentries VALUES('12/17/2016 14:10', 'hi');
The first column is of character type with length 17. The second column is of type text.
When I have my code attempt to INSERT using the same command above I get the error in my log:
ERROR: relation "thoughtentries" does not exist at character 13
STATEMENT: INSERT INTO thoughtentries VALUES('12/17/2016 14:11', 'hi');
I am using pg and pg-format to format the command. Here is my code to do this:
client.connect(function (err) {
if (err) throw err
app.listen(3000, function () {
console.log('listening on 3000')
})
var textToDB = format('INSERT INTO thoughtentries VALUES(%s, %s);', timestamp, "'hi'")
client.query(textToDB, function (err, result) {
if (err) {
console.log(err)
}
console.log(result)
client.end(function (err) {
if (err) throw err
})
})
})
How do I go about fixing this?
Have you verified that the table was, in fact, created in the public schema?
SELECT *
FROM information_schema.tables
WHERE table_name = 'thoughtentries';
Once you have verified that, I see two possible explanations remaining:
You are connecting to a different database by mistake. Verify, in the same session, with:
select current_database();
Your search_path setting does not include the public schema. If that's the case, you can schema-qualify the table to fix: public.thoughtentries
How does the search_path influence identifier resolution and the "current schema"
Aside: Save timestamps as data type timestamp, not character(17).
Actually, don't use character(n) at all:
Any downsides of using data type "text" for storing strings?

Values of the results object is null when quering a database in node.js

So I'm trying to query a record from a database and then put it into xml format in node.js. The programname is the primary key of the sasinfo table, so it's guaranteed that I'll only be working with one record. The problem is that when I run the code below, console.log(messagetoclient) prints this:
<messagetoclient><programname>undefined</programname><comment>undefined</comment><guid>undefined</guid></messagetoclient>
However, console.log(results) prints the this (the correct values from the record):
[ { programname: 'helloworld',
comment: 'testing',
GUID: '9b23e0f7b7da4535b99f706301539a44' } ]
Could someone help me figue out why the values of the key value pairs aren't being printed? Thanks.
query2 = connection.query('SELECT * FROM sasinfo WHERE programname = ?', [programname], function(err, results) {
if(err){
console.log(err);
}
else{
console.log(results);
messagetoclient= '<messagetoclient><programname>'+results.programname+'</programname><comment>'+results.comment+'</comment><guid>'+results.GUID+'<guid></messagetoclient>';
console.log(messagetoclient);
}
});
Try
messagetoclient= '<messagetoclient><programname>'+results[0].programname+'</programname><comment>'+results[0].comment+'</comment><guid>'+results[0].GUID+'<guid></messagetoclient>';
since results is an array.

Resources