Oh my Node or Security in NodeJS
by Roman Sachenko
Oh my Node or Security in NodeJS
by Roman Sachenko
About Me
back-end developer, team lead in DA-14
~ 2.5 years in software development area
Topics: sources of security problems
Topics: what do they lead up to
Topics: painkiller
Topics: best practices
Contents:
- Weak authentication process
- Poor error handling
- Poor request validation
- Insecure dependencies
- Opensource Itself
- Useful Toos
- Best Practice
Weak Authentication process
Sources: Weak Authentication process
- infinite session expiration date
- infinite token expiration date
- weak secret key
Sources: Weak Authentication process
app.use(session({
secret: 'secret.secret.secret',
name: <name>
}))
Session Options
Sources: Weak Authentication process
jwt.sign({
data: 'foobar'
}, 'secret', { expiresIn: '999999999999999h' });
Auth Token Options
Painkiller for: Weak Authentication process
- Access/JWT token (https://github.com/auth0/node-jsonwebtoken)
- PassportJS (http://passportjs.org/)
- Auth Services (stormpath)
Poor error handling
Sources: Poor error handling
try {
let data = doSomething();
res.send({ success: true, data: data });
} catch(err) {
res.send({ success: false: error: err });
}
Sources: Poor error handling
ReferenceError: foo is not defined\n at /home/roman_sachenko/Projects/API/app/controllers/main.js:28:9\n at wrapped (/home/roman_sachenko/Projects/API/node_modules/newrelic/lib/transaction/tracer/index.js:183:28)\n at wrappedPromise.linkTransaction (/home/roman_sachenko/Projects/API/node_modules/newrelic/lib/instrumentation/promise.js:273:65)\n at wrappedPromise.wrapped [as __NR_wrapper] (/home/roman_sachenko/Projects/API/node_modules/newrelic/lib/transaction/tracer/index.js:183:28)\n at __NR_wrappedThenHandler (/home/roman_sachenko/Projects/API/node_modules/newrelic/lib/instrumentation/promise.js:445:26)
Sources: Poor error handling
Cast to ObjectId failed for value \"\" at path \"_id\"","name":"CastError","kind":"ObjectId","value":"","path":"_id"},"details":"CastError: Cast to ObjectId failed for value \"\" at path \"_id\"\n at MongooseError.CastError (/home/roman_sachenko/Projects/API/node_modules/mongoose/lib/error/cast.js:18:16)
Sources: Poor error handling
Sensitive information in headers or error messages like:
X-Powered-By: Express
Painkiller for: Poor error handling
- log sensitive error, not send in response
- use environment-based error handler
let error = 'some weird error message';
log.err(error);
if(process.env.NODE_ENV === ENV_PRODUCTION) {
return res.send({ success: false, error: 'Oops, something went wrong' });
}
return res.send({ success: false, error: error });
Module:
- error handler (https://github.com/expressjs/errorhandler)
Manual Way:
Poor request validation
ONE DOES NOT SIMPLY
VALIDATE REQUESTS
Sources: Poor request validation
example #1:
DELETE /users/?id=<userId>
- delete user by id
Sources: Poor request validation
example #1:
DELETE /users/?id={'$exists': true}
UserModel.remove({ _id: req.query.id });
=> UserModel.remove({ _id: { '$exists': true } });
Sources: Poor request validation
example #2:
User Model
{
id : <object id>,
first_name : <string>,
last_name : <string>,
deleted : <boolean>
}
Sources: Poor request validation
example #2:
User Model
{
id : <object id>,
first_name : <string>,
last_name : <string>,
deleted : <boolean>
}
PUT /users/<userId>
//update writable fields
DELETE /users/<userId>
//set 'deleted' as true
Sources: Poor request validation
example #2:
User Model
{
id : <object id>,
first_name : <string>,
last_name : <string>,
deleted : <boolean>
}
PUT /users/<userId>
//update writable fields
DELETE /users/<userId>
//set 'deleted' as true
What if we send 'deleted: true' in the body of our PUT request?
Sources: Poor request validation
example #3:
db.myCollection.find( { $where: "this.first_name == <value>" } );
Sources: Poor request validation
example #3:
db.myCollection.find( { $where: "this.first_name == 'a; sleep(1000000)'" } );
Painkiller for: Poor request validation
- validate incoming query params, body data etc.
- node validator (https://github.com/chriso/validator.js/)
- express validator (https://github.com/ctavan/express-validator)
- joi (https://github.com/hapijs/joi)
Insecure Dependencies
Sources: Insecure Dependencies
your code
3rd party dependencies
Application
Painkiller for: Insecure Dependencies
- snyk (https://snyk.io/)
Painkiller for: Insecure Dependencies
`snyk wizard`
2 vulnerabilities introduced via apn@2.1.4
- info: https://snyk.io/package/npm/apn/2.1.4
Remediation options (Use arrow keys)
> Re-install apn@2.1.4 (triggers upgrade to ms@2.0.0)
Review issues separately
Set to ignore for 30 days (updates policy)
Skip
Painkiller for: Insecure Dependencies
`nsp check`
Regular Expression Denial of Service
Name: uglify-js
CVSS: 5.3 (Medium)
Installed: 2.2.5
Vulnerable: <2.6.0
Patched: >=2.6.0
Path: api@0.5.0 > jade@1.11.0 > transformers@2.1.0 > uglify-js@2.2.5
More Info: https://nodesecurity.io/advisories/48
Opensource Itself
Sources: Opensource
Conditions:
- everyone is allowed to commit
- internal module dependencies
Result:
- easy to download and install insecure or malicious code
- hijacking the require chain
Sources: Opensource
Useful Tools
Best Practice: Useful Modules
- helmet (https://github.com/helmetjs/helmet)
- csurf (https://github.com/expressjs/csurf)
- node-rate-limiter (https://github.com/jhurliman/node-rate-limiter)
- cors (https://github.com/expressjs/cors)
Best Practice
Best Practice: "dont's"
- do not use 'eval'
- use carefully friends of 'eval' (setInterval, setTimeout)
- don't create buffer with int number in params ( new Buffer(100) )
- don't install suspicious modules
- don't install or use carefully issued modules
Best Practice: "do's"
- OWASP top 10 (http://nodegoat.herokuapp.com/tutorial)
- Limit requests frequency
- Use database ORM (mongoose, sequelize)
- Don't accept or use carefully query params as database query items
- Validate incoming body, query params
- Validate incoming body schema
- Set strong access control system
- Use SSL
- Use Security check tools (nodesecurity)
Questions?
*tried to find funny meme, but couldn't :(