Guidance for Developers to make it hard on attackers (DDoS)
In keeping with the CISA theme of the day, I thought that I would take some time to talk about DoS. For those out there who are new in the space a denial of service attack (DoS) is the process of making many requests (often more than the server can handle) in an attempt to overwhelm the server. Generally speaking this falls into the low effort, low payout corner of the cyber security space, and has been the favorite of many subclasses of threats on the internet (specifically the cyber activist). When a whole group of people (or services) do it is 'Distributed' denial of service (DDoS).
Now luckily this has been around for about as long as the internet has been, and because of that there are some really good tools that we have to help prevent these types of attacks. But there are some elements of these types of attacks that can be really tricky for developers if they aren't handled properly.
Developer specific considerations for Denial of Service attacks as the application layer, include but are not limited to the following items:
- Exception Handling
- Resource Limiting
- Caching / Queuing Expensive Calls
- Large File Storage
The reason that these are important is because they call out a specific set of Denial of Service attacks that are more difficult to address from the application, because they often look like normal traffic.
Exceptions are a pretty magical thing if you have access to them, compared to many of the older programming languages we have it kind of well off. If you are working in C or C++ sometimes the only information that you are going to get that something went wrong is a "Segmentation Fault", which is not really super helpful. Comparatively, most of the modern languages have greatly improved on this by the utilization of some form of reflection and the concept of the exception. There is only one really bad thing about this. As it turns out, the process of walking the stack and gathering all of the variables comes at a price. I'm not jumping on the 'reflection is expensive bandwagon' that I remember from my consulting days (btw, it's not true -ish, reflection is very fast as a programming path, but when you are doing it in the method explained here it's much worse that the properly handling the issue).
The main problem in this space is that when you receive an exception you are doing an asymmetric workload, which is really the root of this conversation. The idea here is that an attacker will go after any workload where the amount of input work to output work (work-in:work-out) ratio is deeply imbalanced and call that as many times as they can in rapid succession. One of the easier ways to do this is to perform this with an exception.
This brings us to the next are of communication which is resource limiting, and this is one of the places that things like microservices really pay off. Once you have segments of your application that are properly segmented you then have the ability to either scale to meet the need or rate limit the call. If you are doing this you have the ability to make sure that you can prevent the impact to the rest of the application. As long as the system that is being attacked is a secondary system (not needed by the application to operate) then you have gone a long way to reduce the risk to your application. One of the clearest things that I can point to this is that reporting (usually one of the most complicated and complex parts of the application) should always be abstracted away from the application, since these represent easy targets for an attacker (wittingly or not) to kick off a DoS. Anecdotally, on one of the projects that I worked on in the past we had a new user to the system that was upset because the system was running slowly, that he managed to keep clicking the button and kicking off around 500 additional instances of the same report, bringing the application to it's knees (sometimes we all learn the hard way).
Speaking of that process. one of the things that can be really helpful in dealing with issues like this is that you move to a process that allows you to queue workloads like this, while doing this is not the most appealing it is a manner of degrading gracefully. Because what this allows you to do is make sure that you are completing one of the items, caching the result and checking to see if their is output that follows within a window where you could return this data. Utilizing this approach the user in the above case would get 500 instances of the report, but they would also get them incredibly fast after the first one had completed.
Lastly, one of the major mistakes that I see Jr to mid level developers make is intermixing their application code with the content that they are attempting to serve. This is common for things like CMS systems, or where there is user uploads that need to happen. From a software security standpoint you should never mix user data and executable code. This is a mistake that is waiting to happen, there are two scenarios that could happen here, execution of code that isn't yours (we will talk about this in the near future) or a Denial of Service attack where the server will be asked for 10,000 copies of the same super large PDF File. Talking about the work ratio here we are talking about the size of the URL request, to the response of the PDF file (that's a huge ratio). In these cases it's better to move the files to a CDN or to blob storage.
There is one last type of DDoS that I am going to talk about here because I feel like I would be missing something if I didn't. Be aware of utilization of 3rd party pay services, as they relate to your application. There have been more than a few times that we have seen DoS attacks spiral out of control and into the real world when they are tied to things like account registration, email, or 3rd party sms. Any of these actions could effectively lead to a REALLY bad day, so it's best that you make sure you are monitoring them and dealing with the rate limiting and usage alerts. No one wants to pay for $10k worth of spam registation emails.
The interesting part of all of this is that this represents the minimum that can be done, I feel like this post is already WAY tool long and I have only scratched the surface of things that need to be considered.
If you're interested in making cyber security part of your development practice and looking at it from a holistic standpoint, please do not hesitate to reach out to me. I would love to help you and your team produce safer, more secure software. As always thank you for reading our post and making your software more secure for a better world.
Wanna talk?: https://calendly.com/withstand-security/30min
The original report can be found at this link: https://www.cisa.gov/sites/default/files/2024-03/Understanding%20and%20Responding%20to%20Distributed%20Denial-of-Service%20Attacks_508c.pdf