270 Core Software Security
also be efficient, elegant, and maintainable, these are very expensive. And
there are very few such “code gurus” from which to draw. Making use of
these experts may be very valuable for critical code, but it is infeasible for
most projects in most organizations. What is feasible is to complement
several different approaches, some targeted assurance, some broad, both
manual and automated. A methodology that maximizes the complemen-
tary nature of approaches has proven to find the broadest level of defects
while also being scalable.
To maximize manual code review, for the large amount of grunt,
well-understood, typical code that must surround the critical pieces
and the proprietary business logic modules, it’s probably sufficient for
most organizations to use peer review. Manual peer code review is when
one or more of the coders peers reviews code before commit for build.
Members of the same development team will likely be familiar with what
was intended by the code, the environment in which the code will run,
and the general architecture of the project. It should therefore not be too
difficult for them to understand and comment constructively. For code
that is not too complex, such as routines that call a broadly understood
API, a single reviewer may be sufficient. For more complex code, multiple
reviewers may be employed. In fact, one of the soft benefits from a strong
peer review program is the trust that members of the team develop in
each other. Further, reviewers will become backups to the coder so that
no single person becomes indispensable. On one team, the coders came
to trust each other so much that they would often ask for three or four
reviewers on their code; the resulting code was much more correct and
efficient than it had been previous to this team code review. If the team
is sufficiently skilled by themselves, peer review may be adequate for even
critical or highly sensitive algorithms.
Typically, however, especially at fast-moving organizations where
there may be movement between teams or even periods of turnover, peer
review will be insufficient for critical code. The worst situation is where
a junior programmer shows her or his code to another junior program-
mer who looks at it and replies, “Your code looks just like mine. Pass.
Inexperienced coders are not going to find many sophisticated errors. Still,
one of the more interesting code review processes weve seen involved a
junior reviewer explaining code to a very senior developer.* While this
* Instituted by Joe Hildebrand, CTO of Jabber, Inc.
Applying the SDL Framework to the Real World 271
may be a more time-consuming approach, the training and development
benefits are obvious. A code review becomes an opportunity to mentor
and instruct.
We recommend that critical algorithms, complex functions, security
modules, and cryptography implementations be manually reviewed by
someone who has the skill to assess correctness and who understands
the types of vulnerabilities that can be introduced. This will typically be
someone very senior on the team or who can be made available to the
team. It should be someone who is familiar with the intended functional-
ity; usually, its someone who has implemented the same or similar func-
tions successfully in the past.
9.1.3 Static Analysis
Static code analysis is the process of running an automated tool that reads
either the source or the compiled object code after it has been written.
The tool has preprogrammed patterns and errors for which it searches
in the body of the code. Since, as has already been noted, computer lan-
guages are expressive; this is what’s known in computer world science as
a “nontrivial” problem. That is, static analysis must understand not only
the legality and semantics of a language (duplicating what the compiler
does), but must also understand typical constructs and those constructs
mistakes and misuses. Further, the static analysis must create a graph of
all the possible interactions, “calls” within the code, in order to arrive at
vulnerable interactions. One limited and simplified way to describe mod-
ern static analysis is that the compiler finds what is illegal or disallowed
in the language. A static analyzer builds on legality by finding that which
is “ill-advised.” Again, this is a gross oversimplification of the capabilities
bundled within an industrial-strength static analyzer.
With respect to the SDL, how a static analysis takes place is not particu-
larly important. More important is the sorts of errors that static analysis
finds, and the sorts of errors that will be overlooked. For languages where
direct memory manipulation, allocation, and de-allocation are required,
static analysis has proven excellent at identifying code where memory is
susceptible to misuse. For instance, common stack-based overflows in
C/C++ occur when the size of data to be copied into a stack-based buffer
is not checked before the copy or the length of the copy is not limited to
272 Core Software Security
the size of the buffer. This is considered a classic security vulnerability.
If that buffer can be accessed from a point outside the program (that is,
through an input path), the overflow can be used to execute code of the
attacker’s choosing.
Most static analysis tools will readily identify failure to check size of
the copy as a memory-handling error. Some tools may even be able to
establish seriousness by identifying an input path that leads to the copy.
This is particularly true for static analysis which builds an execution graph
of the software before error analysis.* Or, conversely, the analyzer may find
that there is no such input path, thus downgrading the seriousness of the
issue. In the latter case, dynamic analysis will never find the issue, as there
is no input leading to the buffer copy. In the former case, dynamic analy-
sis may or may not find the issue, depending on the inputs attempted.
Because static analyzers can view all the code, not just the code that can
be reached through inputs, we place static analysis at the point where the
code is still close to the developer. Several of the industrial-strength static
analyzers build a code graph of every path through the code. Building
such a graph gives the analyzer a holistic view not only of paths through
the code, but of relations between modules, use of APIs, data exposure
and hiding, and other subtle programming patterns. With the true graph,
the analyzer can assign seriousness to issues with far more information
about relations between pieces of the code when it finds an error. And, of
course, since the static analyzer has the source code, it can point precisely
in the code where a potential error lies. This saves significant amounts of
time for developers fixing defects.
The downside of the holistic but not executing view is that errors
that may have less potential to get exercised may be reported alongside
and equivalent to those that absolutely are exposed. These unexposed
errors, in fact, may not be particularly significant even when they are
exposed, due to runtime considerations. That is, not all potential buffer
overflows have equal impact. The classic example is a buffer overflow that
requires very high privileges in order to exploit. Attackers who have esca-
lated privilege to the required level have no need to exercise an additional
* The advantage of the call graph is that every possible path through the code can
be examined. The disadvantage of a call graph is that many of the possible paths
enumerated in the graph may never be executed. There are limited approaches for
static analysis to determine which paths are critical and actually exposed to an attack
surface. The analyzer takes a “best guess” approach.
Applying the SDL Framework to the Real World 273
attack that executes the attackers arbitrary code. On most modern oper-
ating systems, at that privilege level, an attacker can execute whatever
he or she wants without a further exploit. In other words, such a buffer
overflow has no potential attack value. Such an overflow is entirely theo-
retical. Still, because the static analyzer is not executing the program and
has no notion of user privileges, the analyzer cannot distinguish between
a high-privilege arbitrary code execution and a low-privilege one. It will
take developer analysis to qualify such a reported defect.
The Embarrassment of Riches problem means that a modern
commercial static analysis tool generally finds more bugs than the
user has resources, or willingness, to fix.
9
One practical solution to the “embarrassment of riches” problem is
to start with a well-understood, high-confidence analysis. “High confi-
dence” means: “Report only those defects for which there is very high
confidence that there is in fact an exploitable defect that must be fixed.
This means turning down the “aggressiveness” or similar configuration to
a low setting. Configure only those checks that the manufacturer of the
static analyzer believes will deliver 90 percent or better confidence in the
results—that is, 10 percent or less false positives. Obviously, this means
that some defects will flow through. We offer the adage that any reduc-
tion in attack surface is a significant win for security. It is extremely hard
to achieve 100 percent; even 80 percent may be difficult in the first few
releases of a program. A reduction in attack surface or vulnerability of
even 20 percent in the early stages of a program to build secure software
(an SDL program) is quite significant.
Political resistance to static analysis bugs is sometimes warranted,
sometimes mere laziness, but sometimes deeper and cultural:
Avoiding the kinds of bugs that static analysis finds is largely a matter
of discipline, which is sometimes unpopular among programmers.
Fixing these bugs, and verifying that your organization has done so,
will require adaptability and judgment. Attempts to design simple
rules and metrics for this are, in my opinion, at best premature, and
perhaps impossible.
10
Starting with lower targets that will deliver high-confidence results will
be accepted much faster, as engineering teams quickly gain confidence in
the tools’ results. We suggest a soft target of engineers trusting the static
274 Core Software Security
analyzer similarly to the way they trust their compilation tools. Starting
small and focusing on high confidence is likely to gain that sort of trust.
And once that trust is gained, teams can begin experimenting with more
checks, broader defect analysis, and more aggressive scans. Still, in our
experience, engineering team confidence and trust is critical to the success
of the static analysis program.
In other words, it is typically a mistake to simply turn on the default or
everything but the kitchen sink” analysis when first getting a start. Teams
will be bombarded with unmanageable defect totals (sometimes in the
tens of thousands from a single analysis). Defect tracking systems will be
overwhelmed. Such numbers are likely to generate “political resistance” to
adoption. Engineers will lose confidence that the tool can deliver usable
and reliable results.
Instead, we advise starting small, clean, and manageable. Build on
successes. Template successful builds, configurations, and test suites that
teams can adopt easily and quickly. Have successful teams assist those
that may be struggling. Let the teams that have reduced their defect
counts evangelize to those that are just getting started or are new to
analysis. We have successfully used precisely this approach several times
in different organizations.
One key is to place static analysis in the correct development “spot.
Like the compiler, through which the code must pass successfully in order
to generate object code, the code must pass through static analysis and
likewise be free from incorrect uses and identified vulnerabilities before it
can be built (that is, linked into a library, executable, or other fully linked
and executable object). Static analysis can be thought of as a part of the
check-in and build process; static analysis is the mandatory step for quali-
fying code as buildable.
If the analysis tool supports developer use during development, let engi-
neers have this additional check for the code they produce. Programmers
will learn and trust the tool. They will also learn about security and secure
coding; security mistakes will be pointed out during code creation. Still,
we believe that while this use delivers obvious benefits, the code should still
be analyzed again when committed for build into the product or release.
Give coders every advantage. Give them every possibility for deliver ing
correct code. And verify correctness at some formal gate through which
the code must pass. If the code is clean, there is little lost from putting
it through static analysis—commercial analyzers are generally quite fast
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset