Application development use a number of different techniques to keep our applications secure. In this video, you’ll learn about input validation, fuzzing, secure cookies, code signing, and more.
As security professionals, we’re tasked with making sure that our applications and operating systems are always updated to the latest version and are bug free. But of course, that process starts at the beginning with the developers themselves, creating applications that have been hardened and resistant to these types of attacks. But of course, this process starts with the application developers who are tasked with building these applications, while at the same time making sure they’re safe from attacks.
This of course is a balancing act, because the developers have a goal to create an application that can be used, very often security is a secondary goal during the process of development. A lot of the testing that takes place during an application development process happens with the QA team. This is the Quality Assurance team that is tasked with not only making sure that the application is working to specification, but also making sure that the application is secure. But even with the best testing, applications can still have vulnerabilities that go unnoticed and eventually someone is going to find those vulnerabilities and take advantage of them.
One task for the developer is to always make sure that they understand the type of input that is going into the application. This input validation should always occur whenever information is going into the application process. This means that the application developer needs to document every place where data is going into the application. This might be in a form that’s on the screen, it might be fields that a user is filling out, or it might be information that’s added as a file is uploaded. The application developers should check all of this data that’s being input, and if anything is outside the scope of what it should be, those input variables should be resolved.
That process of checking and correcting the data that’s being input is called normalization. An example of this might be a zip code. Depending on your country, a zip code might be a series of letters or numbers, and those are usually a certain length. The application developer may define a zip code as only having a certain number of characters long, with a letter that’s in a particular column. If the data being input into the application doesn’t follow these rules, then that information can be rejected or corrected as part of the application. It’s important that the application developer understand exactly what input is being used, and how that input is being handled by the application. The attackers are going to use third party tools, such as fuzzers, to be able to constantly try to randomize input into the application to see if perhaps they can make the application perform unexpectedly or in a way that they could replicate later on.
This term, fuzzing, is referring to a task called dynamic analysis where random data is simply being put into the input of an application. You may hear this referred to as fault injecting, robustness testing, syntax testing, negative testing, and other terms as well. The attackers are looking for something out of the ordinary to occur. They’re looking to see if there might be a buffer overflow, if the application might crash, or something else that could cause this application to give them an opportunity to find a vulnerability. A standard fuzzing framework started with a 1988 class project at the University of Wisconsin, where they began taking applications and running their own utilities to find vulnerabilities. They called this the operating system utility program reliability, with Professor Barton Miller. As you can imagine, there are many different types of fuzzing tests that you can perform on an individual application. These tests may be specific to a particular application platform, or operating system, and there can be many, many variables involved when performing these fuzzing tests.
These fuzzing tests take a lot of time and a lot of processing power. They’re almost always automated and so there are constant randomized inputs that are being tried against the application. This may take quite a bit of time because the fuzzing engine is going to try many, many different iterations try to locate where vulnerability might be. Many of these fuzzing utilities are also optimized to try the most likely tests to find a vulnerability. This also speeds things up instead of going through every possible random input, you can simply try the ones that are most likely to cause a problem.
Carnegie Mellon has a Computer Emergency Response Team or CERT that has released a version of a fuzzer called the CERT Basic Fuzzing Framework, or BFF, and you can download this and try it on your own machine by going to professormesser.link/bff. Here is this fuzzing framework running on my machine, it’s going through an automated process with data that’s provided as a sample with the fuzzing program, and it’s going through every iteration to see if it can find problems. This also has a version of top running on this Linux machine, so that you can see just how much of the utilization is being used by the fuzzing program and you can keep track of where certain vulnerabilities may be found or occurring during the fuzzing process.
Another important security concern is the information that is stored on your computer from your browser. This information is referred to as cookies. Cookies are commonly used to keep track of information that is only used for a limited amount of time. This might be tracking details, or things that might personalize a login into a site, or perhaps session management information. This is not an executable, it’s not a running application, and generally this is not something that is a security concern. Of course we want to be sure that no one else has access to this information, so your cookies should, of course, be protected. Many of the cookies that are stored on our system are designated as secure cookies. Those cookies have an attribute on them that is marked as secure. This tells the browser that if this information is being sent across the network, it needs to be sent over an encrypted connection using HTTPS.
Although we’re designating that these cookies are secure, that doesn’t mean that the information inside of those cookies needs to be something that is private or sensitive. The cookies are there to make the browsing process easier for the user and are not designed to store private or personal information. If you’re not the person who has designed and created an application, it’s difficult to know exactly what work has been put into the security side of that app. To be able to make our applications more secure, we’ve created something called HTTP secure headers. This is a way to configure our web server to restrict the capabilities of a browser to be able to perform certain functions.
This means that we can tell the end user’s browser to either allow, or not allow certain tasks to occur while this application is in use. For example, we may configure a secure header that tells the client’s browser to only communicate to the web server using HTTPS. This forces the user to use an encrypted communication and restricts any communication that may not be using the HTTPS protocol. Or we might try providing cross site scripting attacks by telling the browser to only allow scripts, stylesheets, or images from loading from the web server. Or perhaps we’ve configured a secure header to prevent any data from loading into an IFrame. This is an inline frame, and something that’s commonly used when an attacker is trying to perform a cross-site scripting attack.
When we install and run a new application on our system, there’s a level of trust we have with the application that it’s not going to run any malicious software. And we’re constantly installing new applications and running scripts on our system, and we have to be aware every time we run a new application that there may be something inside of the application that we are not aware of. But of course, there’s always a concern that that application may have been modified by a third party to add malicious software into the executable. It would be useful if we could confirm that the application that we’re running is the application that was originally deployed by the application developer and that no one has made any other changes to that application in the meantime.
This is a perfect opportunity to take advantage of digital signatures with our code, by using code signing. This is very similar to the process we use to provide encryption certificates on web servers. We first need a trusted certificate authority who’s going to sign the developer’s public key. The developer will then use their private key to digitally sign any code that they happen to be deploying. That means that you can validate that this code is exactly what was deployed by the original developer, by validating it with their public key. This is something that’s commonly done when the application is installed and if the validation fails, you get messages on the screen telling you that the code signing signature is not valid.
From an administrator’s perspective, there’s probably a finite list of applications that commonly are used in your environment, they have been tested on your systems, and you can trust that they don’t contain any malicious software. But of course any application, from anywhere, could contain vulnerabilities, Trojan horses, malware, or other unwanted code. Many administrators then will configure operating system software or they’ll configure security software settings to allow or deny certain applications from executing. This means that anything that’s listed on the allow list can run with no problem on that particular system, but only applications in that allow list can operate. If someone tries to install a new application that’s not on the allow list, that application will not execute. Some organizations have a more open approach to applications and will only prevent applications that are on a specific deny list. If an application is trying to run and it’s listed on the bad list, it will be stopped before it executes on that computer.
Most operating systems have methods built into the OS that allow the administrator to set up allow lists, and deny lists. But these lists can be based on many different types of criteria. For example, there might be an application hash. You would take a hash of the application executable and the only application version that would run is the one that matches the one associated with that hash. If the application is upgraded or changed in any way, then the hash will not match and the application won’t run. An application that has been digitally signed can also be used for criteria on an allow list or a deny list. For example, you can tell from a digital signature if the application is from Microsoft, or Adobe, or Cisco. And if it matches any of those, you could choose to allow those applications to run on your computer.
Or perhaps the applications that are allowed on your computer are stored in very specific folders on your storage drive and we can configure an allow list to only run applications that happen to be stored in those folders. Your operating system might also recognize where applications are running from. If an application is running from a server that’s on an internal network, it may say that that particular network zone is allowed to run applications. But if you’re trying to run that application from a server that’s on the internet, you might want to deny that until that application has been properly vetted.
When an application developer writes some software, there might be a hidden vulnerability inside of that code that no one’s found yet. It’s very difficult for human beings to sift through all of those hundreds or thousands of lines of code to be able to identify where those vulnerabilities might be. So instead we can automate the process with Static Application Security Testing or SAST. We can use the static code analyzers to go through the source code and identify places where there may be vulnerabilities such as buffer overflows, database injections, or other well-known types of attacks.
But of course, the static code analysis doesn’t find every type of vulnerability. For example, if a type of encryption has been implemented in a way that is insecure, a static code analyzer may not find that type of issue. And as you might expect, the output from a static code analyzer does not necessarily mean that an actual vulnerability exists in the code. Every single one of these instances needs to be examined and confirmed before anyone can be sure that a particular vulnerability exists.
Here’s an example of some output from a static code analyzer. For example, in the first set of source code test.c on line 32, an error was found that says it does not check for buffer overflows, it gives some examples of what those might be and it says use fgets instead. Another set of errors was found on test.c on line 56, and it gives examples of things that may be considered as a problem and ways that you might be able to fix it. Each one of these test results has to be examined to make sure that it really does match a particular error or vulnerability, and these problems can then be resolved before the application is deployed.