Thursday, December 1, 2011

Node.JS Security - the good, bad and ugly

At the moment, dev world is full of rave about Node and server side JavaScript (databases like MongoDB and the likes). There hasn't been a better time for front-end and JS developers. On the first look - it appears great, promising and exciting.

On the down side, as with most upcoming technologies, there isn't enough security analysis, consideration and advisory to reference and understand gotchas with server side JS. Nothing wrong with that - it's functions, coolness and innovation that brings business and not security (history/economics is a testimony).

In this post, I will share my security view point as I see it. This could be an ever growing list and the kind of things you can achieve with server side JavaScript - there is no early end to this.

Let's start with the good things. Node inherently introduces a great security benefit over
traditional server side programming paradigms and that is "secure by default" (reminds me of my NetBSD days). As highlighted in white below, your create your web server - a bare bone types and not a full blown with bells and whistles like Apache.



And then chose and pick what you want. Like define what your doc root will have - unlike anything and everything in a traditional web doc root. Like highlighted in yellow below - that is what your web server will respond to for requests. Rest needs to be caught by a 404.



Summarizing - your web server isn't configured and capable more than what you want it to be unlike Apache, Tomcat or IIS. I recall countless instances of Tomcat compromises due to default admin and manager apps that come installed and running with default passwords. And IIS getting exploited with WebDAV buffer overflow when in reality the web app never really needed it in first place. Typically web servers sent a false sense of security where developers mostly considered them to be secure. And we all know, more features, bigger the attack surface. Bigger the attack surface more chances of things going wrong. And something that can go wrong will go wrong!

On the flip side - the bad parts. Node carries over the known dangerous JavaScript APIs like the eval that can be trivially exploited to do server side injection (that were earlier only client side exploits like XSS).

Let's look at a PoC exploit where app evaluates the input and returns  an output like below


Abusing eval on client side would result an XSS but on server-side it induces a server side injection alike SQL Injection as seen below where we inject an HTTP response.


 The screenshot below highlights execution of server side injection.


To best of my knowledge, this issue was first brought to notice in context of Node by Bryan Sullivan at BlackHat. Not a brand new exploit. We know eval is evil. What is worth note here is most developers wouldn't imagine this happening at the first go. From that perspective this exploit vector server side is novel.

What do I see ugly? The ugly parts are the ones that introduce new attack vectors. There should have been default protection built-in ideally. The event driven single threaded programming model is not what web developers are used to. Node is single threaded and a simple error can create a denial of service condition as highlighted in the screenshot below.


As highlighted, hitting submit crashes the node server.


Similar DoS condition would result when messing with global variables - intentionally or unintentionally. Above scenarios are quite likely considering JS developers are usually quite used to errors. I see thousands of live sites day in and out that have a number of errors showing up in Firebug console and running absolutely ok which will not be the case as you go server side.

Another  ugly part is that web developers are not quite used to service permissioning. Web developers had it outsourced to Apache/IIS, would now end up running their node services as root, that earlier ran as nobody.

A 1000 feet high apple to apple comparison between let's say PHP and Node tells me - it took a step back in security. At least, you would come to expect a sanitization/validation library for a new programming language, if not a fancy new auto-sanitization module like PHP Filter (aah yes - Filter isn't a complete auto sanitization in PHP but you get what I mean).

An honest look and I feel node isn't meant to be used as is.  With a strong framework, is how it should be used. There are many in the fray right now - Express probably is the most widely used. I haven't tried it yet but from what I see, security in node is a work in progress.

Being a Yahoo, how can I end without not mentioning Yahoo Cocktails. Haven't played around with it yet, but this is something I have super high hopes with. The engineers I met there are fabulous. Come Q1 2012 it would be there for all of us to play around. Yahoo is a great company, the best  I have worked for - no doubt I would love to see it scoring high.

Learning more and more of Node, I keep reminding myself "Node is powerful, and with power comes responsibility".

25 comments:

  1. Great article. We are actually writing a new enterprise app, and now the front-end team are suggesting using node. We have very big clients who always pen-test our current site and security is a big issue! We have actually had to get the team to slow down and consider the security aspects before rushing into this new architecture. I am spending a lot of time researching node from a security and performance/scaling perspective as this is the prime concern from a business angle. Your point about the JS error leading to DOS was a classic example of what we could end up with! The single threaded model has always worried me.

    Please do give us more updates on node.

    ReplyDelete
  2. Thanks, Zahir.

    I have had these concerns coming from several folks I meet at conferences and communities.

    NodeJS as it is, is not the way to go from what I learn so far. It has to be handled by a framework which should also provide most desired security controls with minimum developer interference especially on things that developers did not expect in other development paradigms and things that were implicit.

    Did you experiment with Express? I would be looking into the security aspects of it soon. At the moment, it is Cocktails what I am playing around with.

    OVERALL - I would be very cautious for an enterprise app with the maturity of security features available on Node.

    If the business decision is hard and you go to use Node, here are some things I think would work but it isn't a complete list:

    1. Use a templating framework. I like Ctemplate. Mustache is a derivative of Ctemplate and available for Node. It autoencodes HTML context (not the JS and other contexts like Ctemplate) user input in templates to protect against XSS primarily

    2. Hack the HTTP module of Node and auto some filtering there if you could via C module. Esp for SQLi and related input validation issues

    3. Use a framework that handles Node errors to avoid DoS

    4. CODE DEFENSIVELY. This is the best bet. Like audit usage of eval and the likes.

    5. This is not a Node specific issue but watch out for DOM XSS. It is on the rise everyday. Again - code defensively. Avoid document.write and innerHTML instead use innerText or filter user input. Encoding won't save always though due to browser decoding which can again trigger DOM XSS.

    I will publish more things along the way. Share some my way if you have any recommendations.

    Good luck!

    ReplyDelete
  3. I'm using Node a lot, and I have never used Eval in my code. It seems the whole point here is: do not use eval()! Beyond that, is the security really that bad? If you setup your server with a firewall and you're not a completely ignorant software engineer, it seems your server should be pretty robust.

    ReplyDelete
  4. Hi Brian - Thanks for sharing your opinion.

    I think it is much much more than eval. Today I wrote 3 new posts on exactly that.

    #1 Global Namespace Pollution http://bishankochher.blogspot.com/2012/02/nodejs-global-namespace-pollution.html

    #2 with is evil http://bishankochher.blogspot.com/2012/02/nodejs-with-is-evil.html

    #3 switch is evil http://bishankochher.blogspot.com/2012/02/nodejs-switch-is-evil.html

    #1 is something that non JS developers aren't used to. #2 and #3 were probably not that serious in the context of client side JS with Node they are really dangerous.

    I will be posting more stuff soon. BTW there are also eval cousins like setInterval that are equally dangerous. There is lot more that I worry about on Node.

    Do share if you come across something. This is an active area of research.

    ReplyDelete
  5. Any webserver can be insecure if you code like a dufus.

    ReplyDelete
  6. Please do some research into Node.js. Counterpoints:

    XSS - This is true of ANY serverside technology. The key here is to escape inputs. Templating systems like express.js handle this for you. If you don't escape input you will see this in ANY serverside stack, java, php, etc.

    Crashing - All you need is a global exception handler or to escape input and you are good to go.

    ReplyDelete
    Replies
    1. XSS:

      Are you sure express.js can defend against XSS in contexts other than HTML? It does not.

      i did not find any templating system in Node that does context sensitive output escaping. HTML escaping is simple. And that's what Mu and other engines in Node achieve.

      What we need is something like Google Ctemplate http://code.google.com/p/ctemplate/

      We did a hack to make it work on Node. So it isn't that difficult. We might OS it after some fine tuning down the line. As of now, it's just a hack

      Crashing:
      You said it. "All you need" - why should I need to do that. It's got to be the default config. History is full of instances where systems that are secure by default are more resilient than ones that have opt-in security. Worse, this is a break down of paradigm for traditional server side programmers.

      Delete
  7. I visited your blog for the first time and just been your fan. Keep posting as I am gonna come to read it everyday.

    ReplyDelete
  8. @washington security systems - thanks for the appreciation. This kind of spurs me to write more often. Another one should be coming soon.

    ReplyDelete
  9. You can leverage a lot of the security issues you mentionned by adding
    'use strict'
    at the top of every js files of your project like you would add <?php for a PHP file. Basically, strict mode prevents a lot of common JS mistakes and might even improve performacnes in some situations. Part of the things it prevents :
    - with keyword: it is disabled in strict mode and throws an exception if you try to use it
    - global namespace pollution: you can't use a variable that has not been declared using var first

    It also add a lot of exceptions where before JS code was silently doing nothing. If you write, let's say "delete Object.prototype", withotu strict mode, it was doing nothing (it would be disastrous to remove this prototype right? so it was impossible, but this line was also not doing anything... now you get a nice TypeError if you try this).

    If you want more documentation on this : https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode?redirectlocale=en-US&redirectslug=JavaScript%2FStrict_mode

    To make it shorter : use strict mode. That's a HUGE benefit. There was some talk about activating it by default on NodeJS but for some reason it has not been done. So you have to add it as the first line of code in every file. That's a shame but still it leverage a lot of the language biggest issues.

    I also want to tell that 'use strict'; also works in browsers. If you want to write clean code, you can. Still, on browser side, since there are old broken browsers, you should not rely on the nice new exceptions for your code to work properly and still tests things out before.

    ReplyDelete
  10. I also forgot to tell one important thing. You don't run NodeJS as root. That's a bad idea. You just don't. Then you will ask me "But how do I listen on port 80?". That's a good one. User land softwares can't listen on ports numbered lower than 1024. So what? It's easy as pie. You just add a nat rule (on Linux using ipnat) to forward port 80 on your internal port xxxx (eg 8000). That would look like:
    sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8000

    Then if it works as intended, save the change in conf:
    sudo service iptables save
    sudo chkconfig iptables on

    Hope this might help people here address their biggest security issues with NodeJS. I believe there is a lot of good practices to got used to but it will make a better web in the end. I believe NodeJS is a piece of technology that as a great future. Still needs some refining but still... It looks very very promising.

    ReplyDelete
    Replies
    1. You stole my words. Really.
      But you forgot to say, another common practice is to have
      a reverse-proxy that listens to 80, then drops privileges.

      Moreover, you can drop privileges with Node by calling
      process.setuid(...), something you can't
      normally do with other languages without native extensions.

      Delete
  11. The whole point of your post should be.. don't use Node.js, if you're an amateur. It's clear that you should use STRICT ECMAScript with Node.js. Don't generalize. If you are a professional, you know where weak points of ECMAScript are. Don't directly interpret input, don't use global variables, unless caching or immediate memory-databases are the actual case.

    The most important is: don't use Node.js, if you're an ECMAScript amateur. You'll fuck it up.

    http://www.crockford.com/javascript/javascript.html

    ReplyDelete
  12. Yet another blog post warning about eval(), this is just what the world needed. People have been warning about eval() since before JavaScript's inception; Perl for example.

    ReplyDelete
  13. I was gonna give this post +1, but I was rickroll'd after seeing the eval code..

    ReplyDelete
  14. Your toy it's so beautiful, I like it vey much, I also bought one at cheap prada handbags
    , If you want one too. can visit this address, The backpacks are beautiful at there. I think you will be love it too.

    ReplyDelete
  15. Your toy it's so beautiful, I like it vey much, I also bought one at hermes handbags online
    , If you want one too. can visit this address, The backpacks are beautiful at there. I think you will be love it too.

    ReplyDelete
  16. I don't understand why anyone would eval() the contents of any variable that was sent from a form (or URL or any other susceptible location)? Am I reading the code wrong? I'm new to server-side JS using Node and I'm trying to understand some of the security aspects I need to consider.

    ReplyDelete
    Replies
    1. You are right. This isn't a good example. And some people hated it. I don't blame them. Quite honestly, in my experience I have seen worst (ab)uses of 'eval'

      To your question, the whole point is - use 'eval' with extreme caution on the server side.

      Delete
  17. You are comparing apples and oranges. You are comparing using node to the benefits fully featured application framework. If you wanted to comare apples to apples you'd compare node to a Java app opening up a socket to listen on a port (no Tomcat, Spring, JBoss etc) which has all of the same problems listed here (some are easier some are harder)

    ReplyDelete
  18. Check out: http://www.youtube.com/watch?v=hQVTIJBZook

    ReplyDelete