Static analysis security testing looks for security flaws in the code itself, before a single dynamic test runs. As threats to business applications grow, people expect software testing to catch most potential vulnerabilities. Dynamic testing is only one way to do that. Static code analysis is a cheap way to find a share of the potential security flaws early. It won’t find all of them, but it finds a good number. (The examples in this article are in Java.)
What does it catch? Missing input and return value checks, hard-coded constants that attackers can change in the bytecode, embedded SQL statements open to SQL injection, and call patterns that let an intruder slip extra data into a method. What it doesn’t cover are properties such as execution time, data volume or throughput, which only a dynamic test can measure. Static analysis complements dynamic testing and runs before it. It does not replace it.
What Static Analysis Is For
Static analysis is one of many test approaches for finding defects in a software system. It examines requirements documents, design models and code components of a product without actually running the product. Design reviews and code inspections work on the same principle, except that people do them. Static analysis is done by a tool that checks the artifacts against a set of rules. The goal is to expose defects in how the software is built before dynamic testing starts.
Which Security Flaws Static Code Analysis Finds
There are many tools for analyzing source code statically. Tools that look specifically for security flaws are usually called SAST tools today (static application security testing). When you choose one, two questions matter. First, which types of defects does the tool report? Second, how many false positives does it produce, meaning reports of defects that aren’t real defects? A tool that raises a lot of them only distracts from the real problems.
For the real defects, you need a classification scheme. From a code security perspective, there are five main classes: missing code, unauthorized code, tamperable code, error-prone code and non-compliant code.
Missing Code
A typical example of missing code is a public method that doesn’t check its input parameters. The first check is whether the input has the right type and lies within the valid range. If it doesn’t, the method should throw an exception.
The same goes for return values. Values returned by methods in remote classes should be checked before they are used.
Another example is a class definition without the “final” keyword. A class that isn’t declared final lets an intruder copy and manipulate the code. To prevent that, all subordinate classes should be declared final.
At least a third of the code should consist of security checks. If it doesn’t, that is a sign the code runs unprotected.
Unauthorized Code
Hard-coded constants are an example of unauthorized code. Values like these are hard to maintain and dangerous. Attackers who get access to the bytecode can change them. It is better to move all constants into external resource files, and that applies to text in particular.
Casts and nesting are further problem areas.
Tamperable Code
Embedded SQL statements are an example of tamperable code. They are open to SQL injection attacks, which let outsiders corrupt the contents of the database. SQL statements have no place in application code. If you use them at all, keep them inside a protected data access layer.
Cloning and deserialization are other areas where tamperable code shows up.
Error-Prone Code
Error-prone code can easily fail under certain conditions. Typical cases are comparing an object with a string or a recursion without an exit condition. There is a security angle as well: if a function call passes fewer parameters than the called method expects, an intruder can append their own data to the parameter list and use it to inject malicious code.
Non-Compliant Code
Non-compliant code deviates from a given standard. That isn’t a direct security threat, but in some cases it can turn into one. Take standard services that should be avoided because they might be compromised. Anyone who uses them anyway risks taking on infected objects.
How Effective Automated Static Analysis Is Compared to Dynamic Testing
A study at North Carolina State University in the 2000s set out to show how effective automated static analysis can be. The researchers ran the tool FlexeLint over three million lines of C++ code from Nortel Networks Corporation. It found more than 136,000 defects, one for every 22 lines of code, even though the code had been in production for over three years.
The number of defects from static analysis correlated with the number of failure reports from pre-release testing with an accuracy of 83 percent. That suggests static analysis could have caught those faults just as well. And it would have been far cheaper: dynamic testing took 12 person-months, static analysis would have taken 2.
Static Analysis Security Testing for Java Code
As the study shows, most defects can be found in the code before anyone starts testing. That holds for security flaws too. It does not hold for other non-functional defects such as execution time, data volume or throughput.
Tools that support these checks fit into the development process without much effort, for example in continuous integration. With agile development and continuous delivery, defects have to be found and fixed quickly and cheaply, and static code analysis is the best way to do that.
The following list shows the kind of rules these tools implement. The rules come from the JavAudit tool and reflect the Java security rules of their time. Some of them, such as not signing the code, no longer match current practice:
- Avoid allocating uninitialized objects
- Restrict access to classes and methods
- Declare all classes and methods final
- Do not allow new classes to be added to finished packages
- Do not allow nested classes
- Do not sign the code
- Put all code into a single JAR
- Make all classes uncloneable
- Make all classes unserializable
- Make all classes undeserializable
- Do not allow embedded SQL statements
- Do not allow data type casts
- Check all input arguments
- Check all return values
- Wrap all I/O operations in try-catch blocks
- Wrap all calls to remote methods in try-catch blocks
- Compare the type and number of parameters at every interface
- Avoid hard-coded data values
Conclusion
Experience with static code analysis shows that many security flaws in an IT system are already visible in the code. Static analysis is a cheap, fast and reliable way to find them. All developers need are the right tools and a firm commitment to writing clean code.
Frequently Asked Questions
Can static analysis replace dynamic testing in security audits?
No. Static analysis does not uncover all potential security flaws, but it does reveal some of them, and it does so before any testing takes place. It is not suitable for other non-functional properties: time consumption, data volume, and throughput capacity can only be tested dynamically. It is therefore a cost-effective supplement that precedes dynamic testing, not a replacement.
What matters most when selecting a tool for static code analysis?
Two questions are decisive: What types of defects does the tool report, and how often does it generate false positives? False positives are reports of defects that aren’t actually defects. If a tool generates many of these, it distracts from the real defects. By 2015, a wide selection of tools was already available, making it necessary to compare them based on these two criteria.
What types of security-related code defects can be distinguished?
From a code security perspective, there are five main categories: missing code, unauthorized code, tamperable code, error-prone code, and non-compliant code. Such a classification scheme helps organize the reports generated by an analysis tool. Non-compliant code (code that deviates from a specified standard) is not a direct threat in itself, but it can become one.
How much validation logic should clean application code contain?
At least one-third of the code should consist of security-checking statements. If this proportion falls significantly below that, it is a sign that execution is not secure. This includes checking the input parameters of public methods for type and valid value ranges, including triggering an exception, as well as verifying return values from remote classes before they are used.
Why are hard-coded constants in the source code a security issue?
Hard-coded values are difficult to maintain and vulnerable to attack: Anyone who gains access to the bytecode can modify them. Therefore, all constants, especially text, should be moved to external resources. The article classifies hard-coded constants as unauthorized code, along with issues related to type casting and nesting.
Why are SQL statements in application code considered risky?
Embedded SQL statements are vulnerable to SQL injection attacks, which allow attackers to corrupt the database contents. They therefore have no place in application code. If they are used at all, it should only be within a protected access layer. Vulnerabilities related to cloning and deserialization also fall into the same category of tamperable code.
How can an incorrect number of parameters in a method call lead to a security vulnerability?
If the number of parameters in the function call is fewer than the number of parameters in the called method, an attacker can append their own data to the parameter list and thus inject viruses into the code. Such constructs are examples of error-prone code, as are comparing an object to a string or using recursion without a termination condition.
Is static analysis still worthwhile for code that has been running in production for years?
Yes. In a study conducted by North Carolina State University, three million lines of C++ code from Nortel Networks Corporation were analyzed using the FlexeLint tool. It uncovered over 136,000 defects (one for every 22 lines of code), even though the code had already been in production for more than three years. The number of defects correlated 83 percent with the error messages from the pre-release test, but took 2 person-months instead of 12.


