Temporarily mitigating SQL injection by custom Python proxy

We recently had a 3rd party come in and create a service for us that was tied up to a small set of different systems. The system had a public facing part for our customers and it quickly became a crucial system to keep online. During a quick check of their work it was discovered that a critical SQL injection bug had found a place in there.

Keeping a system running with SQL injection that could possibly lead to an OS shell (Windows) is not a question. The bug needed to be fixed ASAP, and we could not take down the system for several hours while we waited for the 3rd party to respond.

The system’s backend was running on ASP, our customers would interact with only one page and they could upload a picture and enter some personal details (name, birthday, address, etc.). We needed something quickly that would somehow mitigate the SQL injection.

Besides taking down the service and editing the source code, what are our options? We can set up a reverse proxy and do some work on there before it hits the service.

Replace “SQL” characters

This is not a good option, it would keep the bug open as there are a sea of different ways to circumvent this as an attacker. A good question about this was asked on Stack Exchange.

It could possibly be enough for our use to replace quotes with magic quotes, but I wouldn’t have been comfortable with that even as a temporary solution.

Automatically drop all connections containing SQL codewords

This alternative would have been doomed to fail somehow. First of all, we probably would’ve dropped legit connections from people with a SQL codeword in their name, like “Andrew.” Second, it wouldn’t actually work because it is trivial to bypass such a “detection.” Third, it would take me way too much time to find all SQL codewords and I would miss at least one, but probably a dozen.

Create a custom Python script to allow only whitelisted characters

This was the alternative I was the most comfortable with. It has some heavy downsides and could never have been used for a prolonged time period. It allowed us to keep the service running while making sure the service was protected.

I ended up writing a script that runs a Flask server (lightweight Python web framework) that only has one route. When it receives a GET-request it forwards that to the service and returns the content. When it receives a POST-request, it goes through all of the POST-parameters and strips the value from characters not in our whitelist. I met some trouble with ASP though; they have some state parameters that are added to all HTML forms so that the backend can verify that the request is not corrupted. Rather than adding all characters that could be present there, they were ignored if found, they would never hit a SQL statement anyway.

This way I could allow all legitimate users through, though there could possibly be some special characters lost on the way. As a temporary solution with mostly Swedish users, this was a reasonable compromise.