Dip Your Toes in the
Sea of Security
James Titcumb
PHP UK Conference 2016
James Titcumb
www.jamestitcumb.com
www.roave.com
www.phphants.co.uk
www.phpsouthcoast.co.uk
@asgrim
Who is this guy?
Use “phpuk16” discount code!
Some simple code...
<?php
$a = (int)filter_var($_GET['a'], FILTER_SANITIZE_NUMBER_INT);
$b = (int)filter_var($_GET['b'], FILTER_SANITIZE_NUMBER_INT);
$result = $a + $b;
printf('The answer is %d', $result);
The Golden Rules
The Golden Rules
(my made up golden rules)
1. Keep it simple
2. Know the risks
3. Fail securely
4. Don’t reinvent the wheel
5. Never trust anything
OWASP
& the OWASP Top 10
https://www.owasp.org/
Application Security
(mainly PHP applications)
Always remember…
Filter Input
Escape Output
© 2003 Disney/Pixar. All Rights Reserved.
SQL Injection (#1)
SQL Injection (#1)
http://xkcd.com/327/
SQL Injection (#1)
1. Use PDO / mysqli
2. Use prepared / parameterized statements
SQL Injection (#1)
<?php
// user_id=1; DROP TABLE users; --
$user_id = $_GET['user_id'];
$sql = "
SELECT * FROM users
WHERE user_id = {$user_id}";
$db->execute($sql);
✘
SQL Injection (#1)
<?php
$user_id = $_GET['user_id'];
$sql = "
SELECT * FROM users
WHERE user_id = :userid";
$stmt = $db->prepare($sql);
$stmt->bind('userid', $user_id);
$stmt->execute();
✓
© 2003 Disney/Pixar. All Rights Reserved.
exec($_GET)
https://github.com/search?q=exec%28%24_GET&ref=cmdform&type=Code
eval()
https://github.com/search?q=eval%28%24_GET&type=Code&ref=searchresults
Cross-Site Scripting / XSS (#3)
© 2003 Disney/Pixar. All Rights Reserved.
Cross-Site Scripting / XSS (#3)
● Escape output
<?php
$unfilteredInput = '<script type="text/javascript">...</script>';
// Unescaped - JS will run :'(
echo $unfilteredInput;
// Escaped - JS will not run :)
echo htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
Cross-Site Request Forgery
or CSRF (#8)
http://www.factzoo.com/invertebrates/cuttlefish-chameleon-of-the-sea.html
<?php
if (!$isPost) {
$csrfToken = base64_encode(random_bytes(32)));
$_SESSION['csrf_token'] = $csrfToken;
// ... output the form ...
echo '<input type="hidden" name="csrf_token" value="'.$csrfToken.'" />';
} else if ($isPost) {
if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die("Token invalid...");
}
// ... handle the form ...
}
Cross-Site Request Forgery / CSRF (#8)
<?php
if (!$isPost) {
$csrfToken = base64_encode(random_bytes(32)));
$_SESSION['csrf_token'] = $csrfToken;
// ... output the form ...
echo '<input type="hidden" name="csrf_token" value="'.$csrfToken.'" />';
} else if ($isPost) {
if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die("Token invalid...");
}
// ... handle the form ...
}
Cross-Site Request Forgery / CSRF (#8)
<?php
if (!$isPost) {
$csrfToken = base64_encode(random_bytes(32)));
$_SESSION['csrf_token'] = $csrfToken;
// ... output the form ...
echo '<input type="hidden" name="csrf_token" value="'.$csrfToken.'" />';
} else if ($isPost) {
if (hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die("Token invalid...");
}
// ... handle the form ...
}
Cross-Site Request Forgery / CSRF (#8)
Timing attacks
From zend_is_identical:
return (Z_STR_P(op1) == Z_STR_P(op2) ||
(Z_STRLEN_P(op1) == Z_STRLEN_P(op2) &&
memcmp(Z_STRVAL_P(op1), Z_STRVAL_P(op2), Z_STRLEN_P(op1)) == 0));
Timing attacks
Actual string: “foobar”
● a (0.00001)
● aa (0.00001)
● aaa (0.00001)
● aaaa (0.00001)
● aaaaa (0.00001)
● aaaaaa (0.00002) ← success!
● aaaaaaa (0.00001)
● aaaaaaaa (0.00001)
● aaaaaaaaa (0.00001)
Timing attacks
1 int memcmp(const void* s1, const void* s2,size_t n)
2 {
3 const unsigned char *p1 = s1, *p2 = s2;
4 while(n--)
5 if( *p1 != *p2 )
6 return *p1 - *p2;
7 else
8 p1++,p2++;
9 return 0;
10 }
http://clc-wiki.net/wiki/C_standard_library:string.h:memcmp#Implementation
Timing attacks
Actual string: “foobar”
● “aaaaaa” (0.00001)
● “baaaaa” (0.00001)
● …
● “faaaaa” (0.00002) ← success!
● “fbaaaa” (0.00002)
● “fcaaaa” (0.00002)
● …
● “foaaaa” (0.00003) ← success!
Sensitive Data Exposure (#6)
© 2003 Disney/Pixar. All Rights Reserved.
Sensitive Data Exposure (#6)
© 2003 Disney/Pixar. All Rights Reserved.
curl + https
<?php
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl + https
<?php
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_CAINFO, "/path/to/certificate");
© 2003 Disney/Pixar. All Rights Reserved.
Third Party Code
Third Party Code
!!! WARNING !!!
Third Party Code
github.com/ /SecurityAdvisories
!!! WARNING !!!
We are not all
security experts!
We are not all
security experts!
… but we CAN write secure code
Hack your own system!
© 2003 Disney/Pixar. All Rights Reserved.
What do you want?
Think like a hacker
How do you get it?
Think Differently
Threat Modelling
D.R.E.A.D.
© Buena Vista Pictures
Threat Modelling
Damage
R
E
A
D
© Buena Vista Pictures
Threat Modelling
Damage
Reproducibility
E
A
D
© Buena Vista Pictures
Threat Modelling
Damage
Reproducibility
Exploitability
A
D
© Buena Vista Pictures
Threat Modelling
Damage
Reproducibility
Exploitability
Affected users
D
© Buena Vista Pictures
Threat Modelling
Damage
Reproducibility
Exploitability
Affected users
Discoverability
© Buena Vista Pictures
Put them in order
And fix them!
© Buena Vista Pictures
Authentication
& Authorization
Authentication
Verifying Identity
Case Study: Custom Authentication
We thought about doing this…
Case Study: Custom Authentication
We thought about doing this…
Case Study: Custom Authentication
We thought about doing this…
Password Hashing
password_hash()
Authorization
Verifying Access
CRYPTOGRAPHY
IS
HARD
CRYPTOGRAPHY
IS
HARD
NEVER EVER “ROLL YOUR OWN”
CRYPTOGRAPHY
IS
HARD
NEVER EVER “ROLL YOUR OWN”
EVER!!!
How to encrypt then?
I’ve got some
great ideas for
encryption...
Image: The Guardian (http://goo.gl/pUkyvO)
How to encrypt then?
libsodium PECL package
Linux Server Security
Create an SSH Fortress
Firewalls
iptables
#!/bin/bash
IPT="/sbin/iptables"
$IPT --flush
$IPT --delete-chain
$IPT -P INPUT DROP
$IPT -P FORWARD DROP
$IPT -P OUTPUT DROP
# Loopback
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT
# Inbound traffic
$IPT -A INPUT -p tcp --dport ssh -j ACCEPT
$IPT -A INPUT -p tcp --dport 80 -j ACCEPT
$IPT -A INPUT -p tcp --dport 443 -j ACCEPT
# Outbound traffic
$IPT -A OUTPUT -p tcp --dport 80 -j ACCEPT
$IPT -A OUTPUT -p tcp --dport 443 -j ACCEPT
$IPT -A OUTPUT -p udp --dport 53 -m state --state NEW -j ACCEPT
ufw
sudo ufw enable
sudo ufw allow 22
sudo ufw allow 80
Mitigate Brute Force
Attacks
Install Only
What You Need
© 2003 Disney/Pixar. All Rights Reserved.
+
Case Study: Be Minimal
Internets
Postfix
Squid Proxy
(badly configured)
hacker
spam
Resources
● http://securingphp.com/
● https://www.owasp.org/
● http://blog.ircmaxell.com/
● https://github.com/paragonie/random_compat
● https://github.com/ircmaxell/password_compat
● https://paragonie.com/blog
● https://websec.io/resources.php
The Golden Rules
1. Keep it simple
2. Know the risks
3. Fail securely
4. Don’t reinvent the wheel
5. Never trust anything / anyone
If you follow all this, you get...
If you follow all this, you get...
Any questions? :)
https://joind.in/talk/c2bb0
James Titcumb @asgrim

Dip Your Toes in the Sea of Security (PHP UK 2016)