While working with the AJAX Control Toolkit (http://ajax.asp.net), I came across something interesting with the CascadingDropDownList control and ASP.NET page validation.
What Does the CascadingDropDownList Do?
The control is used to create tiered drop-down lists that each depend upon parent values for their own data population. The canonical example given regards using three DropDownList controls to narrow a selection of a car. The first list displays a list of manufacturers, the second a list of models, and the final a list of common packages, with each successive list control populating only the relevant values based upon its parent. For example, selecting “Ford” from the manufacturer list would populate “Focus, Sierra, Probe, F-Series, etc…” in the model list. Selecting “F-Series” would populate “F150, F250, F350, etc…” in the package list.
What’s the Problem?
The Atlas Solution
The only official solution provided by the Atlas team is to disable validation for the page:
However, even that team admits that this must be done with extreme caution and a complete understanding of the consequences. It is fine to disable the automatic validation for a page as long as you are validating the received values manually or if you really trust the users of the application not to inject. (hint: even on intranet apps you never trust the users not to inject because automated bots and viruses running on client machines inside the intranet are becoming advanced enough to post injections to page controls without the user’s knowledge, not to mention the “password on a sticky note on the monitor” hole.)
Why is the Solution a Problem?
The solution can be worked. Server-side validation of incoming data from input controls (including DropDownList controls) is a good practice to begin with. However, it raises some interesting limitations with regards to the standard integration models used by most developers. If, for example, you are using the <ObjectDataSource> control to take parameters from your page controls and feed their selected values directly into a stored procedure or SQL query, you won’t get the opportunity to manually validate the input. This means that a control on the page that is used as a parameter in the ObjectDataSource could have an injection string posted back as its value and sent directly to the stored procedure without the opportunity for server-side validation. This opens up the injection hole and could only be solved by coding some pretty awkward stored procedures. There is a better way.
Finding a Solution
Things We Know
I love the CascadingDropDownList control and want to still use it.
- To avoid ASP.NET raising validation errors, page-validation must be disabled for the page.
- Disabling automatic validation and failing to perform manual validation can open a security hole.
- For the particular page using CascadingDropDownList, don’t use the ObjectDataSource directly. Bind to the control with the three lines of code it takes in the code-behind file. You can still use the TableAdapters and fantastic ASP.NET 2.0 DataSet pattern, you just need to validate the incoming paramters with a few assertions before running off with the input to the database.
- As a lesser, potentially still dangerous, and (to my mind) somewhat messy solution, you could bullet-proof your stored procedures with validation – but this feels like a future stumble and could get ugly in maintenance, testing, and debugging. Just write the manual validation code and be done!
Injection Attacks You Say?
Without page validation a security hole appears. Consider the following scenario. A page is rendered with three items in a drop-down list, arbitrarily the names of three authors. The ID and name of the author are rendered as the value and text respectively. Because this is client-side html, the contents of the list can be modified before the page is posted back. Say you insert the following into the html for the drop-down list:
<option value=”1 or 1=1;“>Foo</option>
This adds a new item to the list whose value is ‘foo’ or 1=1;
If the stored procedure being called was:
WHERE AuthorID = @selectedAuthorID
then the executed SQL would now end with:
WHERE AuthorID = 1 or 1=1;
Why is that a Bad Thing?
Well, for one the stored procedure or sql query will now return every item in the table because OR 1=1 will always evaluate to true. Secondly, there are far, far worse injections that can be performed depending upon the rights of the user that the web application uses to connect to the database. Because most applications have a single user account for the entire web application (restricting admin features only by url or declarative security), that account usually has vastly elevated rights. You can now inject any SQL command after the WHERE clause. For more information on SQL injection attacks, see http://www.unixwiz.net/techtips/sql-injection.html