If env vars didn't exist, the people who do the awful things described in TFA would still do those awful things. In fact the problems described there mostly took place when env vars were overridden by command line flags and unauthenticated web server requests.
"Sanitize your inputs!" is the security equivalent of abstinence-only sex education. Yes, it technically will fix the problem if executed perfectly, but an endless history of failure should have convinced everyone by now that it is not the right solution for the real world. As other commenters have pointed out, shells were never designed with adversarial input in mind and so "sanitizing" them means hunting down and endless number of bugs and still not knowing if you've done it correctly.
The correct way is to design from the start so that user input cannot be executed. For SQL, everyone now knows that means parametrized statements; in this case, it means job queues and standalone processes with locked-down privileges.
Ehh... how many instances of unsanitized input leading to RCE were due to the subtleties of the interpreter, and how many are the result of people blithely passing user input into the interpreter, not using the sanitation functions available in most mainstream languages (in many cases, in the standard library)?
If getting people to do this is impossible, so is getting people to stop storing passwords in plaintext.
> If getting people to do this is impossible, so is getting people to stop storing passwords in plaintext.
This may very well be the case, though. You may get most people to know that storing passwords in plaintext is not a great idea (tm), but there will always be people ignoring that, be it due to downright ignorance or external factors.
I can envision some non-technical person in an organisation prescribing to IT that a new user password cannot look like one of the user's old passwords, where "look like" actually means something like an edit distance of <=2. Since the secure way of implementing this is looping over all possible passwords close to the one the user entered now, hashing them all and comparing them to a list of past hashes (which is quite resource-intensive, which costs mony), I bet there will be people that will give in and just store the last few passwords in plaintext. Maybe they'll encrypt them, but they'll be there.
Yes except now I have 40 microservices that I have to make sure all parse URL parameters properly instead of the one I used to have that would parse them and then put them where everyone else could find it.
Security in depth is hamstrung by an ever-increasing surface area.
I'd say that if your microservices aren't sharing that type of code, you've probably made a software design mistake. Perhaps beside the point, but that's a bit of a contrived example.
I mean, people doing things wrong isn't evidence that they cannot be done right... right?
I can give you an example of somethign that is relatively common and "bad".
Imagine a website that will take an URL and extract the text from it - removing markup - or another service that will scan a URL for meta-tags, etc. All the kind of things that you can find easily.
Now imagine what happens if a user passes input such as "file:////etc/passwd". There are a whole bunch of services which will spit out the contents of the file, because they use some URL-fetching library and don't limit the protocols to http or https.
I wrote a blog-post about that, which was featured here a while back:
Being old enough to remember the special hell that was CGI, that particular mechanism showing up in 12 Factor Apps doesn't sit entirely well with me.
It doesn't stop me from supporting it as a deployment process, but there's a 22 year old inside of me that cries a little every time I talk about using environment variables for config.
I'm categorically against shelling out for anything from a web based service. If you need this sort of functionality implement a queue, send a signal using some IPC process and wait until the job is done.
Exec'ing anything (even Postscript, which is after all a full fledged programming language) is a huge risk. Ensure that the input you receive is not malicious and if you can recompile any executables that will process hostile inputs to process only the subset of commands and functions that you think should be able to deal with untrusted input.
And of course you still have to properly sandbox those programs.
1. Those command line programs weren't necessarily designed with an adversarial user in mind! Sanitizing input to prevent shell escape sequences is not enough. You really need to sandbox well.
2. For some tasks, it might be easy for a malicious user to guess which command-line tool you are using. You might be getting way less depth out of obscurity than you think you are.
> Ensure that the input you receive is not malicious
To do that input needs to be decidable. Input from possibly-malicious sources needs to be limited to a grammar no more complex than deterministic context-free[1]. Accepting Turing complete input introduces the possibility that recognizing anything about the input might be provably undecidable.
There is another mathematically-correct layer of security you can get, though, which is the IO interface to the real world that you provide the input. Barring (huge!) bugs in the (very simple!) interpreter, it doesn't matter how much basic brainfuck [1] from a user you execute, they aren't going to get a shell. Worst they can do is burn CPU until you shut them down. (Brainfuck interpreters are already generally pretty tightly RAM limited.)
Sandboxing is of course easier said than done, but it's still mathematically valid. The problem is that our real hardware has too many metaphorical sharp edges from a mathematical point of view, not that it doesn't work.
Dang. Would containerization solve this, or at least help mitigate the damage?
Rachel's stories are always interesting, I always find myself learning new things from them, and I have been for over a decade now. They are a great reminder of my sysadmin past, now that I mostly do DBA work anymore. However, I do wonder if I should be sending her aspirin or tequila as tribute at times...
You could probably limit some of the insanity by walling off things that you absolutely need to run and can't work around. Even then you still have to worry about someone having too much fun inside the jail, container, or whatever your system offers.
As for aspirin and friends, well, being effectively retired has done wonders of late. But thanks!
I disagree, shelling out to another program is a perfectly reasonable design decision, one that has paid off in development speed. The people that run from their OS like it's something to be scared of are really missing out on the benefits of the their OS. I have built whole pipelines of essentially shell commands and I have never had a command injection vulnerability because I follow one simple rule:
I filter and sanitize input as soon as I receive it from the user. There are an infinitesimally small number of instances where you need to process anything but [a-zA-Z0-9]+ charset in a shell pipeline.
Once you have a pattern down for safely executing commands, there is no reason to be scared. It's really a myth that you can't become "secure", and this author's blog post is a perfect example. Tell me how you will conduct command injection when you have a whitelist of a-z0-9. I need an example, instead of this fear mongering that your OS is something to run and hide from.
20 comments
[ 5.0 ms ] story [ 53.8 ms ] thread"Sanitize your inputs!" is the security equivalent of abstinence-only sex education. Yes, it technically will fix the problem if executed perfectly, but an endless history of failure should have convinced everyone by now that it is not the right solution for the real world. As other commenters have pointed out, shells were never designed with adversarial input in mind and so "sanitizing" them means hunting down and endless number of bugs and still not knowing if you've done it correctly.
The correct way is to design from the start so that user input cannot be executed. For SQL, everyone now knows that means parametrized statements; in this case, it means job queues and standalone processes with locked-down privileges.
If getting people to do this is impossible, so is getting people to stop storing passwords in plaintext.
This may very well be the case, though. You may get most people to know that storing passwords in plaintext is not a great idea (tm), but there will always be people ignoring that, be it due to downright ignorance or external factors.
I can envision some non-technical person in an organisation prescribing to IT that a new user password cannot look like one of the user's old passwords, where "look like" actually means something like an edit distance of <=2. Since the secure way of implementing this is looping over all possible passwords close to the one the user entered now, hashing them all and comparing them to a list of past hashes (which is quite resource-intensive, which costs mony), I bet there will be people that will give in and just store the last few passwords in plaintext. Maybe they'll encrypt them, but they'll be there.
Security in depth is hamstrung by an ever-increasing surface area.
I mean, people doing things wrong isn't evidence that they cannot be done right... right?
Imagine a website that will take an URL and extract the text from it - removing markup - or another service that will scan a URL for meta-tags, etc. All the kind of things that you can find easily.
Now imagine what happens if a user passes input such as "file:////etc/passwd". There are a whole bunch of services which will spit out the contents of the file, because they use some URL-fetching library and don't limit the protocols to http or https.
I wrote a blog-post about that, which was featured here a while back:
https://news.ycombinator.com/item?id=12478538
It doesn't stop me from supporting it as a deployment process, but there's a 22 year old inside of me that cries a little every time I talk about using environment variables for config.
Exec'ing anything (even Postscript, which is after all a full fledged programming language) is a huge risk. Ensure that the input you receive is not malicious and if you can recompile any executables that will process hostile inputs to process only the subset of commands and functions that you think should be able to deal with untrusted input.
And of course you still have to properly sandbox those programs.
1. Those command line programs weren't necessarily designed with an adversarial user in mind! Sanitizing input to prevent shell escape sequences is not enough. You really need to sandbox well.
2. For some tasks, it might be easy for a malicious user to guess which command-line tool you are using. You might be getting way less depth out of obscurity than you think you are.
To do that input needs to be decidable. Input from possibly-malicious sources needs to be limited to a grammar no more complex than deterministic context-free[1]. Accepting Turing complete input introduces the possibility that recognizing anything about the input might be provably undecidable.
[1] https://archive.org/details/The_Science_of_Insecurity_
Sandboxing is of course easier said than done, but it's still mathematically valid. The problem is that our real hardware has too many metaphorical sharp edges from a mathematical point of view, not that it doesn't work.
[1]: https://esolangs.org/wiki/Brainfuck
Rachel's stories are always interesting, I always find myself learning new things from them, and I have been for over a decade now. They are a great reminder of my sysadmin past, now that I mostly do DBA work anymore. However, I do wonder if I should be sending her aspirin or tequila as tribute at times...
As for aspirin and friends, well, being effectively retired has done wonders of late. But thanks!
I filter and sanitize input as soon as I receive it from the user. There are an infinitesimally small number of instances where you need to process anything but [a-zA-Z0-9]+ charset in a shell pipeline.
Once you have a pattern down for safely executing commands, there is no reason to be scared. It's really a myth that you can't become "secure", and this author's blog post is a perfect example. Tell me how you will conduct command injection when you have a whitelist of a-z0-9. I need an example, instead of this fear mongering that your OS is something to run and hide from.