There is a lot of emphasis in the web development world on making urls “pretty” and, more importantly, discoverable. While there isn’t anything wrong with wanting urls that human beings can read and understand, web developers need to understand that the querystring is an entry point into your application if you are passing values in it and therefore is an attack vector.
So, say you have a querystring that looks something like this.
http://lazycoder.com/people/delete/1242
What does 1242 stand for? Why are you passing it in the query string? What happens to the value when it gets assigned to a variable inside your code? Are you sure you have all of your authorization checks in place to make sure that the currently logged in user has permission to edit whatever 1242 represents? The W3C mentions not including sensitive data in the querystring.
Authors of services which use the HTTP protocol SHOULD NOT use GET based forms for the submission of sensitive data, because this will cause this data to be encoded in the Request-URI. Many existing servers, proxies, and user agents will log the request URI in some place where it might be visible to third parties. Servers can use POST-based form submission instead.
I personally get itchy anytime I see PK values in a web interface. It means if an attacker does somehow gain access to my database, they already know a PK value and could possibly pass it in through a security hole.
Encoding or encrypting the values before putting them into the querystring doesn’t completely solve the problem. In fact, it makes the URL ugly again.
What I would suggest is this:
- Encrypt sensitive values (userID, values used in edit/delete actions)
- Place those values in hidden form fields. These can be created on the fly if needed
- Only pass sensitive values using a POST verb.
- Include a secure site/session token in a hidden form to help foil cross-site attacks.
Ideally, I’d like some way in the framework to mark variables as “secure” to prevent any part of the framework from putting them in the querystring. Perhaps raise some kind of warning.
My point isn’t to indict pretty URLs, it’s just to raise awareness of the querystring and it’s potential security risks.
There are also other possible problems with passing params in the querystring. They mainly involve changing data on the server. The W3C explicitly states that HTTP GET is only to be used when no data will be changed on the server.
GET considered harmful.
Get Considered Harmful; Sometimes.
Securing forms with POST isn’t enough









5 Comments
I was just about to ping you to post this so we can continue out twitter-fight as a blog-fight.
But you seemed to have changed the conversation. I agree with everything you said here except for marking a value as “secure” or not.
The “problem” you are describing is as old as the web itself. As long as we’ve been building web applications we’ve had these issues. I agree that all destructive actions should be POSTed to the server and the web app should have proper security in place. I may even agree that you might not want to use PK (primary keys) as your identifier; but only because you may want to grow beyond a RDBMS at some point.
I honestly don’t think that marking values “secure” is a feature that a web application framework should have. That said, if you want it and you can add that functionality with a plugin ala Rails or the extensibility model of the new ASP.NET MVC framework, then knock yourself out. But I would be very hesitant to use a framework that had that option by default though.
To be clear, GET /people/delete/1242 is not a pretty URL. It *is* a dangerous URL. I would rather see one of the following from an authenticated user:
POST /people/1242 (submit=delete)
POST /people/ (submit=delete&person=1242)
DELETE /people/1242
Of course, we all know a post-back from /RemoveUserFromDatabase.aspx?user_ID=1242 is the best way, right?
I tried to remove any reference to a specific framework in this post because it’s possible in any web application if you aren’t careful/knowledgable.
If an app allowed a user to delete something the user does not have the authority to delete via an URL like http://lazycoder.com/people/delete/1242, then the app is negligent towards security.
It really doesn’t make any difference if 1242 is in the URL or a hidden input. Hackers can find the “View Source” button. The URL is not the problem, it’s allowing someone to delete something by changing the id when they don’t have permission to. However, that seems incredibly rare.
The real problem is allowing a delete (or any change) via a GET operation. It violates the semantics we’d expect from the web. But even that normally doesn’t cause problems, because in most cases, authentication should prevent a web-crawler from accidentally deleting something it shouldn’t.
The problem we saw with the Google accelerator was that it was a client technology that ran with the user’s browser stored credentials. Thus it could hit the those delete links with proper authentication.
Again, this is resolved by making sure GET is idempotent and isn’t allowed to change or delete data.
Speaking of security, why are we still using predictable, incrementing integers was primary keys?
Or rather, why are we exposing these to the Intarwebs?
Shouldn’t we have some sort of surrogate key that the app uses (something random, like a non-incrementing, random scatter GUID) that somehow gets mapped to the actual primary key ID in the database (which may be an int)
3 Trackbacks/Pingbacks
[...] an interesting discussion with Blowmage (Mike Moore) over Twitter. I said that having the “param1/param2″ pattern in the ASP.NET MVC made me itchy from a security stan…. He replied. Eh, just don’t add them to the URL. The ASP.NET MVC assumes you know what you [...]
[...] so the last post had some good responses. In that post I mainly looked at it the risk in putting params in the [...]
Interesting Finds: October 20, 2007…
…
Post a Comment