tj / node-querystring

querystring parser for node and the browser - supporting nesting (used by Express, Connect, etc)
MIT License
455 stars 66 forks source link

Adding a counter to the decoder to prevent hashflooding? #28

Open alessioalex opened 12 years ago

alessioalex commented 12 years ago

Arnaut (`3rdEden) came up with this monkey-patch idea for the querystring native module, but I think we can implement it here also, here's what he said on the google group:

The fastest workaround for this would just be adding a counter to the decoder, and have it break out the forEach loop after it has stored about 1k keys in the obj.

tj commented 12 years ago

i still find it funny that they haven't seen the attack in practice haha, theoretically there are all sorts of attacks one could perform. I'm fine with this though and we could default it to something reasonable (cant imagine anyone even going over 50 really but maybe 1000 as a default)

buschtoens commented 10 years ago

Cap or throw?

This is again one of the cases where I'd really like to throw, as the resulting object is jibberish and a DOS attempt anyways, but on the other hand I don't want to kill all legacy apps.

So do we just cap or return {}? The limit would also be a thing for #89.

rlidwka commented 10 years ago

Throw 414 without any doubt.

Otherwise if there are people affected by this, they would have nice time figuring out why their app doesn't work.

buschtoens commented 10 years ago

(Of course this problem exists solely server-side)

Sadly we can't just throw a nice 414 as we don't have access to the response object. But I guess you meant throw new Error("414: Request-URI too long");.

Good frameworks capture those and pass them on to the client. Smelly homebrewn code probably doesn't even have process.on("error"). In this case we will crash a good bit of apps, if they get DOSd.

The alternative either would be returning {}, which will provoke some errors later on or pass on the blah blah wich will likely cause the same.

Throwing doesn't sound too bad.

buschtoens commented 10 years ago

(Can't edit on the iPhone, please ignore poor grammar and spelling :wink: )

rlidwka commented 10 years ago

Sadly we can't just throw a nice 414 as we don't have access to the response object

Oh sorry, I completely forgot that qs is not a connect-only module. :)

But error.status and error.code are widely used for this kind of things.

buschtoens commented 10 years ago

Your argument itself is still valid though. I feel really like we should throw. This is a case where we should force the user to take action.

What does our dearest @visionmedia say? :)

tj commented 10 years ago

throwing in node stuff is generally bad if you can avoid it, you never know where they'll call qs.parse(), if it's not immediately in a route etc it likely won't get caught and will explode your program. I'd probably just cap it to a reasonable depth/size

buschtoens commented 10 years ago

And this is the reason I would want to throw. To enforce better security on the user. Otherwise 90% won't do shit. The request is lost anyway and returning a capped qs doesn't really make sense either. And if the app blows up, chances are high it's an app the user can restart almost immediately.

If you still don't want to throw, I'd vote on counting the & before actually parsing and then return {}. So we don't waste precious time parsing garbage and blocking everything else.

buschtoens commented 10 years ago

Anyway, you have the last word. Cap, count and {}, or throw? I'll implement it.

eivindfjeldstad commented 10 years ago

I was under the impression that Google already fixed this by using a random seed for hash tables. Either way, I think throwing should be avoided. Maybe just break the loop and print a warning or something?

https://github.com/joyent/node/commit/146b2e267b533d31a537414a6a1bb7009b814974

tj commented 10 years ago

yeah the hash thing was a different issue, right now we just allow arbitrarily long query-strings so you could still get away with impacting a service, just like if you were to sent 500mb of json or something and didn't have a limit on request bodes.

I'd be ok with capping or {}, both are potentially confusing for legitimate requests if a dev just sends something large expecting it to work, maybe we should add a console.warn() in there

eivindfjeldstad commented 10 years ago

ah, I see. I think the node querystring module has a default limit of 1000 keys as well. seems like a reasonable default to me.

buschtoens commented 10 years ago

Okay, so no throwing, but console.warn(). Now there are two options left: Cap or count the delimeters before trying to parse them.

eivindfjeldstad commented 10 years ago

counting the delimiters won't help if someone is doing blah[blah][blah].....[blah][blah]=1

buschtoens commented 10 years ago

True. So we'll go with capping and console.warn().