Error using mysql in an AWS Lambda function - javascript

I have created an AWS Lambda function to retrieve info from an AWS RDS Database. I have also created an API in AWS API Gateway that triggers the Lambda function. The API works fine when my SQL statement is "select * from user". However, when I try something like "select * from user with tag = people", I get this error:
{"errorType":"Error","errorMessage":"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tag = 'people'' at line 1","trace":["Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tag = 'people'' at line 1"," at PromisePool.query (/var/task/node_modules/mysql2/promise.js:330:22)"," at Runtime.module.exports.handler (/var/task/index.js:19:38)"," at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"]}
My AWS Lambda function defined as (Node.js 12.x):
const mysql = require('mysql2');
const pool = mysql.createPool({
host: process.env.LAMBDA_HOSTNAME,
user: process.env.LAMBDA_USERNAME,
password: process.env.LAMBDA_PASSWORD,
database: process.env.LAMBDA_DATABASE,
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
module.exports.handler = async (event, context, callback) => {
context.callbackWaitsForEmptyLoop = false;
const tag = event["params"]["querystring"]["tag"]
const sql = "select * from user with tag = " + tag;
const promisePool = pool.promise();
const [rows] = await promisePool.query(sql);
const lambdaProxyResponse = {
statusCode: 200,
body: JSON.stringify([rows])
};
return lambdaProxyResponse;
};
Can someone help me out with this issue? Also when I run the command in mysql workbench, it executes correctly.

I'm not positive what your overall environment looks like but first and foremost it's not a great idea to compose your own SQL this way. This code could potentially be subject to a SQL injection attack.
But, assuming you trust the input, it's a pretty simple fix. You need quotes:
const sql = "select * from user with tag = '" + tag + "'";
Note that this is true when the column is a string type. On an numeric type you would not need the quotes.
If you don't trust the input then you need to look at using prepared statements of some sort.

Related

how to sanitize sql.js inputs?

I am using sql.js to manage the SQLite file I created for an electron app. My issue is that I want to make sure all the inputs are sanitized. As of now, I am using statements like so:
const SQL = await sqljs();
const db = new SQL.Database();
// These values are just examples and will be user inputs through electron.
let id = 1;
let name = 'row name';
db.run(`INSERT INTO tableName VALUES (${}, 'hello');`);
I'm pretty sure that this way is unsafe and can cause SQL injections. What can I do to prevent such a problem? Thank you kindly.
You can use bound parameters. These are values that are passed to the database engine and not parsed as part of a SQL statement:
let id = 1;
let name = 'row name';
/* Either pass in a dictionary to use named bound parameters */
db.run("INSERT INTO tableName VALUES(:id, :name)", { ':id': id, ':name': name });
/* Or an array to use positional bound parameters */
db.run("INSERT INTO tableName VALUES(?, ?)", [id, name]);
More information is available in the SQLite documentation, as well as sqljs documentation.

how should I apply sync node.js module with mysql, socket functions?

I'm trying to understand and use sync-npm module, but not sure how to change my functions below to match sync format... (https://www.npmjs.com/package/sync)
Basically I'm trying to use input data (which is formed as a list in client side) I receive from frontend(client) and send it to node.js via socket. I tried to store it in my global variable 'query', but I learned that it doesn't get updated. So when I tried to print 'query' outside of socket function, it doesn't work.
It sounds like I should use sync module, but I'm not quite sure how to implement that in my code...If anyone could give me an idea how to change my functions below, it would be great..thanks!
Receiving input data from frontend and sending it to node.js via socket
var server = app.listen(3001);
var socket = require('socket.io');
var io = socket(server);
var query = []
// Register a callback function to run when we have connection
io.sockets.on('connection',newConnection);
function newConnection(socket){
console.log('new connection: ' + socket.id);
socket.on('search', newSearch);
function newSearch(final){
query.push(final)
console.log(query[0]);
console.log(Array.isArray(query[0])); //returns True
console.log(query[0][0]); // this also works
console.log(query[0][1]);
}
}
console.log('print');
console.log(query);
// this only gets printed in the beginning as an empty array
Ultimately, I'm parsing that list of input data and concat into my sql select phrase. Below is my DB portion code:
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '~~~',
user : '~~~',
password : '~~~',
database : '~~~'
});
connection.connect();
console.log('mysql connected');
var sql = 'select' + '*' + 'from EBN ';
//ideally data in the 'query' list will concat with this sql string
connection.query(sql,function(err,rows,fields){
if(err){
console.log(err);
}
else{
fs.writeFileSync('search.json', JSON.stringify(rows), 'utf8');
}
});
Firstly, you should wrap the code that does the query execution in a function, something like
var mysql = require('mysql');
var connection = mysql.createConnection({
host : '~~~',
user : '~~~',
password : '~~~',
database : '~~~'
});
connection.connect();
console.log('mysql connected');
function executeQuery(query)
var sql = 'select' + '*' + 'from EBN ';
//do something with query and add it to the sql string
connection.query(sql,function(err,rows,fields){
if(err){
console.log(err);
}
else{
fs.writeFileSync('search.json', JSON.stringify(rows), 'utf8');
}
}
}
This is necessary because you don't want to execute a query right after starting the server, but only after you received the query message via socket. So now you can call executeQuery(final) in your socket message handler.
You should learn how asynchronous programming and callbacks works in nodejs/javascript, and why you should use it as much as possible for server applications (e.g. use fs.writeFile instead of writeFileSync). You can use packages like sync to make life easier for you, but only when you know exactly what you want to do. Just throwing something with the name 'sync' in it at a problem that might be caused by asynchronicity is not going to work.

Prevent SQL Injection in JavaScript / Node.js

I am using Node.js to create a Discord bot. Some of my code looks as follows:
var info = {
userid: message.author.id
}
connection.query("SELECT * FROM table WHERE userid = '" + message.author.id + "'", info, function(error) {
if (error) throw error;
});
People have said that the way I put in message.author.id is not a secure way. How can I do this? An example?
The best way to is to use prepared statements or queries (link to documentation for NPM mysql module: https://github.com/mysqljs/mysql#preparing-queries)
var sql = "SELECT * FROM table WHERE userid = ?";
var inserts = [message.author.id];
sql = mysql.format(sql, inserts);
If prepared statements is not an option (I have no idea why it wouldn't be), a poor man's way to prevent SQL injection is to escape all user-supplied input as described here: https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet#MySQL_Escaping
Use prepared queries;
var sql = "SELECT * FROM table WHERE userid = ?";
var inserts = [message.author.id];
sql = mysql.format(sql, inserts);
You can find more information here.
Here is the documentantion on how to properly escape any user provided data to prevent SQL injections: https://github.com/mysqljs/mysql#escaping-query-values .
mysql.escape(userdata) should be enough.

How to properly generate Facebook Graph API App Secret Proof in Javascript

I am making a server side Facebook Graph API call to the all_mutual_friends edge: https://developers.facebook.com/docs/graph-api/reference/user-context/all_mutual_friends/
The call works when the two users are friends, but returns no useful data when they users aren't friends. According to the docs, this is because I must sign the call with the appsecret_proof parameter. No matter what I try, I am not able to successfully pass this parameter. I am using jsrsasign running on Parse. I have tried every configuration of using the access token as the message and my appSecret as the key, and vice versa. I have also tried multiple combinations of utf8 and hex. Every time I receive the error: invalid appsecret_proof provided in the API argument
Code:
var Signer = require("cloud/vendor/jsrsasign/lib/jsrsasign.js");
var userId = request.params.userId;
var accessToken = request.params.accessToken;
var appSecret = "redactedStringPastedFromFacebook";
var signer = new Signer.Mac({alg: "hmacsha256", pass: appSecret});
var appSecretString = signer.doFinalString(accessToken);
var appSecretHex = signer.doFinalHex(accessToken);
var graphRequestURL = "https://graph.facebook.com/v2.5/" + userId;
var fields = "?fields=context.fields(all_mutual_friends.fields(name,picture.width(200).height(200)))";
//var authorization = "&access_token=" + accessToken; //this works, but only for existing friends
var authorization = "&access_token=" + accessToken + "&appsecret_proof=" + appSecretHex;
return Parse.Cloud.httpRequest({
url: graphRequestURL + fields + authorization,
method: "GET",
})
Most examples I have seen are in PHP or Python and the crypto routines are a bit more clear. This works in that both appSecretString and appSecretHex don't throw errors and look reasonable, however the values are always rejected by Facebook.
Notes:
I have triple checked the App Secret value provided by Facebook
I have been approved by Facebook to use the all_mutual_friends feature, which is a requirement for this particular call
I am using Parse, which isn't Node, and can't use NPM modules that have external dependencies, which is why I am using jsrsasign. I also tried using CryptoJS directly, but it is no longer maintained and doesn't have proper module support and jsrsasign seems to wrap it anyway.
Here it is:
import CryptoJS from 'crypto-js';
const accessToken = <your_page_access_token>;
const clientSecret = <your_app_client_secret>;
const appsecretProof = CryptoJS.HmacSHA256(accessToken, clientSecret).toString(CryptoJS.enc.Hex);

sequelizejs saving an object when row was removed

I have the following code. The idea is that I update a database row in an interval, however if I remove the row manually from the database while this script runs, the save() still goes into success(), but the row is not actually put back into the database. (Because sequelize does an update query with a where clause and no rows match.) I expected a new row to be created or error() to be called. Any ideas to what I can do to make this behave like I want to?
var Sequelize = require("sequelize")
, sequelize = new Sequelize('test', 'test', 'test', {logging: false, host: 'localhost'})
, Server = sequelize.import(__dirname + "/models/Servers")
sequelize.sync({force: true}).on('success', function() {
Server
.create({ hostname: 'Sequelize Server 1', ip: '127.0.0.1', port: 0})
.on('success', function(server) {
console.log('Server added to db, going to interval');
setInterval(function() { console.log('timeout reached'); server.port = server.port + 1; server.save().success(function() { console.log('saved ' + server.port) }).error(function(error) { console.log(error); }); }, 1000);
})
})
I'm afraid what you are trying to do is not currently supported by sequelize.
Error callbacks are only ment for actual error situations, i.e. SQL syntax errors, stuff like that. Trying to update a non-existing row is not an error in SQL.
The import distinction here is, that you are modifying your database outside of your program. Sequelize has no way of knowing that! I have two possible solutions, only one of which is viable right now:
1 (works right now)
Use sequelize.query to include error handling in your query
IF EXISTS (SELELCT * FROM table WHERE id = 42)
UPDATE table SET port = newport WHERE id = 42
ELSE
INSERT INTO table ... port = newport
Alternatively you could create a feature request on the sequelize github for INSERT ... ON DUPLICATE KEY UPDATE syntax to be implemented see and here
2 (will work when transactions are implemented
Use transactions to first check if the row exists, and insert it if it does not. Transactions are on the roadmap for sequelize, but not currently supported. If you are NOT using connection pooling, you might be able to acomplish transactions manually by calling sequelize.query('BEGIN / COMMIT TRANSACTION').

Categories