14 Applied Secure Coding Principles
14 Applied Secure Coding Principles
14
APPLIED SECURE
CODING
PRINCIPLES
PRACTICAL WEB DEFENSE COURSE
eLearnSecurity 2013 ©
All rights reserved. No part of this document may be reproduced in any form or by any electronic or
mechanical means, including information storage and retrieval systems, without written permission
from the publisher, except in the case of a reviewer, who may quote brief passages embodied in
critical articles or in a review.
eLearnSecurity s.r.l.
36,38 Via Matteucci
Pisa, ITALY 56124
P W D / P R A C T I C A L W E B D E F E N S E
1. TABLE OF CONTENTS
INTRODUCTION 1
1. REDUCE THE ATTACK SURFACE 2
1.1 PRINCIPLE DESCRIPTION 2
1.2 PRACTICAL EXAMPLES 2
1.3 FURTHER READING 4
2. STRICT INPUT VALIDATION 5
2.1 PRINCIPLE DESCRIPTION 5
2.2 PRACTICAL EXAMPLES 5
2.3 FURTHER READING 7
3. AGGRESSIVELY DIFFERENTIATE INPUT FROM CODE 8
3.1 PRINCIPLE DESCRIPTION 8
3.2 PRACTICAL EXAMPLES 9
3.3 FURTHER READING 11
4. SECURE ACCESS TO DATA AND FUNCTION 12
4.1 PRINCIPLE DESCRIPTION 12
4.2 PRACTICAL EXAMPLES 12
4.3 FURTHER READING 13
5. SECURE COMMUNICATIONS AND STORAGE 14
5.1 PRINCIPLE DESCRIPTION 14
5.2 SECURE COMMUNICATION EXAMPLES 14
5.3 SECURE STORAGE EXAMPLES 15
5.4 FURTHER READING 16
6. LEAST PRIVILEGE 17
6.1 PRINCIPLE DESCRIPTION 17
6.2 WEB SERVER EXAMPLES 17
6.3 DATABASE EXAMPLES 19
6.4 FURTHER READING 20
7. DEFENSE IN DEPTH 21
7.1 PRINCIPLE DESCRIPTION 21
7.2 DOM XSS PREVENTION IN HTML5 POSTMESSAGE EXAMPLE 21
7.3 REFLECTED XSS PREVENTION EXAMPLE 23
7.4 FURTHER READING 24
8. SECURE DEFAULTS 25
8.1 PRINCIPLE DESCRIPTION 25
8.2 PRACTICAL EXAMPLES 25
8.3 FURTHER READING 26
9. MAKE SECURITY SIMPLE AND OBVIOUS 27
9.1 PRINCIPLE DESCRIPTION 27
9.2 DATA LABELING EXAMPLE 27
9.3 STOPPING EXECUTION AS SOON AS A SECURITY CHECK FAILS EXAMPLE 28
P W D / P R A C T I C A L W E B D E F E N S E
I
INTRODUCTION
This module focuses on general strategies to produce secure code.
As we saw in the tactical chapters, defending against every possible attack is non-trivial. In
addition to this, new attacks are being published every year and it is unreasonable to
expect developers to know and defend against every “cutting-edge attack that was
published last week”. Because of this, this module, although strategic, will illustrate the
general and widely accepted principles to produce secure code against known and future
(not-yet-known) attacks.
The principles in this chapter aim to produce web applications that enforce the core pillars
of information security:
• Confidentiality: Sensitive information should not be available unless permissions
grant it
• Integrity: The application should protect data from unauthorized modifications
• Availability: The application should be available to legitimate users at all times
1
1. REDUCE THE ATTACK SURFACE
Then the dispatcher source code has a map of “Page ID Page info”:
Now we have:
1. Reduced the attack surface of the application from 10,000 PHP files
directly reachable (really difficult to secure) to just 1 (making access control
much easier to implement)
2. Removed path traversals as an attack vector in the front-controller: The
user must supply a valid id, not the file path. The request is rejected if the
file id is not defined.
3. A dispatcher using known, defined paths (in $pageMap), instead of user
input.
• Always favor server-side sources of input over client-side ones: Having things
like “User role id” in the user session (i.e. $_SESSION in PHP) or a database
instead of a parameter is a smart way to prevent tampering (i.e. as opposed to
storing this information in GET, POST or cookie parameters). The attack surface
is removed by not exposing such parameters in the client-side.
• Avoid storing information on the client: Anything on the client side (even if
encrypted) can be an attack vector. For example, store tokens in cookies (i.e. so
that you lookup the token in your database instead of decrypting a cookie): Now
the attacker can only tamper the token but not the data. Cryptographic and reverse
engineering attacks become impossible because there is nothing to reverse or
decrypt. This approach would have prevented the padding oracle vulnerability
PayPal had via encrypted cookies1.
• Avoid long session durations: If users are automatically logged out after 5-10
minutes of inactivity, using some client-side JavaScript to go to the login page too,
the attack window to ride on a user session will be significantly reduced over a 1
month-valid session.
1
http://seclists.org/fulldisclosure/2013/Aug/278
Now the attacker only needs a vulnerability on a single domain to steal 1 cookie
that works on 50 domains. Instead of this, each domain should have its own
cookie and session handling.
• Avoid wildcard CORS: A CORS server configured as follows is unnecessarily
allowing any domain on the internet to read responses and send valid
authentication cookies:
If you really need CORS, then reduce the attack surface to only the domains you
trust.
• If the database server is on the same host as the web server, don’t expose
the port to the internet: If MySQL is listening on the web server on port 3306
this should only be accessible to the web application and not the whole planet. If
port 3306 must be remotely accessible, then MySQL can be configured to only
allow connections from the company/administrator IP address. Reducing the
attack surface from “allowing connections from any computer in the planet” to
“allow connections from this company IP address” is a great improvement.
• Require authentication: Valid users vs. anybody on the internet is a reduction in
attack surface.
• Reduce the number of screens, parameters, features: Less screens, parameters
and/or features will generally imply less complexity, less attack surface and more
security.
2
2. STRICT INPUT VALIDATION
The following example uses the white-list as a lookup for the value to use, in order to
avoid using user input. This example validates the user provided page ($_GET['page'])
against a known white-list of allowed pages and rejects anything else. Please note how
$_GET['page'] is meant to be the ‘id’ instead of the ‘path’:
2) Validate against a white-list of allowed characters and reject anything else: Second
best
When validating against a white-list of allowed values is not possible, the next best is to
validate against a white-list of allowed characters. This is typically best accomplished using
a regular expression but because of ReDoS2 attack vectors:
• The length should always be checked before the regular expression
• Repetition patterns such as “()+” should be avoided in the regular
expression (if possible)
3) Use blacklists ONLY as a last resort when 1-2 are really not possible: Worst
Blacklists should only be considered when white-list approaches are absolutely not
possible (which is very rare). Blacklists are a very weak form of validation that only
rejects “known bad” and therefore validation will be more likely to become vulnerable as
new attack vectors are published.
This, being said, blacklists can be a great way to complement white-lists as an additional
security control:
2 https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS
3
3. AGGRESSIVELY
DIFFERENTIATE INPUT FROM
CODE
Please note that injection attacks include XSS, SQLi, LDAP, SMTP, XML, Shell
commands, code, etc.
Another example where parameterized queries are possible is XPath: Using XQuery, it is
possible to bind variables to XPath queries3. In addition to this, in .NET it is possible to
compile XPath queries4.
2) Escape and surround user input in quotes: Second Best
Sometimes parameterized queries are not an option, particularly when rendering user input
in a page where the HTML are the “instructions” and user input needs to be displayed
within the HTML. In such cases, the most aggressive form of escaping/encoding
should be used. For example:
echo "<html>
<head><meta charset="UTF-8"></head>
<span class=\"" . htmlentities($_GET['xss'], ENT_QUOTES, "UTF-8") .
"\">test</span>
</html>";
3 http://www.slideshare.net/kverbert/xml-parta
4 http://msdn.microsoft.com/en-us/library/093b7219.aspx
Sometimes quoting the value will not be possible, like <div>$here</div>, however,
surrounding the value in quotes is very important to make escaping work and
should always be done where possible.
3) Apply a blacklist as a last resort when 1-2 are not possible: Worst
Blacklists are a very weak security control. This should only be done when
parameterization and escaping are absolutely not possible and only after white-list
validation.
A blacklist will be most effective after performing the relevant research to identify
absolutely all available control characters for the language/protocol at hand and/or using a
trusted platform/library to do this for you: Escaping a shell command has nothing to do
with escaping an STMP command or input within an XML tag. Blacklist sanitization
must be relevant to the language/protocol at hand.
For example, escaping and parameterization are typically not possible are email libraries:
4
4. SECURE ACCESS TO DATA AND
FUNCTION
Checking a user is logged in, and belonging to an administrator role before creating a new
user:
session_start();
…
if (!isset($_SESSION['logged_in']) ||
!isset($_SESSION['user_group'])
|| !$_SESSION['logged_in'] || $_SESSION['user_group'] !==
ADMIN_GROUP) {
die('You are not authorized to do this …'); Abort processing
}
else {//Permission check successful: User is logged in and an
administrator
if (validate_input(…)) {
create_new_user(…);
}
}
Checking a user is allowed to access a given record in the database (i.e. the record belongs
to the user department):
$record = retrieve_from_db($input);
if ($record->department !== $_SESSION['department']) {
die('You are not allowed to view this record…'); //Abort
processing
}
5
5. SECURE COMMUNICATIONS
AND STORAGE
<VirtualHost *:80>
ServerAlias *
RewriteEngine On
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [redirect=301]
</VirtualHost>
5 https://www.owasp.org/index.php/HTTP_Strict_Transport_Security
session.cookie_secure = On
session.cookie_httponly = On
o Setup the “httponly” and “secure” flags before “session_start()” in the code
6 http://www.ietf.org/rfc/rfc2898.txt
A typical way to do this is to store the encryption key in the application server,
while the encrypted data is in the database server.
NOTE: If your database and application are on the same server, then at least have
the encryption key in the server and not the database. But ideally, applications and
databases should be on different servers, where possible.
In addition to this, it is important to note that some industries require the use of certain
approved cryptographic algorithms, for example: FIPS 140-27.
7 http://csrc.nist.gov/publications/fips/fips140-2/fips1402annexa.pdf
6
6. LEAST PRIVILEGE
In Linux the following is a one liner that will prevent the webserver user (assuming
it is not root, which should not be) from writing files (this should be ok for most
applications). This assumes the webroot directory is /var/www:
8 http://blogs.msdn.com/b/sqlsecurity/archive/2008/01/10/xp-cmdshell.aspx
9 http://freecode.com/projects/myperl
10 http://blogs.msdn.com/b/sathishcg/archive/2006/11/24/undocumented-sql-server-2000-functions.aspx
7
7. DEFENSE IN DEPTH
<?php
//Lets suppose $_GET['number'] needs to be provided for the class
name to be “test1”, “test2”, “test3”, etc.
$number = (string) $_GET['number']; //Cast to string to avoid “[]”
PHP conversions to array
if (!preg_match('/^\d+$/', $number)) {//This is not a number
die('Invalid number!...');//This is an attack, abort processing
}
$number = (int) $number; //Now cast the number to integer, in case
there is a mistake earlier
//Finally output encode the value:
echo "<html>
<head><meta charset="UTF-8"></head>
<span class=\"test" . htmlentities($number, ENT_QUOTES, "UTF-8") .
"\">test</span>
</html>";
8
8. SECURE DEFAULTS
11 http://blog.codeclimate.com/blog/2013/03/27/rails-insecure-defaults/
12 http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/security.html
13 https://docs.djangoproject.com/en/dev/topics/security/
14 http://fuelphp.com/docs/general/security.html
15 http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html
16 http://symfony.com/doc/current/book/templating.html
17 http://doc.nette.org/en/templating
In the case of XSS, a component that renders HTML on the fly will generally be best for
output encoding correctly since it will be able to take context into account (i.e. escape
inside an attribute instead of inside a <div> element).
Most ORM18 libraries nowadays will also use bind variables in their dynamically generated
SQL queries by default.
Using a framework will also focus code review effort into “code that bypassed the
framework”, which will be “the code where human error is more likely”, hence lowering
the cost of code reviews significantly.
The ultimate goal of web application frameworks, from the security point of view is to
make more convenient to produce secure code than insecure code.
For example, CakePHP makes this (safe from XSS by default via automatic output
encoding):
echo $this->Form->input('password');
18 http://en.wikipedia.org/wiki/Object-relational_mapping
9
9. MAKE SECURITY SIMPLE AND
OBVIOUS
To this (the developer is doing exactly the same, but now does not need to follow all the
code):
$user_input = $valid_input = $sql_escaped =
array();//Initialize the data structure arrays
$user_input['user_id'] = (string) $_POST['user_id'];//The
source
$valid = true;
$input = (string) $_GET['input'];
if (strlen($input) > 20|| !preg_match('/^[a-z]+$/i', $input))
{//Something other than letters was provided!
$valid = false;
}
if ($valid) {
//processing
}
Over an approach like this (stops execution as soon as a security check fails):
Whether you prefer to throw a exception, exit() or die() is a matter of taste, what matters
the most from the security perspective is to ensure that developers do not need to follow
the source code to determine if it is secure or not. In the first example, a developer would
need to follow what happens with $valid (which may be non-trivial on a large code-base
involving many files), while in the second example, the developer sees straightaway that
when a check fails, execution is aborted and does not need to keep track of anything else
mentally.
session_start();
if (!isset($_SESSION['logged_in']) || ! $_SESSION['logged_in']) {
die('To login please <a
href="http://example.com/login.php">click here</a>');
}
if (!isset($page) || !isset($pageMap[$page])) {
die('Invalid page'); //Abort processing
}
$file_path = " ../
app_code/"; //Location of 10,000 PHP files
(outside of webroot)
require $file_path . $pageMap[$page]['path'];//Execute the file
19 http://www.artima.com/weblogs/viewpost.jsp?thread=331531