How to separate login credentials from user data in mysql and nodejs? - mysql

I was curious when I been using Django and found that the user data and authentication credentials were in different tables. Unfortunately I never understood how this worked, but I imagine through relationships.
My database is in MySQL and I have created the users table with the user data, then I have created another table called auth with its respective field, id, salt and hash which is what I am interested in saving. I have also created a field called user_id to relate to the user in the users table with the id (I don't know if it's fine like that).
Now, I have a function in my NodeJS code that is responsible for saving the data received by the network layer:
function insert (table, data) {
return new Promise((resolve, reject) => {
connection.query(`INSERT INTO ${table} SET ?`, data, (err, data) => {
if (err) return reject(err)
resolve(data)
})
})
}
has that structure so that it is reusable at all times.
On the other hand I have the controller that handles the business logic of the data received by the network layer:
function insert (user) {
if (!user) {
throw dataMissed
}
bcrypt.genSalt(1000, function(err, salt) {
if (err) throw err
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) throw err
user = {
name: user.name,
email: user.email,
position: user.position,
hash,
salt
}
return store.insert(collection, user)
})
})
}
Unlike the function that is dedicated to saving the data in the database, this one is unique since the logic will depend on the data, obviously.
And the problem I have is that I don't know how to save the generated salt and the hash in the corresponding table. As you can see, I use the same function to save both but it gives me an error, since it is a logic that does not work.
How do I save a value that will later be referenced by another table?

I think the problem is that your 2nd insert function expect user and it also expects user to have password when in reality user should not have password. You need to have 2 DALs, one to control user table and the other to control the credentials Then in your service layer you do your logic using waterfall since credentials DAL depends on user's id
Here is pseudo code of what I mean
Userdal.js
/*
* User should look something like
* {
* name: "xyz",
* age: 40,
* email: "test#test.com"
* etc...
* }
* Basically it has non sensitive information nothing has to do with credentials.
*/
function insert(user) {
/// does insert logic
return store.insert(collection, user) //or resolve, whatever works for you
}
CredentialsDal.js
/*
* creds should look something like
* {
* username: "aaaa",
* password: "asfa",
* userId: "1234567"
* }
*/
function insert(creds) {
// Do your logic here for creds, for example
bcrypt.genSalt(1000, function(err, salt) {
if (err) throw err
bcrypt.hash(creds.password, salt, function (err, hash) {
if (err) throw err
const sanitizedCreds = {
username: creds.username,
salt: salt,
hash: hash
// Literally anything you can credential goes here.
}
return store.insert(collection, sanitizedCreds)
})
})
}
UserService.js
// This is a nice library this gives things such as waterfall, series, etc... (you dont need to use it though)
const asyncx = require('async')
const credsDals = require('./dals/CredentialsDal')
const userDal = require('./dals/UserDal')
function register(userData, credsData) {
asyncx.waterfall([
// Create user
(callback) => {
let user = userDal.insert(userData)
callback(null, user.id)
},
// Create credentials
(userId, callback) => {
let creds = credsDal.insert(credsData)
callback(null, true)
}
], (err, res) => {
// Handle your result here
})
}
Controller.js
const UserService = require('./Services/UserService')
function register(req, res) {
const userData = req.body.user
const credsData = req.body.credentials
UserService.register(userData, credsData)
res.status(200).end()
}
finally your router
const controllers = require('./controllers')
router.post('/user', controllers.register)
The above code is missing some pieces I left for you to fill, such as actual logic implementation, callbacks and returns, promises, etc...
Note: such separation is really important and nice to have since it create a secure code. whenever you request a specific user you don't want to send all of their sensitive information with the response. For example, imagine you tap on your friend's user profile on facebook and all of the sudden that facebook frontend request will bring back even the user's credentials even if they are not rendered they are still being requested and can be sniffed! so these sensitive information are stored on a separate table if not separate server all together. In cases such as credit cards, etc... they have different servers that can only accept requests from the same network and bans all other requests, this way it makes it harder for anyone to make any request to those servers, basically you have to be inside the server's network to make that request.
Link to repl.it: https://repl.it/repls/BlindCuddlyUserinterface#index.js

Related

Having multiple get routes on a NodeJS / Express REST API?

I'm creating a JS web app using NodeJS and Express (with SQL/MySQL for the database), pretty much directly implementing this API tutorial: https://www.bezkoder.com/node-js-rest-api-express-mysql/ (just replacing 'tutorials' with 'Employees').
I'm trying to write API functions to get all Employees with certain attributes (in the SQL table), for example all employees with lastName = "Garcia" or all employees with teamID = 43682, etc.
In my routes.js file I have this:
module.exports = app => {
const employees = require("../controllers/employee.controller.js");
const router = require("express").Router();
// Create a new Employee
router.post("/", employees.create);
// Retrieve all Employees
router.get("/", employees.findAll);
// Retrieve all Employees with lastName
router.get('/', employees.findLastName);
... a bunch more CRUD functions ...
app.use('/api/employees', router);
};
And this is the corresponding Controller function:
exports.findLastName = (req, res) => {
const lastName = req.query.lastName; // tried changing req.query.lastName to req.params.lastName
Employee.getLastName(lastName, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Error occurred while retrieving by last name."
});
else {
console.log(`Employees with lastName ${lastName} were found!` );
res.send(data);
}
});
};
exports.findAll = (req, res) => {
const title = req.query.title;
Employee.getAll(title, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while retrieving employees."
});
else {
console.log(`Employee with ${title} title was found!` );
res.send(data);
}
});
};
The findAll route/function (just copied from that tutorial) works by finding all Employees with a certain ID number (the primary key in the DB) and I know that works from testing it through Postman. I wrote the findLastName route/function by copying the findAll function and changing it to search by lastName, and making the corresponding functions in the model and controller classes.
The new function, findLastName, doesn't work... unless I put the route before the findAll route (or comment it out). Then it correctly calls all my functions and returns all employees with the lastName param.
What's actually happening here? Are you not allowed to have multiple .get() routes or something? If so, how would I implement a search API like this? This is my first time building a web app like this so admittedly I'm still a little hazy on how routing and all that works. Thank you for any help though!
In Express whenever the first route matches second will be ignored, so in your scenario you have two route.get with same path /
router.get('/', employees.findAll);
//Since route with path `/` is matched above already this will be ignored
router.get('/', properties.findLastName);
In order to find Employees with last name you will need to create a new route with param (param will contain the last name)
router.get('/:lastName', properties.findLastName);
You can access the param value like this req.params.lastName in controller

Nodejs Crypto encrypted string not decrypting correctly after storing in MySQL

I have a simple nodejs application that is storing tokens from an external service, its unsafe to store them as plain strings in the event of database compromise so I need to encrypt them.
copying and pasting the following github gist into my application simple-nodejs-iv-encrypt-decrypt.js
I am able to successfully encrypt and decrypt my strings, however once saving the encrypted string to MySQL it no longer decrypts into a matching string
my MySQL column is formatted encryptedToken VARCHAR(255)
// before storing to database
{
encryptedToken: 'OKWLlYEsCtddWQOL8ezQBI+whtU30gVs67nGiRLxxca10Y4AELjMZN3afVzuys17leE9U9Ski+fByaEXFTXnefDUdyR4PUwJBi6poY1RHOY=',
decryptedToken: 'Z4XkR0vkrbAO6LzmaYGYa0dnaaxvlkIme27L-GlPB7l6M4gkikz1S_vTfJyCUJMx'
}
// after storing to database
{
encryptedToken: 'OKWLlYEsCtddWQOL8ezQBI+whtU30gVs67nGiRLxxca10Y4AELjMZN3afVzuys17leE9U9Ski+fByaEXFTXnefDUdyR4PUwJBi6poY1RHOY=',
decryptedToken: ':D�\b�O3Qlס��,,\u0017aYGYa0dnaaxvlkIme27L-GlPB7l6M4gkikz1S_vTfJyCUJMx'
}
the algorithm used is aes256 and the encoding is base64
I believe this is happening because you're using a different IV (Initialization Vector) each time.
The encryptionHelper function getKeyAndIV creates a random IV each time you call it, so decryption will not be deterministic.
If you ensure you're using the same IV each time, the decrypted token should be the same as well.
I've tested this out like so:
SQL
create table tokens (encryptedToken varchar(255))
Javascript / Node.js
const encryptionHelper = require("./simple-nodejs-iv-encrypt-decrypt.js")
// This could be anything
const token = "abcdefghijklmonp";
// We're using fixed values here. In reality you could use a different IV for each row, it's ok to store this in the database.
const key = Buffer.from("MTIzNDU2Nzg5MGFiY2RlZmdoaWprbG1ub3BxcnN0dXY=", "base64");
const iv = Buffer.from("26vFZGhH66xFszo59pEaWA==", "base64");
const encryptedToken = encryptionHelper.encryptText(encryptionHelper.CIPHERS.AES_256, key, iv, token, "base64");
// con should be initialized with a connection to the relevant db.
con.query("insert into tokens (encryptedToken) values (?)", [encryptedToken ], (error, results) => {
if (error) {
console.error("Insert query failed: ", error);
} else {
console.log("Token insert successful!");
}
});
con.query("select * from tokens", (error, results) => {
if (error) {
console.error("Select query failed: ", error);
return;
}
console.log("Tokens (encrypted):", results.map(r => r.encryptedToken));
console.log("Tokens (decrypted):", results.map(r => encryptionHelper.decryptText(encryptionHelper.CIPHERS.AES_256, key, iv, r.encryptedToken, "base64").toString("base64")));
});
// Let's just ensure con is closed
setTimeout(() => {
con.end()
}, 100);

How can I safely use user supplied text in JSON?

I have a Node application that needs to take user input(a username and password) and make a request to another API with those two things included. I have a function that should do this:
getTokenFromLogin = function(user, pass) {
var promise = new Promise(function(resolve, reject) {
var options = this.options;
options.url = endpoints.login;
options.method = 'POST';
options.form = {
'username': user,
'password': pass
};
request(options, function (err, httpResponse, body) {
if (err) return reject(err);
resolve(body);
});
});
return promise;
};
However, theoretically, couldn't an attacker just input a string in JSON format that could manipulate my request object? If this is the case, how can I ensure that these strings are safe? I've seen two methods, JSON.parse() and JSON.stringify(), but nothing I've found clarifies what I should use and exactly how those methods could stop this kind of vulnerability.
The answer is your code is already "safe." Strings are strings (assuming you are checking the typeof user and pass of course) and strings cannot magically interact with objects by themselves.
Now if the server you're sending the request to is poorly designed/coded and does not properly sanitize its inputs (user and pass) when inserting into a database for example, then that's an entirely different scenario. However for making the request, there is no security issue here.

Mongoose Populate with express res.json() breaks

So I'm selecting Activities from the mongodb and populating User for each.
var query = Activity.find(query).populate("user");
return query.sort({created:"desc"}).exec(function(err, activities) {
debugger;
if (!err) {
return res.json(activities);
} else {
res.status(400).json(err);
}
});
As you can see I have a debugger; breakpoint is there, When I'm pring activities it prints an array of activities with the user object populated.
Also when I'm calling something like activities[0].toJSON() I get everything good!
But the response comes back with the user property empty !
I looked into the source of express.response.json(OBJ) and saw this line:
var body = JSON.stringify(val, replacer, spaces);
val is my activities
When calling JSON.stringify(activities) it will create a json with an empty user field.. any suggestions ?
Try the lean option. That gives back plain JS objects with no mongoose weirdness. Also, your error handling seems a little awkward, can be simplified.
var query = Activity.find(query).populate("user");
query.sort({created:"desc"}).lean().exec(function(err, activities) {
if (err) return res.status(400).json(err);
res.json(activities);
});
I would go even further, not hard-coding error sending in routes but simply passing along via if (err) return next(err) to error-handling middleware defined elsewhere in your app. You can still set the status, then use detection in your middleware, something like this:
app.use(function(err, req, res, next){
err.status = err.status || 500;
res.status(err.status).json(err);
});

sails.js: how to update model

Forgive my noob question. I'm using angularjs to send a user model (json) with varying fields. It works well with sails.js default PUT. I overrode the PUT, the problem is that I wish to update the model with the received JSON and do some processing on the modified model. Now I can't update the model with
User.update({
id: req.body.id
},{
req.body
}, function(err, users) {
// Error handling
if (err) {
return console.log(err);
// Updated users successfully!
} else {
console.log("Users updated:", users);
}
});
Please help
EDIT:
After knocking my head on the wall for days, problem solved! I know, my code formatting here is not the best..
changed this:
{
req.body
}
to just:
req.body
(without the braces)
full snippet becomes:
User.update({
id: req.body.id
},
req.body
, function(err, users) {
// Error handling
if (err) {
return console.log(err);
// Updated users successfully!
} else {
console.log("Users updated:", users);
}
});
Thanks.
So you figured out your problem, sort of. req.body is already an object. But you really should sanitize it before you put it into your update and then save the object. There's a lot of reasons for this but with Mongo when you get only a partial object you'll replace the object in the collection which, in your example with a user, could be bad. When I send users to the frontend I cull off things I don't want transmitted all over like passwords. The other reason is the golden rule of web application development - never trust the client! I'd start with something like:
var user = User.findOne(req.body.id).done(function(error, user) {
if(error) {
// do something with the error.
}
if(req.body.email) {
// validate whether the email address is valid?
// Then save it to the object.
user.email = req.body.email;
}
// Repeat for each eligible attribute, etc.
user.save(function(error) {
if(error) {
// do something with the error.
} else {
// value saved!
req.send(user);
}
});
});