Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Practical PHP Security

Practical PHP Security

PHP has a pretty bad track record of insecure applications. Fortunately, today we use components like Doctrine and Twig to make our applications secure by default (for the most part) from SQL Injection and XSS. But there are still plenty of other gotchas and sources of vulnerabilities that are written everyday. This talk will cover some common insecure practices that are perhaps less well known than SQL injection and cross-site scripting (XSS), and how to fix them.

Will Donohoe

August 18, 2015
Tweet

More Decks by Will Donohoe

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

  3. 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/

    View Slide

  4. extension:php mysql_query $_GET

    View Slide

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

    View Slide

  6. $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}

    View Slide

  7. 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.”

    View Slide

  8. 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!

    View Slide

  9. 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)

    View Slide

  10. 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=%%%

    View Slide

  11. 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

    View Slide

  12. $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

    View Slide

  13. extension:php doctrine expr like setParameter

    View Slide

  14. Keep PHP updated

    View Slide

  15. https://www.sektioneins.de/en/blog/15-07-31-
    php_challenge_2015.html
    $users = array(
    "0:c42d47602bc29f89644841702ca0ebf6", // UserID, md5 of pass
    "1:084e0343a0486ff05530df6c705c8bb4" // md5('guest')
    );
    $input = $_COOKIE['user'];
    $input[1] = md5($input[1]);
    foreach ($users as $user) {
    $user = explode(":", $user);
    if ($input === $user) {
    $uid = $input[0] + 0;
    }
    }
    if ($uid === 0) { die("Hello admin, have some secrets"); }
    http://3v4l.org/sELBf

    View Slide

  16. 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)

    View Slide

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

    View Slide

  18. 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']);
    // ...

    View Slide

  19. O:6:"Logger":3:{s:7:"logFile";s:
    10:"whoops.php";s:6:"buffer";s:28:"system($_GET["evil"]);";s:2:"fh";N;}
    When executed, writes ‘whoops.php’ with contents:

    View Slide

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

    View Slide

  21. 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)

    View Slide

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

    View Slide

  23. 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

    View Slide

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

    View Slide