Posts tagged with security - page 2
Earlier this week the ASP.NET article of the day linked to 4-Tier Architecture in ASP.NET with C# which I noticed suffered from both HTML and SQL injection. I promptly informed the author and the ASP.NET site (who pulled the link) but the author was rather unconcerned and wrote (after editing my comment):
Thanks for your feedback Damieng. Sql statement has been used here to make the tutorial simple, it is informed in the DAL description.
The problem is people borrowing this code may not notice the vulnerability or understand how to fix it. This isn’t the first time I’ve seen easily exploited sample code, responded and been buffed off with the it’s just sample code excuse.
Writing secure code isn’t difficult, time consuming or confusing to read.
If you must write your own data-access-layer (DAL) code use parameterized queries and not string concatenation.
When outputting values be 100% sure whether your technique will encode the values for you or not and be aware of what encoding tools are available to you.
ASP & ASP.NET’s Response.Write and <%= %> methods do NOT encode for you and you should be using HttpUtility.HtmlEncode to output data to a HTML stream.
Samples of vulnerable and secure code are in my presentation on Web Security I gave at the Guernsey Software Developer Forum a few months ago.
Last nights Guernsey Software Developers Forum meeting was sparsely attended with a number of the regulars attendees absent. There were however two new faces including Kezzer who I’d been chatting to on-line for years.
Hopefully the low numbers were down to the seasonal summer holidays and the subsequent knock-on effect that we couldn’t get email out to the BCS Guernsey division to gather sufficient awareness.
I did a short presentation on Web Application Security for Developers that covered HTML injection, SQL injection and cross-site scripting including some live demonstrations on sample code. Slides and sample are available although without audio or screen cast of the demonstrations until I work out how to do that with Keynote.
I’ve come to the conclusion that putting presentations together takes me around 1 hour of preparation to 1 minute of presentation…
This presentation is now available on-line.
I will be giving a talk about web application security tonight at the Guernsey Software Developers Forum.
Web application security is a very large subject I will only be covering SQL injection, HTML injection/cross-site scripting and input manipulation. If there is enough interest I’ll consider covering other subjects such as session hijacking, defensive programming, hashing etc. in a future talk.
As always the doors are open to everyone opening at 6pm tonight (Wednesday 15 August 2007) at the Guernsey Training Agency above the Post Office in Smith Street.
The talk will last around 30 minutes but the meetings tend to go on to around 7:30pm for those that want to discuss it or other developer topics.
Back in ’98 I was developing an extranet site for a local company when I realized that it would be open for exploit if somebody put single quotes in text fields. It was early in the development cycle so I fixed it and moved on, unable to find out how other people were avoiding the problem.
It turned out many were not and it became a well-known exploit called SQL injection. Unfortunately there are many developers who don’t know or appreciate the problem, and it is this:
If you build SQL by appending strings and data without correct encoding your application can be exploited. These exploits can range from exposing sensitive information, through to modification and deletion of data.
This problem is very real and applies to:
- All SQL statements, not just SELECT
- All database systems, not just MS SQL or MySQL
- All programming environments, not just C#, PHP or ASP
- All data, most essentially that obtained from end-users, regardless of client-side checking
Let’s walk through an example and see how it works and what can be done to avoid it.
Example: User login
We have a user-name and password from a web form and want to get the users ID from the database, or nothing if it wasn’t valid. We want to send something like this SQL statement to our database.
SELECT UserID FROM Users WHERE UserName='Bob' AND Password='test'
And so a developer might do something like this (in C# using .NET);
var dr = connection.Execute("SELECT UserID FROM Users WHERE UserName='" + Request("UserName") + "' AND Password='" + Request("Password") + "'"); if (dr.Read()) userId = dr.GetInt32(dr.GetOrdinal("UserID"));
The problem here is that if there is a ‘ in the form fields it effectively breaks out of the selection criteria and allows the end user to add extra criteria or even commands to what we are sending to the database server. Should they enters the following into the password form field…
' OR ''='
Then our code above will send the following SQL to the database:
SELECT UserID FROM Users WHERE UserName='aaa' AND Password='' OR ''=''
Which will return every record in the database and our code will let him log in as the first user it finds – normally a developer or administrator account. Ouch!
Bad solution: Encode it yourself
One solution often adopted is to always ensure all string input has a single-quote replaced by two single-quotes, which is what SQL server expects if you really want to send it a single quote.
This solution fails in that it doesn’t handle numbers or dates and falls apart in that both numbers and dates are often regionally formatted.
Good solution: Let the DB client encode it
A much better solution is to use your environment to perform all the proper encoding for you. As well as protecting you from such exploits you’ll also avoid localization problems where the string representation of something on your client is interpreted differently in your database. This can be a real problem in the UK where dates formatted by a UK web-server are sent to a misconfigured SQL server expecting US formatting and the days and months become transposed without error.
var cmd = new SqlCommand("SELECT UserID FROM Users WHERE UserName=@UserName AND Password=@Password"); cmd.Parameters.Add(new SqlParameter("@UserName", System.Data.SqlDbType.NVarChar, 255, Request("UserName"))) cmd.Parameters.Add(new SqlParameter("@Password", System.Data.SqlDbType.NVarChar, 255, Request("Password"))) dr = cmd.ExecuteReader(); if (dr.Read()) userId = dr.GetInt32(dr.GetOrdinal("UserID"));
Okay there is no doubt this is a little longer but it takes care of all our encoding and localization worries, and if for example you want to insert a lot of data into the database, creating the command and parameters once, then just setting the parameters for each insert (and executing it) will run faster than lots of string building inserts…
Moral of the story
Always encode data properly. If you can use the provided methods and functions to do so. If none are provided grab the specification and find out all the special characters used. Learn what encoding and escape sequences are used and apply them properly.
A few places where data should be encoded properly:
- HTML – Obviously < and > have special meanings and need to be escaped. ASP.NET controls will take care of this if you set the .Text or .InnerText properties but set .InnerHTML at your own peril. Old ASP has the Server.HTMLEncode() function.
- URL – A whole host of rules but the query string is often modified in code. Use URLEncode() or something similar especially if you want XHTML compliance too.
- XML – Again a whole host of rules for what is valid data. Either use an XML object to write out your data (MSXML, Xerces etc) or maybe even store it in
- CSV – Even comma-separated value files have encoding rules. What do you need to do if text is going to have a ” in a field. What happens if a number contains a comma! Find out or use a well regarded library to do it for you.
Notes about the example
A better login system would not allow the web server direct access to sensitive data such as the user table. All access to sensitive information should be through stored procs that enforce those restrictions.
Such a login system would therefore call a stored procedure that logged the attempted, decided if it was valid, and locked out the user if too many incorrect attempts. I’ll blog that if anyone is interested.
Even if you don’t want to do that, returning a single field is better achieved by using ExecuteScalar() and forgetting a data reader.
Microsoft have a developer how-to on injection.