24
Explicit Code & QA

Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Embed Size (px)

Citation preview

Page 1: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Explicit Code & QA

Page 2: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

So, who are you, anyway?

Bryan C. Geraghty

Security Consultant at Security PS

@archwisp

I’m a Sr. PHP developer with a systems and security engineering background - turned application security

consultant

Page 3: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Remember, layers

Simpler is easier to test

Don’t make assumptions

Compromised browser = game over

Page 4: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

These are not specifically security activities but they produce more stable code

Page 5: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Don’t rely on the server’s configuration

Your bootstrap should set the defaults for any PHP core functionality that your application is using.

These must be customizable for every environment

e.g. (dev, staging, live)

Absolute Minimum

Include paths

Error reporting

Time zones

Page 6: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Example Bootstrap

<?php

// All environment configuration for this application is done from this file

ini_set('display_errors', 0);

ini_set('error_reporting', -1);

date_default_timezone_set('UTC');

$rootPath = dirname(__FILE__);

set_include_path(sprintf(

'%s%s%s%s',

$rootPath, PATH_SEPARATOR, $rootPath, '/third-party'

));

require_once('MindFrame2/AutoLoad.php');

MindFrame2_AutoLoad::install();

Page 7: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA
Page 8: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Don’t use global constants, variables, or registries

You can’t be certain that nobody else will ever use the same global constant name

Tracking down where a value is assigned to a global variable is usually very difficult

Global variables remove control of that variable from the current scope

Global registries are really just fancy global variables and the same problems apply

Page 9: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Value Reference Rules

Values should only be referenced in one of the following ways:

A direct reference in the current scope:$limit = $this->_objectPool->getConfig()-

>getDefaultLimit();

A parameter:public function getUsers($limit) {

return $this->_objectPool-

>getMapper(‘User’)->fetchAll($limit);

}

Page 10: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Don’t use function parameter defaults

Reduces understanding from the caller perspective

Often add unnecessary complexity

Represents the most common use-case

Other use-cases are often ignored entirely

Changing the default breaks all implementations

Adding parameters breaks default implementations

"There should be one-- and preferably only one --obvious way to do it." - Tim Peters

Page 11: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Default parameter example

class User

{

public function __construct($name, $email, $status = self::STATUS_ACTIVE) {

$this->_name = $name;

$this->_email = $email;

$this->_status = $status;

}

}

function createUser($name, $email, $status) {

$user = new User($name, email);

return $this->_objectPoool->getMapper(‘User’)->create($user);

}

Page 12: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Use explicit operators whenever possible

if ($sincedate && (int)$sincedate) {

$param["UpdatedSinceDate"] =

date("Y-m-d H:i:s", strtotime(time() - ((int)$sincedate)*60*60);

} elseif ($sincedate && strtotime($sincedate)) {

$param["UpdatedSinceDate"] =

date("Y-m-d H:i:s", strtotime($sincedate));

} elseif(!isset($param["UpdatedSinceDate"])) {

exit ("Invalid param UpdatedSinceDate");

}

This was actual production code.

Do you think this code worked as the author intended when $sincedate was set to '2011-05-31'?

Page 13: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Use constants for possible values

// Bad: This code cannot execute differently in the same stack

define(‘PDO_FETCH_MODE’, 2);

$dbi->fetchAll(PDO_FETCH_MODE);

// Better: The value can change but this literal value means nothing without

// referencing the documentation

$dbi->fetchAll(2);

// Best: Implemented using a meaningful constant which represents a *possible* value

$dbi->fetchAll(PDO::FETCH_ASSOC);

Constants cannot be re-defined, so using a constant for configuration limits your code to only ever be able to run in one configuration in a given stack.

Page 14: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Use wrappers for super-globals

$_REQUEST, $_GET, $_SESSION, and $_FILES are the most commonly used PHP super-globals but there are others

It’s easy to create some serious security problems

It’s best to use a wrapper for interacting with these variables

Page 15: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Simulate strong/static types

private function _buildSelectDatabaseTableSql($database_name, $table_name,

array $select_data, array $order_by_columns, $offset, $limit)

{

MindFrame2_Core::assertArgumentIsNotBlank($database_name, 1, 'database_name');

MindFrame2_Core::assertArgumentIsNotBlank($table_name, 2, 'table_name');

MindFrame2_Core::assertArgumentIsInt($offset, 5, 'offset');

MindFrame2_Core::assertArgumentIsInt($limit, 6, 'limit');

$skel = "SELECT\n %s\nFROM\n %s.%s\n%s%s%s%s;";

$sql = sprintf($skel,

$this->_buildSelectTableSqlFieldClause($table_name),

$this->getSharedModule()->escapeDbElementName($database_name),

$this->getSharedModule()->escapeDbElementName($table_name),

$this->_buildSelectTableSqlJoinClause(

$this->getSharedModule()->getDatabase()->getName(), $table_name),

$this->_buildSelectTableSqlWhereClause($table_name, $select_data),

$this->_buildSelectTableSqlOrderByClause(

$table_name, $order_by_columns),

$this->_buildSelectTableSqlLimitClause($offset, $limit));

return $sql;

}

PHP is dynamically typed, so we cannot do true strong or static typing but we can put controls in place which provide the same protection.

Page 16: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Example Typing Assertions

public static function assertArgumentIsInt($value, $position, $name)

{

if (!is_int($value)) {

$skel = 'Expected integer value for argument #%d (%s), %s given';

$message = sprintf($skel, $position, $name, gettype($value));

throw new InvalidArgumentException($message);

}

}

public static function assertArgumentIsNotBlank($value, $position, $name)

{

if (trim($value) === '') {

$skel = 'Argument #%d (%s) cannot be blank';

$message = sprintf($skel, $position, $name);

throw new InvalidArgumentException($message);

}

}

Page 17: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Use PHPMD & CodeSniffer

#!/bin/bash

WD="`/usr/bin/dirname $0`/../";

echo Changing to working directory: $WD;

cd $WD;

if [ -z $1 ]; then

DIR='.';

else

DIR=$1;

fi

phpmd --exclude coverage $DIR text codesize,unusedcode,naming,design

phpcs --ignore=coverage --standard=`pwd`/Standards/MindFrame2 $DIR

phpcs --ignore=coverage --report=source --standard=`pwd`/Standards/MindFrame2 $DIR

This is the ./bin/codecheck script in MindFrame2

Page 18: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Example PHPMD Output

bryan@ubuntu-virtual ~/Code/MindFrame2 [130] $ ./bin/codecheck Dbms/Dbi

Changing to working directory: ./bin/../

/home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:128 Avoid unused local

variables such as '$partner'.

/home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:185 Avoid unused local

variables such as '$data'.

/home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:199 Avoid unused local

variables such as '$index'.

/home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:240 Avoid unused private

methods such as '_buildReplicaDatabaseSuffix'.

/home/bryan/Code/MindFrame2/Dbms/Dbi/Single.php:25 This class has too many

methods, consider refactoring it.

/home/bryan/Code/MindFrame2/Dbms/Dbi/Single.php:150 Avoid unused parameters such as

'$options'.

Page 19: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Example PHPCS Output

FILE: /home/bryan/Code/MindFrame2/Dbms/Dbi/Single.php

--------------------------------------------------------------------------------

FOUND 6 ERROR(S) AFFECTING 6 LINE(S)

--------------------------------------------------------------------------------

39 | ERROR | Public member variables ("_connection") are forbidden

62 | ERROR | Whitespace found at end of line

144 | ERROR | Whitespace found at end of line

160 | ERROR | Line exceeds maximum limit of 80 characters; contains 97

| | characters

195 | ERROR | Whitespace found at end of line

298 | ERROR | Line exceeds maximum limit of 80 characters; contains 81

| | characters

--------------------------------------------------------------------------------

Page 20: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Unit testing / TDD

Test critical functionality at a minimum

Add edge cases to regression tests as they are discovered

Test-driven development only works if the entire team is dedicated to it

I personally don’t trust code that isn’t regression-tested; even if it was written by me

Page 21: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Continuous Integration

Automated system for code analysis and regression testing

There are a lot of continuous integration systems for PHP

If your tests aren’t automated, very few people will know or care when they break

Page 22: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

Deployment

Manually copying files is riddled with problems

Tagging releases and re-deploying is a complicated and time-consuming process

Deployment systems automate the build and deployment process

Page 23: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

ACLs

MAC

Thread limits

Unwanted services

Page 24: Creating "Secure" PHP Applications, Part 1, Explicit Code & QA

If you’re interested in an application security career, come talk with me.