I'm trying to set up some security middleware for my humble little MERN web app, and I'm currently using helmet and express-mongo-sanitize, specifically for protection against NoSQL injection attacks.
I've set it up, however, as below in my server.js file:
const express = require('express')
const helmet = require('helmet')
const mongoSanitize = require('express-mongo-sanitize')
...
app.use(mongoSanitize())
app.use(helmet())
// Routes below
...
I've tried to test it by making a mock sign up like:
username: {"$gt": ""}
password: 'TestPassword'
so that req.body would be:
{
username: '{"$gt": ""}',
password: 'TestPassword'
}
but express-mongo-sanitize doesn't seem to be catching it and it goes through to my database. Am I misunderstanding something? The value of the username key is a string, so maybe it's already OK? Please forgive my ignorance, I'm learning.
What express-mongo-sanitize does is sanitize keys that start with a dollar sign.
username: '{"$gt": ""}' --> this isn't a key starting with a dollar sign. Rather, the value of username is just a string.
Try sending it this object instead:
{
"username": { "$gt": "" }
}
From what I understood, from debugging and going through the code, the keys it sanitizes is any potential key in a key=value pair of query & post params that have the $ or a dot. It also tries to sanitize any keys in the body and header of the request.
For example, even the json provided by the previous user above won't do it.
but https://your-domain/?$user=json would be sanitized to user=json.
It doesn't remove $ from the value of the params as you and also I was expecting. I also opened up a question on the github to the creator and will see what he says. I would think the security risk is for both the key and value. This doesn't do any good if you're not saving the key to mongodb but saving the value instead.
For reference it checks the following HTTP sections to remove any potential harmful $ or .
['body', 'params', 'headers', 'query'].forEach(function (key) ...
I think the answer by #A10n is correct, and so it appears that 'express-mongo-sanitize' is only a partial solution. I've added my quick-and-dirty solution to remove curly brackets from every req.body/params object (which should blunt most NoSQL attacks) like so:
// Middleware that replaces all {} symbols with [] to prevent NoSQL attack
const removeCurlies = (req, res, next) => {
Object.keys(req.body).forEach((key) => {
req.body[key] = _.replace(req.body[key], "{", "[");
req.body[key] = _.replace(req.body[key], "}", "]");
});
Object.keys(req.params).forEach((key) => {
req.params[key] = _.replace(req.params[key], "{", "[");
req.params[key] = _.replace(req.params[key], "}", "]");
});
next();
};
Then, before your routes:
app.use(removeCurlies);
Ran into the same problem and what worked for me was parsing the incoming request body before using the mongoSanitize package. Like this...
const express = require("express")
const mongoSanitize = require("express-mongo-sanitize')
const app = express()
app.use(express.json())
app.use(mongoSanitize())
Related
I am doing codes in node js express js But while working, I thought that if the hexadecimal number was taken from the base URL string, then my work would be easier.
I mean:
baseUrl: '/api/v1/movies/61b6e1c5503b122ff9436b14/seasons' (Get from req.baseUrl)
My base URL string is: '/api/v1/movies/61b6e1c5503b122ff9436b14/seasons'
I need just: 61b6e1c5503b122ff9436b14
I am currently using the javaScript replace() method but it does not seem to be very effective to me. I am especially interested to know any good method.
Thanks
Regexp would be useful unless you are looking for dynamic routes
This would get the code
let baseUrl = '/api/v1/movies/61b6e1c5503b122ff9436b14/seasons'
const hex = baseUrl.match(/movies\/(\w+)\/seasons/)[1]
console.log(hex.toUpperCase());
What you're looking for are called dynamic routes and in express you can do like this:
const express = require('express');
const app = express();
app.get('/api/v1/movies/:hexid/seasons', (req,res) => {
console.log(req.params.hexid);
})
You can find more info in their docs ( search for :bookId )
You can use req.params to map the route parameters in ExpressJS
app.get('/api/v1/movies/:hashid/seasons', function (req, res) {
console.log(req.params.hashid)
})
im getting this error while login a registred user
ofcourse i got this error after using dotenv package to secure my database encryption key
but proccess.env.SECRET is working currectly
i guess my problem is here :
userSchema.plugin(encrypt, {
secret: process.env.SECRET,
encryptedFields: ["password"],
});
app.js :
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const mongoose = require("mongoose");
const encrypt = require("mongoose-encryption");
const app = express();
app.use(express.static("public"));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({ extended: true }));
mongoose.connect("mongodb://localhost:27017/userDB", {
useUnifiedTopology: true,
useNewUrlParser: true,
});
const userSchema = new mongoose.Schema({
email: String,
password: String,
});
//_
// encrypting password field // |
userSchema.plugin(encrypt, { // | i guess problem is here
secret: process.env.SECRET, // |
encryptedFields: ["password"], //_|
});
const User = mongoose.model("User", userSchema);
error :
Error: Authentication code missing
at Object.schema.methods.authenticateSync (C:\Users\Amir\Desktop\security\node_modules\mongoose-encryption\lib\plugins\mongoose-encryption.js:419:23)
at model.<anonymous> (C:\Users\Amir\Desktop\security\node_modules\mongoose-encryption\lib\plugins\mongoose-encryption.js:239:47)
at Kareem.execPreSync (C:\Users\Amir\Desktop\security\node_modules\kareem\index.js:115:16)
at model.syncWrapper [as $__init] (C:\Users\Amir\Desktop\security\node_modules\kareem\index.js:232:12)
at model.Document.init (C:\Users\Amir\Desktop\security\node_modules\mongoose\lib\document.js:513:8)
at completeOne (C:\Users\Amir\Desktop\security\node_modules\mongoose\lib\query.js:2853:12)
at model.Query.Query._completeOne (C:\Users\Amir\Desktop\security\node_modules\mongoose\lib\query.js:2094:7)
at Immediate.<anonymous> (C:\Users\Amir\Desktop\security\node_modules\mongoose\lib\query.js:2138:10)
at Immediate.<anonymous> (C:\Users\Amir\Desktop\security\node_modules\mquery\lib\utils.js:116:16)
at processImmediate (internal/timers.js:456:21)
I had the same problem and this worked for me:
Navigate to your database using your terminal using (as much I can see you use mongo) ex:
show dbs, use databaseName, show collections, db.databaseName.find() and then
Empty your database db.databaseName.drop() (If it is important stuff, then keep it somewhere safe so you can reuse it again, should be easy if you use Postman)
And recreate your database
You should delete the existing documents in your database and run the code again with an empty document.
Actually, the thing is that you might be using your new encryption on data which was stored before the addition of encryption , whereas if you check the same with the new data it will work fine , thus you should create a new database first so that you add new items in it , and it will hopefully work for you . :)
Well, I was facing with the same issue, but it occurs only to the data which you have stored in the database previously using mongoose-encryption without dotenv.
However, it works fine
when you create a new data by registering as new user.
Drop the database and create a new one. (Make sure you save the data before doing it).
:)
The reason is you might changed the "encryption key". If you can find out the one you made it at start, then you can still check and use this database in your (.js) file. However, "Error: Authentication code missing (Mongoose-encryption)" does not mean you destroy it. You still can use. Have a try!
Yes,
You should try to Drop your Database once and Start the server agein.
It may be possible that you have changed your encryption "secret" code a bit while transferring it to the .env file.
Don't forget to take backup before dropping it.
To fix this error, drop the collection you are trying to encrypt (with db.<collection-name>.drop() and create it again.
You are seeing this error because some of the data in your database is not encrypted while the rest of it is encrypted.
In my case, the error was triggered by the usage of findOneAndReplace.
I wanted to replace the record if it already existed in this way:
const newCredentials = { userId, securedCredentials};
await Credentials.findOneAndReplace({ userId }, newCredentials, {upsert:true});
I noticed that the code works if we try to add the object in the standard way:
await new Credentials(newCredentials).save();
Thus the problem is that we are trying to upsert a non-encrypted object. Ok, let's encrypt it then! I tried by calling the encrypt method, but that doesn't work
// The following line doesn't work. `encrypted` is undefined
const encrypted = await new Credentials(newCredentials).encrypt();
// This does work though...
const credentials = new Credentials(newCredentials);
credentials.encrypt((err) => {console.log(credentials)});
As I couldn't figure out how to do this in an atomic operation, I just went the long way:
await Credentials.deleteOne({userId});
await new Credentials(newCredentials).save();
If someone has an idea about how to atomize the operation, it would be great!
You don't have the MongoDB server port 27017 active on your system.
To do that, simply write "mongod" command in another tab of your hyper terminal.
I am using express-validator in my express app, and I am attempting to prevent additional fields being specified on POST requests. This is because I am passing the value of req.body to my ORM to insert into the database, and I want to avoid having to explicitly map between my inserted object and request body alongside adding validators.
Is this possible? I can't seem to find it in the documentation. Using JSON Schema you can do this with additionalProperties: false
After some more research, I found that it's possible to do with with the Matched Data API
In my express controller I can now do;
const { matchedData } = require('express-validator');
(req, res) => {
const matched = matchedData(req, {
includeOptionals: true,
});
db.insert(matched)
...
}
I am learning Node.js, and while implementing JWT I want to put a separate config.js file to set the secret to generate and verify tokens, but I am having trouble with this.
First I create the config file as follows:
// JWT config
module.exports.jwt_secret = 'mysecret';
Then I require it at the API endpoint as:
const jwt_secret = require('../../../../../config');
And finally, I try to use it as follows:
jwt.verify(token, (jwt_secret), function(err, decoded) { ... }
But it does not work, so I tried a console.log with the jwt_secret, and I get this:
{ jwt_secret: 'mysecret' }
I checked code and searched here at Stack Overflow but I do not see how to solve this. I know probably it is pretty obvious but as I said I am quite new at programming and I am learning.
Thank you in advance.
Taking a quick look at the node-jsonwebtoken api, verify seems to expect a string as the second argument. The way that you've set up your export and require means that your variable jwt_secret is the entire exports object from your config module, not the string. Try changing it to
jwt.verify(token, jwt_secret.jwt_secret, function(err, decoded) { ... }
and if that works, you may want to modify your require statement to something like
const config = require('some/path/config');
and then use config.jwt_secret as the argument. That's just style though, do what works for your project.
My MongoDB keys in person collection are like this:
TWITTER/12345678
GOOGLE/34567890
TWITTER/45678901
...
I define getPersonByKey route this way:
router.route('/getPersonByKey/:providerKey/:personKey').
get(function(req, res) { // get person by key
var key = req.params.providerKey + '/' + req.params.personKey;
// ...
}
);
Of course I'd prefer to be able to write something like this:
router.route('/getPersonByKey/:key').
get(function(req, res) { // get person by key
var key = req.params.key;
// ...
}
);
But this doesn't work, since GET http://localhost/getPersonByKey/TWITTER/12345678 of course results in a 404, since the parameter with the slash is interpreted as two distinct parameters...
Any idea?
Express internally uses path-to-regexp to do path matching.
As explained in the documentation, you can use a "Custom Match Parameter" by adding a regular expression wrapped in parenthesis after the parameter itself.
You can use the following path to get the result you need:
router.route('/getPersonByKey/:key([^/]+/[^/]+)').
get(function(req, res) { // get person by key
var key = req.params.key;
// ...
}
);
You can test and validate this or any other route here.
You can use this if your parameters has containing slashes in it
app.get('/getPersonByKey/:key(*)', function(req, res) { ... })
It works for me (at least in Express 4). In my case, I used parameters like ABC1/12345/6789(10).
Hopefully this useful.
app.get('/getPersonByKey/:key(*)', function(req, res) { ... })
This isn't working for me.
Swagger-ui will encode the path var before using it.
e.g. article/2159 will become article%2F2159.
When going directly with curl, it will not get encoded. the slash will remain a slash instead of %2F. And then the route is not matched.
Update: I'm on fastify. On express 4.X this works correctly.