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


Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

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

  1. 1. Explicit Code & QA
  2. 2. 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
  3. 3. Remember, layersSimpler is easier to testDon’t make assumptionsCompromised browser = game over
  4. 4. These are not specifically security activities but they produce morestable code
  5. 5. Don’t rely on the server’s configuration Your bootstrap should Absolute Minimum set the defaults for any  Include paths PHP core functionality  Error reporting that your application is  Time zones using. These must be customizable for every environment e.g. (dev, staging, live)
  6. 6. Example Bootstrap<?php// All environment configuration for this application is done from this fileini_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();
  7. 7. 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
  8. 8. 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); }
  9. 9. Don’t use function parameter defaults "There should be one-- and preferably only one -- obvious way to do it." - Tim Peters 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
  10. 10. Default parameter exampleclass 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);}
  11. 11. Use explicit operators whenever possibleThis was actual production code.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");}Do you think this code worked as the author intended when $sincedate was set to2011-05-31?
  12. 12. Use constants for possible valuesConstants cannot be re-defined, so using a constant for configuration limits your codeto only ever be able to run in one configuration in a given stack.// Bad: This code cannot execute differently in the same stackdefine(‘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);
  13. 13. 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
  14. 14. Simulate strong/static typesPHP is dynamically typed, so we cannot do true strong or static typing but we canput controls in place which provide the same protection.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 = "SELECTn %snFROMn %s.%sn%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; }
  15. 15. Example Typing Assertionspublic 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); }}
  16. 16. Use PHPMD & CodeSnifferThis is the ./bin/codecheck script in MindFrame2#!/bin/bashWD="`/usr/bin/dirname $0`/../";echo Changing to working directory: $WD;cd $WD;if [ -z $1 ]; then DIR=.;else DIR=$1;fiphpmd --exclude coverage $DIR text codesize,unusedcode,naming,designphpcs --ignore=coverage --standard=`pwd`/Standards/MindFrame2 $DIRphpcs --ignore=coverage --report=source --standard=`pwd`/Standards/MindFrame2 $DIR
  17. 17. Example PHPMD Outputbryan@ubuntu-virtual ~/Code/MindFrame2 [130] $ ./bin/codecheck Dbms/DbiChanging to working directory: ./bin/..//home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:128 Avoid unused localvariables such as $partner./home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:185 Avoid unused localvariables such as $data./home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:199 Avoid unused localvariables such as $index./home/bryan/Code/MindFrame2/Dbms/Dbi/Distributed.php:240 Avoid unused privatemethods such as _buildReplicaDatabaseSuffix./home/bryan/Code/MindFrame2/Dbms/Dbi/Single.php:25 This class has too manymethods, consider refactoring it./home/bryan/Code/MindFrame2/Dbms/Dbi/Single.php:150 Avoid unused parameters such as$options.
  18. 18. Example PHPCS OutputFILE: /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--------------------------------------------------------------------------------
  19. 19. 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
  20. 20. 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
  21. 21. 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
  22. 22. ACLsMACThread limitsUnwanted services
  23. 23. If you’re interested in an application security career, come talk withme.