How can you prevent denial of service attacks? Most of the techniques you’ll use to prevent a DOS attack are network related. This podcast is about programming so I’ll explain some things you can do that will make your software more resistant to attack. This episode continues the discussion from last week and builds on the same silly story. We’ll go back to the regular QA Fridays next week.
The audio goes into more detail but if I had to sum up the main lesson from the podcast, it would be this:
Don’t let a client machine spend a small amount of work that causes your program running on a server to spend a large amount of work unless you are sure that the request is legitimate. And even then, you will want to spread this work so it’s even. Don’t let even legitimate customers overflow your services.
You want to validate requests as soon as possible. This first check may not even be a complete check. All it needs to do is some simple sanity checks to make sure that a request looks somewhat legitimate. If you find something suspicious or obviously wrong, then also don’t spend a lot of time preparing a long response. Either completely forget about the request or send a short and quick response.
Listen to the full episode or you can also read the full transcript below.
There’s always a balance between security and ease of use and openness that you need to find when programming. In the story, the restaurant was open to the public and that let attackers send bins that the workers had to deal with.
The first thing you can consider when designing a program is who needs to use it? Does it really need to be open and available to the public? Maybe it’s only needed by employees within the company. Even if it needs to be more widely available, is it possible to ask visitors to register or sign-up for service before being allowed access?
Maybe you can setup two services. One is for the general public to try and get a feel for what value you can provide before agreeing to become a paying customer. Your paying customers can then move off of the free service and enjoy an even higher level of service. A DOS attack relies on hundreds or thousands of computers all trying to use your service at once. If you take this approach, then only your trial version will be attackable because the bad guys are not going to become paying customers. I shouldn’t say that only the trial version is attackable, the attackers will have to use different methods to try attacking the paying service.
Even though this is still a bit network related because it involves splitting your service into two separate services, I mention it with programming because it will affect your design. You’ll need to plan for this and probably write some amount of code to help support it. Anything you can do that limits which portions of your service can be attacked at any given time will help you fight these attacks.
In general, DOS attacks are more successful when attackers can spend very little time and resources that cause your program to spend a lot of time and resources. The variety of DOS attacks is huge because each can be customized to cause your program to maybe spend a lot of processing time, or maybe overflow the memory you have set aside to handle incoming requests, or maybe cause your program to spend time reading or writing information to disk, or maybe your program passes the request to another server running some other program that you wrote. The attackers could be going after your database, or your identity providers, or trying to confuse your load balancers.
This is why DOS attacks are so difficult to fight. And why writing smart software that keeps the potential for a future DOS attack in mind might just allow you to save your company some day.
If DOS attacks are trying to get you to spend a lot of time and resources, then it seems that a very good thing you can do as a programmer is to reject requests as fast as possible. Don’t let an attacker trick you into performing some CPU intensive calculation just because they asked for it. And remember that error handling within your code can sometimes also involve a lot of instructions that take time and resources.
In the story, I mentioned how the restaurant workers opened the bins and took out the orders first. Then placed those orders and came back with the food before collecting the payment. Now, this makes it very easy for an attacker to submit an order for a time consuming menu item and let the restaurant work busily preparing the order when there’s no payment available. After the worker comes back with the food and checks the payment is the first time that a problem is noticed.
What this means for you as a programmer is that a few extra checks made first even if they’re out of the normal sequence can help you spot a bad request. You don’t need to even send a reply for this because that’s just extra work. Once you spot a bad request, drop it as fast as you can. If you do send a reply, make it quick and short.
There’s more tips you can do with your code to prevent DOS attacks and I’ll explain these after we take a moment to thank our sponsor.
( Message from Sponsor )
Okay, let’s say that you write your program with some extra checks to make sure everything is included and at least looks proper without spending too much time at this point. Remember that the attackers are smart too. If they know you’re going to reject orders without any payment information, then they’ll still want you to do as much work as possible before you figure this out. So imagine that bins start arriving at the restaurant that contain huge orders. They’re overflowing with the orders. The workers now have instructions to make sure that payment information is included before bringing out the food. But with this modified attack, they’re now spending extra time just reading through the orders. In fact, the attack is actually worse now than when it started. It’s very quick for the attacker to put together these massive orders because you see, they’re actually all the same.
This is bad. Anytime you allow attackers to do work once that they can then use to repeatedly cause your program to do more work, then the attacker has extra leverage.
How would you write code to help prevent this new attack? A simple method. And by the way, that’s exactly what you’re looking for because simple methods usually don’t require a lot of CPU time. A simple method is to just reject any request that’s too big. If a customer really wants to order 10 million sandwiches, then they’re not going to complain too much if they have to place this order by phone or in person. Find a reasonable limit that you can put in your code and drop anything beyond that limit as quick as you can. Remember your goal is to make the software reject malicious requests without using extra processing time or extra disk space, or extra memory, etc.
You also want to recognize the variety of attacks that you might face. What programming steps can you take to help with this? One of the best things you can do is design your software to be modular and allow extra modules to be added where needed. What do I mean by this? You get a request and you don’t know yet if it’s legitimate or an attack. If you design your software so that you have components that can run on one or more machines that are designed to test the validity of incoming requests, then they’ll help isolate the rest of your system. If you start getting a surge in traffic, then the administrators who are monitoring your program can add more validation checking modules as needed. It’s like calling for reinforcements. You can help this by preparing your software designs to allow for this.
What if the attacker decides to try logging into your application from many sources? A modular design will help you here too. Just add more login component servers to help. If you have a paid service like this that requires customers to login first before being served, then you’ll want to enforce the validity of requests as early as possible. You don’t need to do extra work for invalid requests. Just reply with a quick message letting the customer know that the request is not authenticated. Make sure that this part of your code is super fast and lightweight because it’ll be one of your first defenses against an attack.
I’ll end this episode with one final piece of guidance. Make sure that you test your application under a high load. If you can get your application to break, then you might as well put up a sign that reads, “Open for attack.” Look at each of the properties that can be included in a request and ask yourself what would happen if… Only you and your attackers can ask these questions. And I guarantee that your attackers will pay attention to these details. Make sure you ask the question first and deal with it. If one of your properties is a count of how many items are being requested, then put limits on this count. Don’t let somebody ask for a million items. Or maybe even 100 is too many. Try requesting obviously wrong things. Anything that can confuse your program should be dealt with swiftly. Don’t let an attacker ask for -50 items because it could cause some other part of your program go into expensive error handling mode.
Also watch out for small and simple requests that pass your initial tests but are slow and expensive to complete. You might reserve special and time consuming operations only for certain customers or limit how many slow requests any customer can make in a given time. These operations might include searching, scanning, analysis, etc. This falls under the guidance of testing your application to make sure it works under a high load. This type of load is caused by legitimate customer use. If you know that your real customers put your systems under a high load at predictable times, then all you’re doing is making it easier for attackers to also attack during the same time. Try to spread out your usage even for legitimate customers. This helps reduce your peak loads by averaging everything.
Remember if there’s a weak link or a crack, then that’s going to be the best spot for an attack. You need to reinforce that weak area. Don’t try to hide it because I guarantee that attackers will find it.