What's on your bookshelf?

A few moments ago I got reminded of a book that I read many years ago. Looking at my bookshelves there are various software development books, some new, some old, some out dated, some superceded, some still waiting to be read (Yes, I have a tendancy to buy books faster than I can read them. Also, sometimes I buy a book and my interests or needs change and so it remains unread for months)

So, what books are on my bookshelves:

What's on my bookshelf 1

  • Human Computer Interaction – A book from my university days. It goes unread these days as it is too rooted in theory and not much practical.
  • Data Communications, Computer Networks and Open Systems – Another book from my university days. It too goes unread these days as I have not had much involvement in writing network software, except on a level where I don’t really need to deal with the network communication plumbing. Although I did once help to write an NDIS driver.
  • Information Modeling and Relational Databases – It looked promising but I’ve so far found it to be a very heavy read. I’ll eventually get back to it, but for the moment it is staying on the shelf.
  • Domain Driven Design – So far I’ve not got much past the first couple of pages, but I’ve only just started reading it. This is one book that I will be finishing sooner rather than later as it is on the reading list of the eXtreme Programming group that I am part of.
  • Design Patterns – It has been a long time since I’ve read this. I remember at the time thinking that I’d managed to figure out a lot of OO pretty well for myself as I’d been using some of the patterns discussed without having ever come across them in a reference such as this.
  • Programming Microsoft Windows with C# – This was one of the first books on .NET I ever bought. I found it a great book as it kind of joined up the traditional thinking of Windows applications with the .NET Framework. And, of course, just about everyone who has programmed with the Win32 API and done battle with the GDI code knows of Charles Petzold
  • Patterns of Enterprise Application Architecture – Some of this was quite interesting. It is a book I have taken to dipping in and out of rather than trying to read all the way through. However, some of Martin Fowler’s references to cricket just lose me. He does liken the rules to Baseball so that Americans and Canadians can understand, but it leaves the poor Scots to just shrug and give up.
  • Extreme Programming Explained – Kent Beck’s original work on XP, it is now superceded but I have not yet had a chance to buy the second edition. When I first read this a few years ago I just didn’t get it very well. As far as I could see it was all about not needing to document anything and working in pairs. I have since briefly pair programmed and I am won over – it is much more efficient. But, I still feel that most people misinterpret the documentation issue and that really needs to be cleared up.

What's on my bookshelf 2

  • Writing Secure Code: This is a fantastic book. There isn’t much to say on this except that you really must read it.
  • Database Modeling: I found this a little too academic, but at least it gives examples using Microsoft Visio
  • Code Complete, Second Edition: This has to be one of the best books on how to write good code.
  • .NET Compact Framework: I did a little work using the compact framework a while back and this was a good starting reference. However, if you are already familiar with the .NET framework then don’t be misled by the bulk of the book – you probably know more than half of what is in this book already.
  • Don’t Make Me Think: A great book on how to design good user interfaces on websites. Most of the examples seem common sense, but it is amazing how that just goes out of the window sometimes when deadlines are looming.
  • The .NET Developers Guide to Windows Security: A great book that presents everything in easy to swallow bite sized chunks. That has to be the best thing about the book because a lot of people think about security and shy away from doing it properly because they think it will be hard, and it isn’t. And this book shows you how easy it is.
  • Professional Software Development: This one just really validated my reasons for being a software developer and the ways in which to maintain a professional level of conduct.
  • Code Complete (again). This is the original first edition. I suppose I should really get rid of it since I now have the second edition, but I can bring my self to throw out a book that has served me so well.
  • Writing Sold Code: I got this around the same time as the original Code Complete book. Another must have read for people who churn out code. The examples are in C / C++ but I think the issues are just as relevant for other languages.
  • Industrial Strength C++: I don’t have much call for this book these days, but the advice was great and if you are a C++ programmer I would recommend that you read it.
  • Rapid Development: It has been a while since I’ve read this book. The “There are no silver bullets” is one thing that, at the time, I thought I understood, but it has taken experience to actually back that up. There have been times I’ve looked for quick solutions, but usually they attract the flies like the piles of manure I should have known they were.
  • Lean Software Development: This was previously on the eXtreme Wednesday reading list. You can find a review on it here.

NOTE: This was rescued from the Wayback Machine. The original was dated Sunday, 30th January, 2005.

Screensavers that attack spammers

While I hate receiving spam, I feel that the latest offering from Lycos to try and tackle spam by hitting Spammers where it hurts – Right in the bandwidth – is highly irresponsible.

If you are not aware of what I am talking about then I am talking about the Make Love Not Spam[^] website by Lycos. It offers you a Screensaver to download which, while running, will hit spammers’ websites. It works by the screensaver requesting from a central database a spammer to attack. The central system monitors the spammers website so that it isn’t completely disabled (how thoughtful) and if one site is getting near the brink will instruct the screensavers to go elsewhere. It doesn’t take too much of the user’s bandwidth as it only sends the request and then ignores the response.

To me this smacks of vigilantism. While some people welcome that someone is “finally doing something” the problem is that it is unregulated. Some might argue the case that it is just an “eye for an eye and a tooth for a tooth”, but many vigilantes end up hurting their targets or innocent bystanders more than the vigilante’s target ever inflicted on others.

I don’t know what the law is in your part of the world, but I would like to warn anyone in the UK that use of this screensaver may be illegal (I want to emphasise MAY BE illegal – I am not a lawyer). I am referring specifically to Section 3 of the 1990 Misuse of Computers Act.

To quote from guidance from the Home Office website[^]:

Section 3 – Unauthorised modification of computer material
Where a person does any act that causes the unauthorised modification of the contents of any computer a section 3 offence is committed. There must have been the intent to cause the modification and knowledge that the modification has not been authorised. The offence does not have to be preceded by a section 1 offence. This offence covers the introduction of harmful worms and viruses to a system, and denial of service attacks. The offence is punishable on summary conviction for a term not exceeding five years.

While the Lycos screensaver does not completely disable a website it does cause “modification” to the service by slowing down the servers almost to the point of breaking. Any user of Lycos’ screensaver does so with the knowledge that their actions are “harmful”

The advice goes on to say that an offence is committed when the person committing the offence is in England, Wales, Scotland or Northern Ireland at the time of the attack, or that the target computer was in England, Wales, Scotland or Northern Ireland. So, potentially even people outside the UK who hit a spammer’s website that is located within the UK may be convicted under this law.

Finally, let me remind you that I am not a lawyer and this is just my interpretation of the law. If you want to use the screensaver then that is up to you.

NOTE: This was rescued from the Wayback Machine. The original was dated Monday, 29th November, 2004.

What could I do? What could I read? Who could I ask?

Earlier today I was listening to an audio book by Jim Rohn. He is a business author but he has one bit of advice that I find is extremely useful in software development. In the section on “Building Self-Enterprise” he has this to say:

“So what is the problem? State the problem. Write it down…. And I’ve got three questions to ask [yourself] in order to solve the problem.

“The first question you need to write down is this: What can I do? Because, you don’t want to go any further than that if you can solve it yourself. Then what you do is develop working papers and start jotting down [that] I could do number one, and I could do number two, and I could do number three. There are some alternatives. Then you start analysing them.

“Now if that doesn’t work then here is number two: What could I read? Maybe there is a book on my problem. Somebody spent a lifetime trying to figure out this problem. Maybe it is written out in concise language somewhere to give you the instant benefit of someone’s advice. You don’t need to reinvent the wheel. Do your homework and find the solution. And then you start to develop some working papers on what you are reading. Don’t miss the book that could help.

“So, don’t sell yourself short here. You can find some answers. Now, first try and find them yourself from your own experiences. Then second, if you can’t find them yourself then ask ‘what could I read?’ Go to the library. Go to the bookstore. Search your own library. Go back through your own journals to find the stuff that has been helpful and valuable and see if maybe you have made some notes that could be helpful in your situation.

“Now, if that doesn’t work then ask question number three. And question number three is: Who could I ask? Now, guess what you are prepared with when you ask somebody to help you. You’ve got your working papers to show them. You say I’ve tried my best to figure it out myself, and that finally left me short. Here are some of the books I’ve read. I’ve researched this material and I’m still short.  Now, could I possibly ask you? And could you possibly help me? You can’t believe how willing somebody will be to help you if first of all they’ve got the idea that you were willing to help yourself.

That is fantastic advice, even although he applies it to building a business, it also works for solving problems in software development. So many times I see in software development forums people ask questions that they obviously can’t be bothered to try to help themselves first. So many times I see people post their homework questions verbatim to the forum and just ask: How do I do this? Write some code for me. Give me the code for this.

NOTE: This was rescued from the Wayback Machine. The original date was Wednesday, 24th November, 2004.

Speed Kills

Speed Kills not just on the road, but in software development too. If you don’t believe me then just ask Uncle Bob[^].

I remember years ago I was crazy like that. “Gotta get this done, gotta get this out the door.” I was working until all hours of the night, heck I even worked on Christmas day cranking out code. Eventually the sales guys said, “we need this feature or this prospect won’t buy, and if they buy they’ll buy big.”  But…. “The feature is going to take months to implement. The code is a mess.”

Why would anyone code like that? Well, in my first job the schedules were somewhat unrealistic, moreover the scope was constantly changing. Being fresh out of university I had no idea how to estimate properly so I went with the premise that these people have been “out there” 10 to 30 years longer than I have. Also, I was a little too eager to please. With hindsight I can see that my gut feelings were right, but at the time any argument was met with somewhat patronising responses that I was still a “boy” and didn’t know how the “real world” worked. Eventually, I did manage to push through process changes to, at least, formalise things a little better, but by that point most of the damage was done.

What I can be thankful for now is that I don’t have to work like that any more. Schedules are realistic, code is better formed – sometimes a little rushed in areas due to an impending deadline, but not to the overall detriment of the project.

Thanks to Abdel Saleh[^] for the link.

NOTE: This was rescued from the Wayback Machine. The original was dated Wednesday, 6th October, 2004.

Running SQL Scripts with a .NET Application

I was recently asked to show how to run a SQL Script on a SQL Server but being controlled by a .NET application. The other guy had been trying to use isql for the task but somehow wasn’t getting it to work. I commented that I’d needed something similar for three different projects recently, but I’d taken a completely different approach. At the time I rushed an explanation of how I achieved the same result. So here is a less rushed explanation of how to run a SQL Script on a SQL Server. I would also imagine this would work for other database systems as well with some slight modifications.

At its simplest you can throw just about any SQL statements you like through the SqlCommand object in .NET and have .NET execute them.

 

SqlConnection connection = new SqlConnection(connectionString);
SqlCommand command = new SqlCommand(script,connection);

That’s it. You can just call command.ExecuteNonQuery(); if you don’t expect, or are not interested in, any values that might come back. You could have a SqlAdapter.Fill a DataSet, or a get a DataReader.

For a demonstration I have put a simple application together that reads in a script from a file, or even from embedded resource and runs the script it finds. If the script returns any data a DataSet is populated and the tables are then output to the console.

The demo application takes two command line arguments. The first is either a -r (for embedded resource) or -f (for a file). The second argument is either the name of the resource, or the name of the file. For example:

RunSQL -r GroupedTurnover

runs the embedded script called RunSQL.GroupedTurnover.sql. For convenience the application will add the prefix and the suffix.

The file can contain GO delimiters so that you can have the application process the script in batches rather than need one script per batch.

Downloads:

NOTE: The demo application needs a default instance of SQL Server 2000 or MSDE running on the local machine which contains the Northwind database. If you download the source code you can, of course, change these settings to suite your need.

NOTE: This was rescued from the Wayback Machine. The original was dated Friday, 15th October, 2005. The downloads currenly point to the Wayback Machine version. These will be updated shortly.

The stored procedure now runs how fast?

A few days ago I was having some problems with a stored procedure taking too long. On our test server the stored procedure ran acceptably in a few seconds so it came as somewhat as a shock when trying the application out on the client’s test server that the same code was timing out after 20 minutes. Now given the number of times this stored procedure was going to be called the data aggregation and extraction utility it was part of would take somewhere in the region of 2 weeks to run.

So, I asked a collegue to have a look at my stored proc to see if a second set of eyes could see where the problem might lie. One of his suggestions was to pull the main select (which ran to about 2 screens of text) apart and run the subqueries independently so that we could looks at the different parts in more detail in order to determine where the bottleneck was. So I did that and discovered that the individual parts ran a lot quicker than the whole. So, I wondered, what if I just dump the results of these extracted subqueries into table variables and just plug the table variables into the main select. Curiously, it went back to taking a few seconds to run the stored procedure.

It would seem that the query optimiser got itself in a bit of a fankle over joining the subqueries up with the main query. But by extracting them to put their results in to table variables only around 7000 rows were used in the final join rather than the near 200 million (of which all but ~7000 were discarded during the join operation) . This speed improvement is very important as the live server is likely to have many millions of more rows since we last copied it to use as the test system.

When operating on very large datasets with joins onto subqueries, it can be useful to extract the subqueries in to temporary tables or table variables in order to speed up the query, and as a bonus the stored procedure will be easier to read.

NOTE: This was rescued from the Wayback Machine. The original was dated Sunday, 31st October 2004.

Iteration in .NET with IEnumerable and IEnumerator

A discussion broke out on Code Project recently about why .NET has two interfaces for iteration (what .NET calles “enumeration”).

What are the two interfaces and what do they do?

The IEnumerable interface is placed on the collection object and defines the GetEnumerator() method, this returns a (normally new) object that has implements the IEnumerator interface. The foreach statement in C# and For Each statement in VB.NET use IEnumerable to access the enumerator in order to loop over the elements in the collection.

The IEnumerator interface is esentially the contract placed on the object that actually does the iteration. It stores the state of the iteration and updates it as the code moves through the collection.

Why not just have the collection be the enumerator too? Why have two separate interfaces?

There is nothing to stop IEnumerator and IEnumerable being implemented on the same class. However, there is a penalty for doing this – It won’t be possible to have two, or more, loops on the same collection at the same time. If it can be absolutely guaranteed that there won’t ever be a need to loop on the collection twice at the same time then that’s fine. But in the majority of circumstances that isn’t possible.

When would someone iterate over a collection more than once at a time?

Here are two examples.

The first example is when there are two loops nested inside each other on the same collection. If the collection was also the enumerator then it wouldn’t be possible to support nested loops on the same collection, when the code gets to the inner loop it is going to collide with the outer loop.

The second example is when there are two, or more, threads accessing the same collection. Again, if the collection was also the enumerator then it wouldn’t be possible to support safe multithreaded iteration over the same collection. When the second thread attempts to loop over the elements in the collection the state of the two enumerations will collide.

Also, because the iteration model used in .NET does not permit alterations to a collection during enumeration these operations are otherwise completely safe.

These names are confusing, why didn’t Microsoft just have an IEnumerator and a ISafeEnumerator and get rid of the IEnumerable? These would convey a much better meaning to the developer as the lack of distinction in the terminology will always make it more difficult to remember which was which.

IEnumerator and ISafeEnumerator would have broadly the same implementation without any real performance gain. It is already stated in the MSDN documentation that code in a loop is not permitted to change the contents of the collection that is being looped over, so in reality all enumerators are safe so long as the instances of enumerator objects are not shared between different loops at the same time.

And as for the lack of distinction in terminology, the suffixes make the distinction. Words in English that end in -able denote the ability to do something. In this case enumerable means the ability to enumerate. Words ending in -or, called agent nouns, denote someone or something that will perform some work. In this case enumerator means something that enumerates.

NOTE: This was rescued from the Google Cache. The original date was Saturday 11th September, 2004.

Tags:

The benefits of Stored Procedures

There are a number of ways to access data in SQL Server, or any enterprise DBMS. There are lots of books that discuss getting data in and out of databases and the best ways to do that. Many advocate the use of stored procedures to ensure the safety of the data.

The tree main benefits that I see on stored procedures are:

  • Abstraction
  • Security
  • Performance

Stored Procedures add an extra layer of abstraction in to the design of a software system. This means that, so long as the interface on the stored procedure stays the same, then the underlying table structure can change with no noticable consequence to the application that is using the database.

For instance, if the database has to be denormalised to get a little extra performance in certain situations then the stored procedures can handle the additional updates and inserts necessary to ensure the integrity of the data across the tables. Without this the each of the callers would have ensure that these changes had taken place. Of course, the use of stored procedures does not in anyway grant a waiver from properly designing the data model, but it can help if the perfect normalised model has to give way for performance improvements.

This layer of abstraction also helps put up an extra barrier to would be intruders. If access to the data in SQL Server is only ever permitted via stored procedures then permission does not need to be explicitly set on any of the tables. Therefore none of the tables should ever need to be exposed directly to outside applications. For an outside application to modify the database, it must go through stored procedures.

Stored procedures can be written to validate any input that is sent to them to ensure the integrity of the data beyond the simple constraints otherwise available on the tables. Parameters can be checked for valid ranges. Information can be cross checked with data in other tables.

Even if it is thought that someone attempting to crack into a website will never get this far in, from a security perspective, anything that can reduce the attack surface is beneficial. 

Performance can be improved by the use of stored procedures. They are precompiled so when they are run there is no additional lag as the SQL is parsed, compiled, execution plans drawn up and then run, they just run because all that extra work is done at the time the CREATE PROCEDURE or ALTER PROCEDURE commands are run rather than when procedures themselves are run.

Another area in which stored procedures improve performance is that is pushes all the work onto the server in one go. A stored procedure can perform a series of queries and return many tables in, what is to the outside world, one operation. This saves the calling process from making many requests and the additional time of several network roundtrips. It also means that, if the contents of one set of data being returned is dependent on the results of a previous set of data that is being retrieved through the same stored procedure, that the data only has to flow from the database server to the application. If stored procedures were not being used it would mean that the data from the first database call has to get sent back to the database for the second call in order for it to continue retrieving the information needed by the application.

For instance. Lets say that Northwind traders send out a quarterly statement to its customers, and that for each statement certain information needs to be extracted from the database. The tables Customer, Order and Order Details are used. This information could be retrieved in several steps by calling the database for each set of information as it is needed to generate the statements. First with a SELECT * FROM Customers WHERE CustomerID = @CustomerID. This gets the details for the head of the statement. Then a SELECT * FROM Orders WHERE CustomerID = @CustomerID AND OrderDate>=@StartDate AND OrderDate<=@EndDate to get the details for each individual order by that customer. And finally a series of calls (one for each of the Order records that were retrieved) like SELECT * FROM [Order Details] WHERE OrderID = @OrderID

Assuming that the customer in question is “Rattlesnake Canyon Grocery” and the period for the statement is Q1 1998 then that is 5 roundtrips to the database and 5 times the database has to parse some SQL. This could be done by a single stored procedure that takes only one trip to the database and is precompiled.

 

CREATE PROCEDURE GetQuarterlyStatement
@CustomerID nvarchar(5),
@StartDate datetime,
@EndDate datetime
AS
SELECT * FROM Customers
    WHERE CustomerID=@CustomerID
SELECT * FROM Orders
    WHERE CustomerID=@CustomerID
    AND OrderDate>=@StartDate
    AND OrderDate<=@EndDate
    ORDER BY OrderDate DESC
SELECT [Order Details].* FROM [Order Details]
    INNER JOIN Orders ON [Order Details].OrderID = Orders.OrderID
    WHERE CustomerID=@CustomerID
    AND OrderDate>=@StartDate
    AND OrderDate<=@EndDate
    ORDER BY OrderDate DESC
GO

 

The stored procedure is now doing in one trip what previously took 5 trips. Of course, this example is somewhat contrived for brevity, in a real application there would be joins to the product tables, and the columns would be listed rather than using SELECT * and so on.

NOTE: This was rescued from the Wayback Machine. The original date was Friday, 1st October, 2004.

Tags:


Original comments:

A drawback using stored procedures is portability. For each targetted DBMS, you may have to rewrite SP code. If a client side abstraction such as ODBC or ADO is used, one can keep a common code base, making CM and Release Engineering activities easier. Of course it comes with a price – performance.

10/12/2004 12:23 AM | Jörgen

 

Jörgen, While that is a potential drawback, in my experience when moving from one DBMS to another the client side code has to change anyway regardless of client side abstraction. However, with a well structured DAL, the impact can be kept to a minumum.

10/12/2004 12:28 AM | Colin Angus Mackay

SQL Injection Attacks

Every day I see messages on various forums asking for help with SQL. Nothing wrong with that. People want to understand how something works, or have a partial understanding but something is keeping them from completing their task. However, I frequently also see messages that have SQL statements being built in C# or VB.NET that are extremely susceptible to injection attack. Sometimes it is from the original poster and, while they really need to learn to defend their systems, that is fine as they are trying to learn. Nevertheless there is also a proportion of people responding to these questions that give advice that opens up gaping security holes in the original poster’s system, if they follow that advice.

Consider this following example:

C#

static DataSet GetCustomersFromCountry(string countryName)
{
    SqlConnection conn = new SqlConnection("Persist Security Info=False;"+
        "Integrated Security=SSPI;database=northwind;server=(local)");
    string commandText = string.Format("SELECT * FROM Customers WHERE Country='{0}'",
        countryName);
    SqlCommand cmd = new SqlCommand(commandText, conn);
    SqlDataAdapter da = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds);
    return ds;
}

VB.NET

Function GetCustomersFromCountry(ByVal countryName As String) As DataSet
    Dim conn As SqlConnection = New SqlConnection("Persist Security Info=False;" + _
        "Integrated Security=SSPI;database=northwind;server=(local)")
    Dim commandText As String = String.Format( _
        "SELECT * FROM Customers WHERE Country='{0}'", _
        countryName)
    Dim cmd As SqlCommand = New SqlCommand(commandText, conn)
    Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
    GetCustomersFromCountry = New DataSet
    da.Fill(GetCustomersFromCountry)
End Function

What happens here is that what ever the value of countryName is will be inserted (injected, if you prefer) directly into the SQL string. More often than not I see examples of code on forums where there has been absolutely no checking done and the developer has used countryNameTextBox.Text directly in the string format or concatenation statement. In these cases just imagine what the effect of various unrestricted text box entries might be.

For instance, imagine the values a malicious user might put in the text box on a web form. What if they type ';DROP TABLE Customers;-- ?

That would expand the full SQL Statement passed by the .NET application to be

SELECT * FROM Customers WHERE Country='';DROP TABLE Customers; -- '

So, no more customers (at least in the database… But how long in real life?)

Some people might then say, sure, but who in their right mind would give that kind of access on a SQL Server to the ASP.NET account? If you ask that question then you cannot have seen the number of people who post code with the connection strings clearly showing that, firstly, they are using the sa account for their web application and, secondly, by posting their problem to a forum they have given to the world the password of their sa account.

Some others might say, yes yes yes, but wouldn’t an attacker would have to know what the overall SQL statement is before they can successfully inject something? Not so, I say. If you look at code posted on forums it becomes obvious that the vast majority of values from textboxes are inserted right after an opening apostrophe, like the example above. Based on that assumption, all an attacker needs to do is close the apostrophe, add a semi-colon and then inject the code they want. Finally, just to make sure that any remaining SQL from the original statement is ignored they add a couple of dashes (comment markers in SQL)

These defenders-of-bad-SQL-because-you-can-never-completely-secure-your-system-anyway-so-why-bother will often follow up with, okay okaay! But the attacker would have to know the structure of the database as well! Well, maybe not. Normally there are common table names. I’m sure most people that have been dealing with databases for a few years will have come across many with tables with the same names. Customers, Users, Contacts, Orders, Suppliers are common business table names. If that doesn’t work it may be possible to inject an attack on sysobjects. Often an attacker just gets lucky or notices a quirky output when entering something unusual and uses that to work on cracking the web site or database.

So here I present three tips for improving the security of your SQL Server database. In no particular order, they are: Use parameterised queries. Login using an appropriate account and grant only the permissions necessary. Use stored procedures.

* Using parameterised queries is really very simple, and it can make your code easier to read, and therefore to maintain. Parameters also have other advantages too (for instance you can receive values back from parameters, not just use them for sending information into the query). The previous code example can be changed very easily to use parameters. For instance:

C#

static DataSet GetCustomersFromCountry(string countryName)
{
    SqlConnection conn = new SqlConnection("Persist Security Info=False;"+
        "Integrated Security=SSPI;database=northwind;server=(local)");
    string commandText = "SELECT * FROM Customers WHERE Country=@CountryName";
    SqlCommand cmd = new SqlCommand(commandText, conn);
    cmd.Parameters.Add("@CountryName",countryName);
    SqlDataAdapter da = new SqlDataAdapter(cmd);
    DataSet ds = new DataSet();
    da.Fill(ds);
    return ds;
}

VB.NET

Function GetCustomersFromCountry(ByVal countryName As String) As DataSet
    Dim conn As SqlConnection = New SqlConnection("Persist Security Info=False;" + _
        "Integrated Security=SSPI;database=northwind;server=(local)")
    Dim commandText As String = "SELECT * FROM Customers WHERE Country=@CountryName"
    Dim cmd As SqlCommand = New SqlCommand(commandText, conn)
    cmd.Parameters.Add("@CountryName", countryName)
    Dim da As SqlDataAdapter = New SqlDataAdapter(cmd)
    GetCustomersFromCountry = New DataSet
    da.Fill(GetCustomersFromCountry)
End Function

* The application should be set up to use a specific account when accessing the SQL Server. That account should then be given access to only the things it needs. For instance:

GRANT SELECT ON Customers TO AspNetAccount

It is generally unwise to GRANT permission ON someObject TO PUBLIC because then everyone has the permission.

* My final tip is to use only stored procedures for selecting and modifying data, because then the code that accesses the tables is controlled on SQL server. You then do not need to grant access directly to the tables, only the stored procedures that are called. The extra protection then comes by virtue of the fact that the only operations that can be performed are those that the stored procedures allow. They can perform additional checks and ensure that relevant related tables are correctly updated.

NOTE: This was rescued from the Wayback Machine. The original post was dated Saturday, 25th September 2004.

Tags:


Original posts:

Excellent post Colin. I’d always wondered what a SQL Injection attack was without actually bothering to Google for it – and now I know.

9/26/2004 6:08 AM | Rob

this is great job, colin. i’ve just know the words ‘SQl injection’ but dont know what it exactly means. now i’ve got it. i can defend my own database.

thanks

10/3/2004 3:41 PM | Fired Dragon

I’ve seen people who’ve read this article thinking they can’t do it because you’ve only given .NET syntax. You should probably emphasize that ‘classic’ ADO also has Command objects with a Parameters collection, as does the more obscure underlying OLE DB object model. You can also use parameters with ODBC. There’s no excuse – parameters are cleaner, more secure, and less prone to error than building a SQL string. The slight disadvantage is that the code becomes a little less portable – SQL Server uses the @param notation, Oracle uses :param, and the OleDbCommand and OdbcCommand both use positional parameters marked with a ?

12/18/2004 1:01 AM | Mike Dimmick

I just can’t get this to work. I do exactly as the example shows, and the query doesn’t replace the parameters with the values to search for. The query came up with nothing, until I entered a row in the database with the name of the parameter as a field. It finds that, so it just doesn’t parse the command. I don’t get it.

7/27/2005 10:46 AM | Vesa Ahola

Since I cannot see your code I cannot see what is going wrong. However, I wrote a longer article about the subject over on codeproject.com. Perhaps that may help. See:

http://www.codeproject.com/cs/database/SqlInjectionAttacks.asp

7/27/2005 4:28 PM | Colin Angus Mackay

UPDATE: Sql Injection Attacks

As a follow up to my post on preventing SQL Injection Attacks a couple of months ago I just found this little nugget, I Made a Difference[^], and it shows what can be achieved if you don’t secure against SQL Injection attacks – and with only 3 hours of effort. Obviously, if you have access to the source code you will be able to launch an attack much quicker.

The original link seems to have disappeared. See the Wayback Machine for an archived copy, or the quoted section below:

Two weeks ago, I taught a Guerilla .NET course for DevelopMentor in Boston. Two or three days ago, a student who listened to me rant about SQL Injection attacks during the Code Access Security module lecture sent us (myself and the other two instructors) the following. It’s obviously been edited to protect the guilty:

“Hi, Ted. I want to thank you for the short primer on SQL injection attacks at the Guerrilla course in Woburn this month. We have a vendor who supplies us with electronic billing and payment services. (We send them billing data, and they present the bills to our customers and take the payments for us.) The week after the Guerrilla class I began to lose confidence in their application for various reasons, like seeing errors that included partial SQL statements, and in one case, a complete SQL statement that was accidentally left on a page from a debugging session. I told our company’s business manager that I was 80% confident that I could hack into their site using SQL injection. He called the vendor, who swore up and down that after spending $83,000 on firewalls that no one could ever hack into their site, and that we should go ahead and try.

“After three hours and a Google search on SQL injection, I was running successful queries from their login page and I had their server emailing us the query results via xp_sendmail. I was also able to confirm that the SQL Server login they use for their application has sa rights. I got a list of their clients, and was able to create tables in their databases.

“The vendor promised that by the next morning everything would be fixed. So the next morning at 8:00 am I tried again. I was no longer able to get results via xp_sendmail, but I was able to shutdown their SQL Server service by sending a shutdown command. I followed that up with a friendly call to their tech support line to let them know that they needed to restart SQL Server–I didn’t want to be too malicious. The guy at the other end of the line apparently had been there the entire night changing code and rolling out pages. He threatened to get on a plane, come to my office, and beat me up.”

“The disturbing thing about the incident is that there is enough data in the vendor’s database to allow someone to commit identity fraud or steal credit card and bank account numbers. And they are not a mom and pop shop either–their client list includes F—-, D—-, D—-, and V—-. [These are names that you would recognize, dear reader.] If I had been malicious I could have stolen data from any of those companies.”

Hunh. Three hours and a Google search was all it took. Anybody still think firewalls are the answer? “Security is a process, not a product.” — Bruce Schneier, Secrets and Lies.

NOTE: This was rescued from the Google Cache. The original date was Wednesday 17th November, 2004.

Tags: