The common approach to securing applications is to try to identify and remove all of the application's security vulnerabilities at the end of the development process. However, this bug-finding approach is not only resource-intensive, it's largely ineffective. To have any chance of exposing all of the security vulnerabilities that may be nested throughout the application, the team would have to identify every single path through the application then rigorously test each and every one. And any error found would be difficult to fix, considering that the effort, cost, and time required to fix each one increases exponentially as the development process progresses. Most importantly, the bug-finding approach to security fails to address the root cause of the problem - the fact that security, like quality, must be built into the application.
Building security into an application involves designing and implementing the application according to a policy for reducing the risk of security attacks then verifying that the policy is implemented and operating correctly.
In other words, security requirements should be defined, implemented, and verified just like other requirements.
For example, establishing a policy to apply user input validation immediately after the input values are received guarantees that all inputs are cleaned before they're passed down through the infinite paths of the code to wreak havoc. If this requirement is defined in the security policy and then verified as implemented in the code, the team doesn't need to spend countless resources trying to find and test every possibility for user input.
One of the best strategies for building security into the application is to define how code needs to be written to protect it from attacks then use static analysis to verify that the policy is implemented in the code. This article provides an overview of how this can be done.
Develop a Security Policy
Security policies are espoused by security experts such as OWASP and mandated for compliance by many regulations such as Sarbanes-Oxley that require organizations to demonstrate they have done "due diligence" in safeguarding application security and information privacy. Yet, although the term is mentioned frequently, it's not often defined. A security policy is a specification document that defines how code needs to be written to protect it from attacks. Security policies typically include custom security requirements, privacy requirements, security coding best practices, security application design rules, and security testing benchmarks.
What do you do if your team doesn't have a well-defined security policy? If the organization has designated security experts, they should be writing these requirements. If not, security consultants can be brought in to help develop appropriate requirements for the specific application under development. Obviously, this would require considerable interaction with the internal team members most familiar with the application.
The security policy should describe what kinds of resources require privileged access, what kind of actions should be logged, what kind of inputs should be validated, and other security concerns specific to the application. To be sure key requirements aren't overlooked, I recommend listing all the important assets that a given application interacts with then prioritizing them based on the importance of protecting each asset.
Verify that the Security Policy is Implemented in the Code
Having an effective security policy defined on paper won't translate into a secure application unless the developers follow it in writing their code. Static analysis can be used to automatically verify whether most security policy requirements are actually implemented in the code and identify code that still needs work, isolating the remaining security policy requirements that might require unit testing, component testing, peer code review, or other techniques.
Using static analysis to automatically verify the code's compliance to application-specific security policy requirements (for instance, for authentication, authorization, logging, and input validation) requires expressing those requirements as custom static analysis rules then configuring the tool to check those custom rules. Often, developing such custom rules is simply a matter of tailoring the static analysis tool's available security policy rule templates to suit your own policy. For instance, custom SOA security policy rules can be created from templates such as:
Custom Java security policy rules can be created from templates such as:
If you're developing in Java, you'd want to do static analysis to check industry-standard Java security rules such as:
For SOA, you'd want to check industry-standard rules such as:
String name = req.getParameter("name");
To comply with this rule, the code would have to be modified as follows:
try {
String name = ISOValidator.validate(req.getParameter("name"));
} catch (ISOValidationException e) {
ISOStandardLogger.log(e);
}
For SOA applications, applying industry-standard static analysis rules can expose common security vulnerabilities that manifest themselves in XML. For example, static analysis could be used to parse DTDs and check for recursive entity declarations that, when parsed, can quickly explode exponentially to a large number of XML elements. If such "XML bombs" are left undetected, they can consume the XML parser and cause a denial of service attack. For instance, static analysis could be used to identify the following DTD that, when processed, explodes to a series of 2100 "Bang!" elements and will cause a denial of service:
<?xml version="1.0" ?>
<!DOCTYPE foobar [
<!ENTITY x0 "Bang!">
<!ENTITY x1 "&x0;&x0;">
<!ENTITY x2 "&x1;&x1;">
...
<!ENTITY x99 "&x98;&x98;">
<!ENTITY x100 "&x99;&x99;">
]>
Take Additional Measures to Safeguard Security
After you're confident that the security policy is implemented in the
code, a smoke test can help you verify that the security mechanisms
operate correctly. This is done by penetration testing, which involves
manually or automatically trying to mimic an attacker's actions and
checking if any tested scenarios result in security breaches. When
penetration testing is done this way, it can provide reasonable
assurance of the application's security after it has verified just
several paths through each security-related function.
If the security policy was enforced using static analysis, the penetration testing should reveal two things:
1. Problems
related to security policy requirements that can't be enforced through
static analysis (for instance, requirements involving Perl):
If problems are identified, either the security policy must be refined
or the code isn't functioning correctly and has to be corrected. In the
latter case, locating the source of the problem will be simplified if
the code's security operations are centralized (as required by the
recommended security policy).
2. Missing requirements:
For example, consider a Web application that requires users to
register. The registration form takes in a variety of fields; one of
them is e-mail. The e-mail field is known to take any input. In this
case, the application is missing a requirement to verify that a valid
e-mail is input into the field. Moreover, to ensure that code remains
secure as the application evolves, all security-related tests
(including penetration tests, static analysis tests, and other security
requirements tests) should be added to a regression test suite, and
this test suite should be run on a regularly scheduled basis
(preferably nightly). Tests are then run consistently, without
disrupting the existing development process. If no problems are
identified, no team intervention is required. If tests find that code
modifications reintroduce previously corrected security vulnerabilities
or introduce new ones, the team is alerted immediately. This automated
testing ensures that applications remain secure over time and also
provides documented proof that the application security policy has been
enforced.
Implement a Process for Updating the Policy
Because a security policy is a living, breathing document, it needs to be updated regularly. Figure 1 depicts a recommended process for updating this policy.
As new vulnerabilities are found, you isolate them and find the root cause of the issue. Once the root cause is identified, a policy is implemented around it. A fix for the vulnerability is determined, and then - if feasible - your static analysis tool is configured to check whether code is written according to the new rule. This checking is then added to your regularly scheduled regression tests so that, moving forward, you know that the vulnerability remains fixed. The policy is then applied across the application and organization ensuring that every instance of that vulnerability is fixed.
If this process seems familiar, it's because its roots trace back to W. Edwards Deming's TQM (Total Quality Management) and Parasoft's AEP (Automated Error Prevention). This process is designed to fit seamlessly into your existing quality improvement process, thereby reducing barriers to adoption.
Security = Quality
It used to be that security was
perceived as a "nice to have" feature, something to focus on - as time
permitted - after implementing and verifying the key product
functionality. However, the industry is starting to realize that
security is synonymous with quality. Even if an application's core
functionality is rigorously verified, security vulnerabilities in the
code can open the door to denial-of-service, cross-site scripting,
buffer overflows, injection flaws, and other serious security attacks.
Such attacks could prevent the application from functioning as expected
- and may stop it from functioning altogether until the source of the
attack is identified and resolved. Likewise, if you spend countless
resources defining, applying, and validating a security policy but fail
to address general reliability and functionality flaws in the
application then you can't rest assured that the security safeguards
built into your application will operate correctly.
In today's world, an application must be secure to be considered "high-quality" and security can't truly be achieved unless it's implemented as a core part of a comprehensive quality strategy for preventing and detecting errors from the earliest phase of development. Static analysis is a critical tool in implementing such a comprehensive strategy team-wide, with minimal disruption to the team's day-to-day workflow.