No-SQL injection in Mongo PHP
php.net
php.net
It would be devilishly hard to inject malicious operations into ZEO/ZODB (the one "NoSQL" I am very familiar with). I can only assume it would be equally hard to do it with systems that do not try to replicate the human-friendly nature of SQL.
Anyway, the idea of blindingly assuming that just because something doesn't use SQL it's invulnerable to any kind of code injection is a very optimistic position.
This is explained in item #5 of http://cr.yp.to/qmail/guarantee.html - Don't parse.
Note that this is definitely true in this case. MongoDB's API is based on the passing of data structures where you have well-defined places to put user input directly. The problem emerges because PHP decides to attempt to parse that user input in a way that it had no real need to, with bad results. Without that parsing, there would have been no need to sanitize data because there would be no possibility of ever having had bad data in the first place.
I'm not sure what tptacek is thinking, but here is an important case where you really want the execution path to change.
Tables often have a very uneven distribution of data. This can mean that the right query plan will depend on the data provided. Consider the following query:
SELECT ...
FROM lease l
JOIN property p
ON p.id = l.property_id
JOIN management_company c
ON c.id = p.management_company_id
WHERE l.created >= ?
AND l.created < ?
AND c.name = ?;
Pretty straightforward query, right? Now here is the question, should the query plan start by filtering leases on date then flow forward to finally filter on the management company, or by getting the right management company and then flow backwards to filter on dates.It turns out that there is no right answer. You want to start with whichever restriction eliminates the most records, quickest. If the date range is wide, we want to start with the management company. If the date range is narrow, we want to start with that.
So let us simplify our life:
SELECT ...
FROM lease l
JOIN property p
ON p.id = l.property_id
JOIN management_company c
ON c.id = p.management_company_id
WHERE l.created >= '2011-02-01'
AND l.created < '2011-03-01'
AND c.name = ?;
There is still no right answer! Let's suppose some company, call it 'AIMCO', has a huge number of properties with a lot of lease volume while other companies generally have 1-5. Then it is better to filter on dates first for 'AIMCO', and better to filter on the company first for anything else. Getting the order wrong gives an order of magnitude performance difference. (This is a real life example. I really did have a bug that basically read, "Make this report run for AIMCO." And this was exactly the problem.)Most databases will pick one query plan and go with it. But Oracle allows you to turn on Adaptive Cursor Sharing. With this it will analyze the data, see that AIMCO should get special treatment, and actually execute a different query plan if you passed in 'AIMCO' than if you passed in something else.
So now you have a data dependent query plan.
Not quite. My example consists of the DBMS execution planner deciding that it will switch between different semantically equivalent query plans depending on the parametrized data passed. This switch should not be noticed in the application layer.
Back to the main thread, I suspect that you are interpreting tptacek as saying something he didn't. You seem to be looking for a potential security flaw. But he didn't ever say that there was one. He only said that the query structure can vary with the parameters passed to a parametrized query. I just described one way that this can happen.
The way that I solved that particular problem in the past is that I used a templating system to generate my SQL. User input was allowed in ifs in my templates, and as parameters for my queries, and that was it. At no point did I trust user data inside of my SQL.
Wouldn't it be safer to use a switch statement?
I think I found the issue.
If you ever put "you just need" into a sentence about security you've already lost.
User input need to be sanitized always. Rails make it easy and is the default way.
Regardless, it's not a knock on rails. Just saying it's not necessarily a magic bullet to secure apps (and I don't believe rails nor PHP needs to be).
Edit: It's not actually a footnote -- it's actually just the only item on the lonesome "Security" page.
Which is why you should never process raw input in multiple places in your app. One method I've used for over a decade of webdev in any language: there's only one place to get the input from, and that place requires you specify what input is allowed.
While this may be not as easy to use as simple strings, it is consistent.
Honestly, I'm trying to figure out how someone could sanitize their input and still be affected by this.
I don't think you could, unless you tried to write your own sanitizing functions from scratch and somehow screwed it up. In PHP, htmlspecialchars(), mysql_real_escape_string() and addslashes() all do fine sanitizing array input -- either throwing an exception or returning the string "Array".
Think of every language as a strictly typed, pure functional programming language, where I/O is the big bad world out there and evil and blocking and complicated. Likewise, on the web you should treat any inputs as being evil... (ok, so the comparison is far from perfect, but the idea holds true)
Lots of built-in options, the only reason this should be an issue is if you're not validating inputs at all, or hacking your own as ronnoch noted.
That being said, there are legions of clueless devs out there who will do this, and that is unfortunate.
http://www.idontplaydarts.com/2010/07/mongodb-is-vulnerable-...
This would be the equivalent of some helper wrapper on top of a redis driver that turned strings with a special format into, say, an MGET command that returned more than a developer meant for it to.
redis is safer because it doesn't have the complex query-over-document functionality that mongo does, but it's not inherently immune.
But the new Redis protocol is safe against that.