Slide 1

Slide 1 text

Practical PHP Security Will Donohoe McSimp Melbourne PHP Users Group August 2015

Slide 2

Slide 2 text

State of affairs https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=wordpress 6 in standalone WordPress so far in 2015

Slide 3

Slide 3 text

SQLi and XSS • SQL Injection and Cross Site Scripting • #1 and #3 respectively on OWASP Top 10 • https://secure.php.net/manual/en/ security.database.sql-injection.php • http://www.sitepoint.com/php-security-cross-site- scripting-attacks-xss/

Slide 4

Slide 4 text

extension:php mysql_query $_GET

Slide 5

Slide 5 text

• SQLi and XSS are solved problems* • Use Doctrine and Twig • Or just use Symfony

Slide 6

Slide 6 text

$input = json_decode($_GET['data'], true); if ($input['auth_token'] == 'my_secret_auth_token') { echo 'Authorised!'; } else { echo 'You are not authorised'; } int(0)== any string that isn’t a number data={"auth_token": 0}

Slide 7

Slide 7 text

Route::filter('csrf', function() { if (Session::token() != Input::get('_token')) { throw new Illuminate\Session\TokenMismatchException; } }); Speaking of Laravel… “On November 7th, Chris Smith (@chrismsnz) of Insomnia Security alerted the Laravel development team of a method of bypassing the CSRF verification in Laravel 4 applications.”

Slide 8

Slide 8 text

http://stackoverflow.com/q/22140204 string '0e462097431906509019562988736854' (length=32) md5('240610708'); string '0e830400451993494058024219903391' (length=32) md5('QNKCDZO'); md5('240610708') == md5('QNKCDZO') boolean true Hooray for scientific notation!

Slide 9

Slide 9 text

Comparison is hard • Lots of stuff == lots of other weird stuff • Use === by default, typecast if you need to • Use hash_equals for hashes or tokens • Use == only if you have a really good reason • https://php.net/manual/en/types.comparisons.php • See Natas Level 24 in OTW (strcmp)

Slide 10

Slide 10 text

Doctrine is great but… $search = $_GET['search']; if (strlen(trim($search)) < 3) { die('Invalid'); } $qb = $entityManager->createQueryBuilder(); $qb->select('u') ->from('User', 'u') ->where($qb->expr()->like('u.name', ':search')) ->orderBy('u.name', 'ASC') ->setParameter('search', $search . '%'); $result = $qb->getQuery()->getResult(); search=%%%

Slide 11

Slide 11 text

The Fix • Docs will not help you: make no reference to this problem when using LIKE • So just use addcslashes to escape % and _ • https://doctrine-orm.readthedocs.org/en/latest/ reference/query-builder.html#high-level-api- methods • https://doctrine-orm.readthedocs.org/en/latest/ reference/query-builder.html#the-expr-class

Slide 12

Slide 12 text

$search = $_GET['search']; if (strlen(trim($search)) < 3) { die('Invalid'); } $search = addcslashes($search, '%_'); $qb = $entityManager->createQueryBuilder(); $qb->select('u') ->from('User', 'u') ->where($qb->expr()->like('u.name', ':search')) ->orderBy('u.name', 'ASC') ->setParameter('search', $search . '%'); $result = $qb->getQuery()->getResult(); The Fix

Slide 13

Slide 13 text

extension:php doctrine expr like setParameter

Slide 14

Slide 14 text

Keep PHP updated

Slide 15

Slide 15 text

https://www.sektioneins.de/en/blog/15-07-31- php_challenge_2015.html

Slide 16

Slide 16 text

Why? • PHP 5.5.27/5.6.11 was released on 10 July 2015 (http://www.php.net/ChangeLog-5.php) • "Fixed bug #69892 (Different arrays compare indentical due to integer key truncation).” • 4294967296 == 0x100000000 (larger than 32 bits, but least significant 32 bits are 0)

Slide 17

Slide 17 text

serialize/unserialize • More harmful than it looks • Except if you read the entire doc page on it But how?

Slide 18

Slide 18 text

class Logger { public $logFile; public $buffer; public $fh; public function __destruct() { $this->WriteBuffer(); } public function WriteBuffer() { if (!$this->fh) { $this->fh = fopen($this->logFile, 'w'); } fwrite($this->fh, $this->buffer); } // ... } // ... $cookieData = unserialize($_COOKIE['data']); // ...

Slide 19

Slide 19 text

O:6:"Logger":3:{s:7:"logFile";s: 10:"whoops.php";s:6:"buffer";s:28:"

Slide 20

Slide 20 text

http://stackoverflow.com/a/9032082 This is near the top of the Google results for ‘php store array cookie'

Slide 21

Slide 21 text

Other Problems • CSRF • .git folder accessible publicly
 https://github.com/jkingsman/get-git • nginx + php-fpm misconfiguration - mistakenly running code in uploaded files • Local file inclusion with PHP filters • Remember that $_GET, $_POST, $_COOKIE can contain arrays! (Watch out for hash functions)

Slide 22

Slide 22 text

• Length extension • HEAD request stops execution on output • app_dev.php for Symfony • Stop execution by closing connection Other Problems

Slide 23

Slide 23 text

More Links • https://reddit.com/r/netsec • https://ctftime.org/ • http://overthewire.org/wargames/natas/ • https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet • https://www.owasp.org/index.php/ Category:OWASP_Top_Ten_Project • @i0n1c • @homakov

Slide 24

Slide 24 text

In Summary • Validate your inputs • Validate your outputs • Be aware of the oddities of the tools you use • Update early and often