OWASP Code Review Guide v2-41-50
OWASP Code Review Guide v2-41-50
YES
STANDARDS POLICIES
PERFORM
REVIEW
COMMUNICATE
GUIDELINES
RESULTS TO TEAM
CODE REVIEW
DATABASE
RECORD
FINDINGS
DEVELOP PREVIOUS
METRICS FINDINGS
PERSIST METRICS
Methodology 41
The McCabe cyclomatic complexity metric is designed to indicate a program’s testability, understandability
and maintainability. This is accomplished by measuring the control flow structure, in order to predict the diffi-
culty of understanding, testing, maintaining, etc. Once the control flow structure is understood one can gain a
realization of the extent to which the program is likely to contain defects. The cyclomatic complexity metric is
intended to be independent of language and language format that measures the number of linearly indepen-
dent paths through a program module. It is also the minimum number of paths that should be tested.
By knowing the cyclomatic complexity of the product, one can focus on the module with the highest com-
plexity. This will most likely be one of the paths data will take, thus able to guide one to a potentially high risk
location for vulnerabilities. The higher the complexity the greater potential for more bugs. The more bugs the
higher the probability for more security flaws.
Does cyclomatic complexity reveal security risk? One will not know until after a review of the security posture
of the module. The cyclomatic complexity metric provides a risk-based approach on where to begin to review
and analyze the code. Securing an application is a complex task and in many ways complexity an enemy of
security as software complexity can make software bugs hard to detect. Complexity of software increases over
time as the product is updated or maintained.
As the decision count increases, so do the complexity and the number of paths. Complex code leads to less
stability and maintainability.
The more complex the code, the higher risk of defects. A company can establish thresholds for cyclomatic
complexity for a module:
0-10: Stable code, acceptable complexity
11-15: Medium Risk, more complex
16-20: High Risk code, too many decisions for a unit of code.
Modules with a very high cyclomatic complexity are extremely complex and could be refactored into smaller
methods.
As the complexity of software increase so does the probability to introduce new errors.
42 Methodology
Inspection Rate:
This metric can be used to get a rough idea of the required duration to perform a code review. The inspection
rate is the rate of coverage a code reviewer can cover per unit of time. For example, a rate of 250 lines per hour
could be a baseline. This rate should not be used as part of a measure of review quality, but simply to deter-
mine duration of the task.
We also need to look for common issues relating to a specific language; issues that may not be security related
but which may affect the stability/availability of the application in the case of extraordinary circumstances.
Other issues when performing a code review are areas such a simple copyright notice in order to protect one’s
intellectual property. Generally these issues should be part of a companies Coding Guidelines (or Standard),
and should be enforceable during a code review. For example a reviewer can reject a code review because
the code violates something in the Coding Guidelines, regardless of whether or not the code would work in
its current state.
Crawling code can be done manually or in an automated fashion using automated tools. However working
manually is probably not effective, as (as can be seen below) there are plenty of indicators, which can apply to
a language. Tools as simple as grep or wingrep can be used. Other tools are available which would search for
keywords relating to a specific programming language. If a team is using a particular review tool that allows
it to specify strings to be highlighted in a review (e.g. Python based review tools using pygments syntax high-
lighter, or an in-house tool for which the team can change the source code) then they could add the relevant
string indicators from the lists below and have them highlighted to reviewers automatically.
The basis of the code review is to locate and analyze areas of code, which may have application security impli-
cations. Assuming the code reviewer has a thorough understanding of the code, what it is intended to do, and
the context in which it is to be used, firstly one needs to sweep the code base for areas of interest.
Appendix C gives practical examples of how to carry out code crawling in the following programming lan-
guages:
• .Net
• Java
• ASP
• C++/Apache
43
A1
44 A1 - Injection
A1 INJECTION
7.1 Overview
What is Injection?
Injection attacks allow a malicious user to add or inject content and commands into an application in order to
modify its behaviour. These types of attacks are common, widespread, an easy for a hacker to test if a web site
is vulnerable and easy and quick for the attacker to take advantage of. Today they are very common in legacy
applications that haven’t been updated.
SQL commands are not protected from the untrusted input. SQL parser is not able to distinguish between
code and data.
String custQuery = SELECT custName, address1 FROM cust_table WHERE custID= ‘“ + request.GetParameter(“id”) + ““
Code Data
Using string concatenation to generate a SQL statement is very common in legacy applications where de-
velopers were not considering security. The issue is this coding technique does not tell the parser which part
of the statement is code and which part is data. In situations where user input is concatenated into the SQL
statement, an attacker can modify the SQL statement by adding SQL code to the input data.
1. Untrusted input is acceptable by the application. There are several ways to mitigate injection vulnerability,
whitelisting, regex, etc. The five best ways are. All five should be used together for a defense in depth approach.
2. Using static analysis tools. Most static analysis for languages like .Net, Java, python are accurate. However
static analysis can become an issue when injection comes from JavaScript and CSS.
3. Parameterize SQL queries. Use SQL methods provided by the programming language or framework that
parameterize the statements, so that the SQL parser can distinguish between code and data.
A1 - Injection 45
4. Use Stored Procedures. Stored procedures will generally help the SQL parser differentiate code and data.
However Stored Procedures can be used to build dynamic SQL statements allowing the code and data to be-
come blended together causing the it to become vulnerable to injection.
Effectively the attacker uses SQL queries to determine what error responses are returned for valid SQL, and
which responses are returned for invalid SQL. Then the attacker can probe; for example check if a table called
“user_password_table” exists. Once they have that information, they could use an attack like the one described
above to maliciously delete the table, or attempt to return information from the table (does the username
“john” exist?). Blind SQL injections can also use timings instead of error messages, e.g. if invalid SQL takes 2
seconds to respond, but valid SQL returns in 0.5 seconds, the attacker can use this information.
Sample 7.1
1 String query = “SELECT id, firstname, lastname FROM authors WHERE forename = ? and surname = ?”;
2 PreparedStatement pstmt = connection.prepareStatement( query );
3 pstmt.setString( 1, firstname );
4 pstmt.setString( 2, lastname );
In this example the string ‘query’ is constructed in a way that it does not rely on any client input, and the
‘PreparedStatement’ is constructed from that string. When the client input is to be entered into the SQl, the
‘setString’ function is used and the first question mark “?” is replaced by the string value of ‘firstname’, the sec-
ond question mark is replaced by the value of ‘lastname’. When the ‘setString’ function is called, this function
automatically checks that no SQL syntax is contained within the string value. Most prepared statement APIs
allow you to specify the type that should be entered, e.g. ‘setInt’, or ‘setBinary’, etc.
You should never use string concatenation in combination with the client input value. Take an example where
the existence (not the value) of a client input variable “surname” is used to construct the SQL query of the
prepared statement;
46 A1 - Injection
Sample 7.2
1 String query = “Select id, firstname, lastname FROM authors WHERE forename = ?”;
2 if (lastname!= NULL && lastname.length != 0) {
3 query += “ and surname = ?”;
4 }
5 query += “;”;
6
7 PreparedStatement pstmt = connection.prepareStatement( query );
8 pstmt.setString( 1, firstname);
9
10 if (lastname!= NULL && lastname.length != 0) { pstmt.setString( 2, lastname ); }
Here the value of ‘lastname’ is not being used, but the existance of it is being evaluated. However there is still a risk
when the SQL statement is larger and has more complex business logic involved in creating it. Take the following
example where the function will search based on firstname or lastname:
Sample 7.3
This logic will be fine when either firstname, or lastname is given, however if neither were given then the SQL state-
ment would not have any WHERE clause, and the entire table would be returned. This is not an SQL injection (the
attacker has done nothing to cause this situation, except not passing two values) however the end result is the same,
information has been leaked from the database, despite the fact that a parameterized query was used.
For this reason, the advice is to avoid using string concatenation to create SQL query strings, even when using param-
eterized queries, especially if the concatenation involves building any items in the where clause.
a maintenance point of view this could invite future programmers to misunderstand the difference between safe
concatenation and the unsafe version (using input string values directly).
One option for flexible parameterized statements is to use ‘if’ statements to select the correct query based on the
input values provided, for example:
Sample 7.4
1 String query;
2 PreparedStatement pstmt;
3
4 if ( (firstname!= NULL && firstname.length != 0) &&
5 lastname!= NULL && lastname.length != 0) ) {
6 query = “Select id, firstname, lastname FROM authors WHERE forename = ? and surname = ?”
7 pstmt = connection.prepareStatement( query );
8 pstmt.setString( 1, firstname );
9 pstmt.setString( 2, lastname );
10 }
11 else if (firstname != NULL && firstname.length != 0) {
12 query = “Select id, firstname, lastname FROM authors WHERE forename = ?”;
13 pstmt = connection.prepareStatement( query );
14 pstmt.setString( 1, firstname );
15 }
16 else if (lastname != NULL && lastname.length != 0){
17 query = “Select id, firstname, lastname FROM authors WHERE surname= ?”;
18 pstmt = connection.prepareStatement( query );
19 pstmt.setString( 1, lastname);
20 }
21 else{
22 throw NameNotSpecifiedException(); }
Sample 7.5
1 <?php
1 $pass=$_GET[“pass”];
2 $con = mysql_connect(‘localhost’, ‘owasp’, ‘abc123’);
3 mysql_select_db(“owasp_php”, $con);
4 $sql=”SELECT card FROM users WHERE password = ‘”.$pass.”’”;
5 $result = mysql_query($sql);
6 ?>
48 A1 - Injection
The most common ways to prevent SQL Injection in PHP are using functions such as addslashes() and mysql_
real_escape_string() but those function can always cause SQL Injections in some cases.
Addslashes :
You will avoid Sql injection using addslashes() only in the case when you wrap the query string with quotes.
The following example would still be vulnerable
Sample 7.6
mysql_real_escape_string():
mysql_real_escape_string() is a little bit more powerful than addslashes() as it calls MySQL’s library function
mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ‘, “ and \x1a.
As with addslashes(), mysql_real_escape_string() will only work if the query string is wrapped in quotes. A
string such as the following would still be vulnerable to an SQL injection:
SQL injections occur when input to a web application is not controlled or sanitized before executing to the
back-end database.
The attacker tries to exploit this vulnerability by passing SQL commands in her/his input and therefore will
create a undesired response from the database such as providing information that bypasses the authorization
and authentication programmed in the web application. An example of vulnerable java code is shown in
sample 7.7
Sample 7.7
“ OR 1=1.
A1 - Injection 49
Example.
A developer creates a webpage with 3 fields and submit button, to search for employees on fields ‘name’,
‘lastname’ and ‘id’
The developer implements a string concatenated SQL statement or stored procedure in the code such as in
sample 7.8.
Sample 7.8
Sample 7.9
A hacker can then insert the following employee ID via the web interface “123’;DROP TABLE pubs --” and exe-
cute the following code:
SELECT name, lastname FROM authors WHERE ei_id = ‘123’; DROP TABLE pubs --’
The semicolon “;” provides SQL with a signal that it has reached the end of the sql statement, however, the
hacker uses this to continue the statement with the malicious SQL code
; DROP TABLE pubs;
Parameter collections
Parameter collections such as SqlParameterCollection provide type checking and length validation. If you use
a parameters collection, input is treated as a literal value, and SQL Server does not treat it as executable code,
and therefore the payload can not be injected.
Using a parameters collection lets you enforce type and length checks. Values outside of the range trigger an
exception. Make sure you handle the exception correctly. Example of the SqlParameterCollection:
Hibernate Query Language (HQL)