secure programming with the zend framework

44
Secure Programming with the Zend-Framework Stefan Esser <[email protected]> June 2009 - Amsterdam http://www.sektioneins.de

Upload: kaplumbaga

Post on 16-Nov-2014

4.273 views

Category:

Documents


0 download

DESCRIPTION

Stefan Esser's "Secure Programming with the Zend Framework" slides from Dutch PHP Conference 2009

TRANSCRIPT

Page 1: Secure Programming with the Zend Framework

Secure Programming with the Zend-FrameworkStefan Esser <[email protected]>

June 2009 - Amsterdam

http://www.sektioneins.de

Page 2: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Who I am?

Stefan Esser

• from Cologne / Germany

• Information-Security since 1998

• PHP Core Developer since 2001

• Month of PHP Bugs and Suhosin

• Head of Research and Development at SektionEins GmbH

2

Page 3: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Part IIntroduction

3

Page 4: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Introduction

• Using the Zend-Framework got very popular in the last years

• Growing request of security for Zend-Framework based applications

• Books/Talks/Seminars concentrate on secure programming of PHP applications without a framework

• Using a framework requires different ways to implement protections

• Some frameworks come with their own security features

4

Page 5: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Topics

• Authentication

• Input Validation and Input Filtering

• SQL Security

• Cross Site Request Forgery (CSRF) Protection

• Session Management Security

• Cross Site Scripting (XSS) Protection

5

Page 6: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Part IIAuthentication

6

Page 7: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Classic Applications vs. Zend-Framework

• Zend-Framework applications usually use a MVC design with dispatcher

• Classic applications usually use neither a MVC design, nor a dispatcher

• without dispatcher every reachable script must implement or embed authentication

• classic approach is error-prone

• often scripts exists that forget to implement the authentication

7

Controller

View Model

Dispatcher

Page 8: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Central Authentication in Controller

• Deriving the Zend_Controller_Action

• Authentication implemented in init() method

• Attention: if a controller has an own init() method then method of the parent class must be called

8

class My_Controller_Action extends Zend_Controller_Action{ /** * Init function * * First check if this is a logged in user, ... */ public function init() { $isLoggedIn = true; try { My_Auth::isLoggedIn(); } catch (My_Auth_UserNotLoggedInException $e) { $isLoggedIn = false; } ... }

Page 9: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Part IIIInput Validation and Input Filtering

9

Page 10: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Accessing Request Parameters (I)

• Traditionally PHP applications access user input directly

➡ $_GET, $_POST, $_COOKIE, $_REQUEST, $_SERVER, $_ENV, $_FILES

• Form of access also possible in Zend-Framework, but not usual

➡ Input validation and input filtering not directly portable from traditional PHP applications

10

Page 11: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Accessing Request Parameters (II)

• Access via request object Zend_Controller_Request_Http

• Either via methods or magic properties

• Access is unfiltered - only raw data

• Access via magic property in the following order

1. internal parameter array

2. $_GET

3. $_POST

4. $_COOKIE

5. $_SERVER

6. $_ENV

11

$message = $this->getRequest()->message;

Page 12: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Accessing Request Parameters (III)

• function getQuery($key = null, $default = null)

• function getPost($key = null, $default = null)

• function getCookie($key = null, $default = null)

• function getServer($key = null, $default = null)

• function getEnv($key = null, $default = null)

• wrapper around $_GET / $_POST / $_COOKIE / $_SERVER / $_ENV with the possibility to return a default value

12

Page 13: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Accessing Request Parameters (IV)

• function getParam($key = null, $default = null)

• gets parameters from the internal parameter array and from $_GET and $_POST or returns the default value

• parameter sources can be configured ($_GET / $_POST)

• similar to $_REQUEST without $_COOKIE

• function getParams($key = null, $default = null)

• gets all parameters from the internal parameter array, $_GET and $_POST

• in case of double entries, later entries will overwrite the earlier entries

13

Page 14: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Validation with Zend_Validate

• Validators to validate parameters

• Zend-Framework comes with a set of validators

Alnum, Alpha, Barcode, Between, Ccnum, Date, Digits, EmailAddress, Float, GreaterThen, Hex, Hostname, Iban, InArray, Int, Ip, LessThan, NotEmpty, Regex, StringLength

14

<?php$email = $this->getRequest()->getPost('email', '[email protected]');

$validator = new Zend_Validate_EmailAddress();if ($validator->isValid($email)) { // email seems valid} else { // email seems invalid; Outputting the reasons foreach ($validator->getMessages() as $message) { echo "$message\n"; }}?>

Page 15: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Chaining Validators

• for complex validations own validators can be implemented

• it is however possible to combine validators in validator chains

15

<?php// Creating a Validator Chain$validatorChain = new Zend_Validate();$validatorChain->addValidator(new Zend_Validate_StringLength(6, 12)) ->addValidator(new Zend_Validate_Alnum());

// Validation of "username"if ($validatorChain->isValid($username)) { // "username" is valid} else { // "username" is invalid; Outputting the reasons foreach ($validatorChain->getMessages() as $message) { echo "$message\n"; }}?>

Page 16: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Filtering with Zend_Filter

• Filtering of parameters is done with filters

• Zend-Framework comes with a set of pre defined filters

Alnum, Alpha, BaseName, Callback, Decrypt, Digits, Dir, Encrypt, Htmlentities, Int, StripNewlines, RealPath, StringToUpper, StringToLower, StringTrim, StripTags

16

<?php$message = $this->getRequest()->getPost('message', '');

$filter = new Zend_Filter_StripTags();

// remove all tags$message = $filter->filter($message);?>

Page 17: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Chaining Filters

• for complex filtering own filters can be implemented

• it is however possible to combine filters in filter chains

17

<?php// Create a filter chain and add filters$filterChain = new Zend_Filter();$filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower());

// Filtering "username"$username = $filterKette->filter($username);?>

Page 18: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Inputvalidation/-filtering in Forms (I)

• ZF-Forms use validators and filters automatically

• they are attached to Zend_Form_Element objects

• and can be chained as wished

18

// create name element$name = $form->createElement('text', 'name', array('size' => 40, 'maxlength' => 40));$name->addValidator('Alpha') ->addValidator('StringLength', false, array(1, 40)) ->setLabel('Name') ->setRequired(true); // create message element$message = $form->createElement('textarea', 'message', array('rows' => 6, 'cols' => 40));$message->setLabel('Message') ->setRequired(true) ->addFilter('StripTags'); // create submit button$submit = $form->createElement('submit', 'send');$submit->setLabel('send'); // add all elements to the form$form->addElement($name)->addElement($message)->addElement($submit);

Page 19: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Inputvalidation/-filtering in Forms (II)

• Form is validated in the action handler

19

// checking form data for validity if (!$form->isValid($this->getRequest()->getPost())) { // submit varibales to view $this->view->form = $form; $this->view->title = "Form 1"; // stop processing return $this->render('form'); }

Page 20: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Validation and Filtering with Zend_Filter_Input

• is a framework for validation and filtering complete arrays

• applies defined filter and validation ruleset to supplied data

• allows validation of all user input automatically

20

$filters = array( '*' => 'StringTrim', 'month' => 'Digits');

$validators = array( 'month' => array( new Zend_Validate_Int(), new Zend_Validate_Between(1, 12) ));

$params = $this->getRequest()->getParams();$input = new Zend_Filter_Input($filters, $validators, $params);

if ($input->isValid()) { echo "OK\n";}

Page 21: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Part IVSQL Security

21

Page 22: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

SQL Security - Traditionally

• Traditional PHP Applications

• use PHP‘s database extensions directly

• use their own database abstraction layer

• use PDO

• lots and lots of different escaping functions

• escaping only supports data not identifiers

• partially support for prepared statements

22

Page 23: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Databaseaccess in Zend-Framewok Applications

➡Zend-Framework offers different APIs for handling queries

• Zend_Db

• Zend_Db_Statement

• Zend_Db_Select

• Zend_Db_Table

23

Page 24: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Zend_Db - Queries (I)

• function query($sql, $bind = array())

• uses prepared statement internally

• SQL-Injection still possible if $sql is dynamically created

• function fetchAll($sql, $bind = array(), $fetchMode = null)

• all „fetch“ methods use prepared statements internally

• SQL-Injection still possible if $sql is dynamically created

24

<?php $sql = "SELECT id FROM _users WHERE lastname=? AND age=?"; $params = array('Smith', '18'); $res = $db->fetchAll($sql, $params);?>

Page 25: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Zend_Db - Queries (II)

• function insert($table, array $bind)

• internally uses prepared statements

• SQL-Injection not possible

• function update($table, array $bind, $where = '')

• uses partially prepared statements

• SQL-Injection still possible if $where is dynamically created

• function delete($table, $where = '')

• SQL-Injection still possible if $where is dynamically created

25

Page 26: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Zend_Db - Escaping

• function quote($value, $type = null)

• applies the correct escaping - one function not many

• ATTENTION: also puts strings in quotes

• function quoteIdentifier($ident, $auto=false)

• applies escaping for identifiers

• a function not available to traditional PHP applications

• ATTENTION: also puts strings in quotes

26

Page 27: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Zend_Db_Select

• used to dynamically build SELECT statements

• uses partially prepared statements

• SQL-Injectionen still possible when wrongly used

• vulnerable through: WHERE / ORDER BY

27

// Build this query:// SELECT product_id, product_name, price// FROM "products"// WHERE (price < 100.00 OR price > 500.00)// AND (product_name = 'Apple')

$minimumPrice = 100;$maximumPrice = 500;$prod = 'Apple';

$select = $db->select() ->from('products', array('product_id', 'product_name', 'price')) ->where("price < $minimumPrice OR price > $maximumPrice") ->where('product_name = ?', $prod);

Page 28: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Part VCross Site Request Forgery (CSRF) Protection

28

Page 29: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Cross Site Request Forgery (CSRF) Protection

• Protections against CSRF attacks are usually based on secret, session depended form tokens

• Zend-Framework offers Zend_Form_Element_Hash which is a secret token with built-in validator

• HTML forms can be secured against CSRF attacks by just adding the form element to the form

29

$form->addElement('hash', 'csrf_token', array('salt' => 's3cr3ts4ltG%Ek@on9!'));

Page 30: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Automatic CSRF Protection

• normally protection must be added manually

• by deriving Zend_Form it is possible to create an own form class that automatically comes with CSRF protection

30

<?phpclass My_Form extends Zend_Form{ function __construct() { parent::__construct(); $this->addElement('hash', 'csrf_token', array('salt' => get_class($this) . 's3cr3t%Ek@on9!')); }}?>

Page 31: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Token Algorithm

• Token algorithm could be improved

• avoid mt_rand()

• more entropy

• but it is safe enough (for now)

31

/** * Generate CSRF token * */ protected function _generateHash() { $this->_hash = md5( mt_rand(1,1000000) . $this->getSalt() . $this->getName() . mt_rand(1,1000000) ); $this->setValue($this->_hash); }

Page 32: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Part VISession Management Security

32

Page 33: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Session Management Configuration

• Configuration has big influence on security

• to safeguard SSL applications set the secure flag

• use an own session id for each application

• harden the session cookie against XSS with the httpOnly flag

• define the maximal lifetime

33

<?phpZend_Session::setOptions(array( /* SSL server */ 'cookie_secure' => true, /* own name */ 'name' => 'mySSL', /* own storage */ 'save_path' => '/sessions/mySSL', /* XSS hardening */ 'cookie_httponly' => true, /* short lifetime */ 'gc_maxlifetime' => 15 * 60 ));Zend_Session::start();?>

Page 34: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Session Fixation and Session Hijacking

• Session Fixation

• is harder in case of session validation / strict session handling

• but is only stopped by regenerating the session id after each change in status

Zend_Session::regenerateId();

• should be added directly into the login functionality

• Session Hijacking

• there is only one real protection - SSL

• httpOnly cookies protect against session id theft by XSS

• session validation only of limited use

34

Page 35: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Session Validation (I)

• recognizes a valid session by checking certain additional information stored in the session

• often recommended as protection against session fixation/hijacking - but doesn‘t make much sense

• Zend-Framework supports session validators to validate sessions

• Zend_Session_Validator_HttpUserAgent

35

<?phptry { Zend_Session::start();} catch (Zend_Session_Exception $e) { Zend_Session::destroy(); Zend_Session::start(); Zend_Session::regenerateId();}Zend_Session::registerValidator(new Zend_Session_Validator_HttpUserAgent());?>

Page 36: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Session Validation (II)

• Attention checking additional information can cause trouble

• User-agent HTTP header checking is dead since Internet Explorer 8

• Accept HTTP header checks have always been a problem with Microsoft Internet Explorer

• Checking the client‘s IP address is a problem when big proxy farms are used (big companies/ISPs)

➡ possible to limit to class C/B/A networks

➡ but useful for SSL applications

36

Page 37: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Session Validation - Validating Client‘s IP Address

37

<?phpclass Zend_Session_Validator_RemoteAddress extends Zend_Session_Validator_Abstract{

/** * Setup() - this method will get the client's remote address and store * it in the session as 'valid data' * * @return void */ public function setup() { $this->setValidData( (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null) ); }

/** * Validate() - this method will determine if the client's remote addr * matches the remote address we stored when we initialized this variable. * * @return bool */ public function validate() { $currentBrowser = (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null);

return $currentBrowser === $this->getValidData(); }

}?>

Page 38: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Part VIICross Site Scripting (XSS) Protection

38

Page 39: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

XSS in Zend-Framework Applications

• Symfony supports automatic output escaping

• Zend-Framework doesn‘t support such automagic

• preventing XSS is job of the programmer

• XSS occurs in the „view“ part

39

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title><?php echo $this->title; ?></title></head><body><h2><?php echo $this->headline; ?></h2><ul><li><a href="<?php echo $this->link; ?>">Link 1</a></li></ul></body></html>

Page 40: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Protecting against XSS (I)

• Two alternative traditional protections

1. Encoding before echoing

40

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title><?php echo $this->escape($this->title); ?></title></head><body><h2><?php echo $this->escape($this->headline); ?></h2><ul><li><a href="<?php echo urlprepare($this->link); ?>">Link 1</a></li></ul></body></html>

Page 41: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Protecting against XSS (II)

• Two alternative traditional protections

2. Encoding when assigning template variables

41

$entityFilter = new Zend_Filter_HtmlEntities();$urlFilter = new My_Filter_Url(); $this->view->title = $this->escape("Page 1");$this->view->headline = $entitiyFilter->filter($this->getRequest()->getPost('link'));$this->view->link = $urlFilter->filter($this->getRequest()->getPost('link'));

Page 42: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Protecting with Zend_View_Helper

• preventing XSS is error prone - one XSS for every forgotten encoding

• automatically scanning for forgotten escaping is hard

• directly echoing variables should be forbidden (e.g. with Bytekit + pre-commit-hook)

• output only via Zend_View_Helper

• preventing XSS becomes job of Zend_View_Helper

42

<form action="action.php" method="post"> <p><label>Your Email:<?php echo $this->formText('email', '[email protected]', array('size' => 32)) ?> </label></p> <p><label>Your Country:<?php echo $this->formSelect('country', 'us', null, $this->countries) ?> </label></p> <p><label>Would you like to opt in?<?php echo $this->formCheckbox('opt_in', 'yes', null, array('yes', 'no')) ?> </label></p></form>

Page 43: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Automatic Escaping by deriving Zend_View

• all output goes through Zend_View

• deriving Zend_View allows automatic encoding

• e.g. by overloading __set() and __get()

• Attention: Encoding must be context sensitive (e.g.: javascript: Links)

43

public function __get($key){ if (isset($this->_params[$key])) { return($this->escape($this->_params[$key])); } return null;}

public function __set($key, $val){ $this->_params[$key] = $val;}

Page 44: Secure Programming with the Zend Framework

Stefan Esser • Secure Programming with the Zend-Framework • June 2009 • 

Thank you for listening...

Questions ?http://www.sektioneins.de

44