XSS, CSRF, Hijacking, Injection, ClickJacking... Come learn all the basics that you NEED to know to protect your software on the web.
This talk will focus on XSS, CSRF, Session Hijacking, SQL Injection, and other security vulnerabilities that need addressed in Website Development. This talk will cover a gambit of these issues, giving specific code examples showing what the vulnerabilities look like and how to identify them. It will then present solutions to close them down and protect yourself.
AlbaniaDreamin24 - How to easily use an API with Flows
Web Security - Northeast PHP, September 2014
1. Web Security and You
By: Oscar Merida
COO & Founding Partner:
musketeers.me
Editor-in-Chief:
php[architect]
!
!
oscarm.org - @OMerida
2. About Security
Do we really need to worry about this?
!
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
2
3. Security? Bah!
Whether big or small.
Someone will try to hack
It only takes one person!
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
3
!
you!
!
4. The Open Web Application Security Project
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
4
!
http://owasp.org/
!
The best online resource for learning about
various attack vectors and solutions to
them.
!
Use good judgement though,
often wiki-user edited ‘solutions’.
5. Stupid Programmer Errors
Let’s clear the air on these first …
!
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
5
6. Unchecked Permissions
Direct URL access to a protected file
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
6
7. Unchecked Permissions
Ability to URL-hack to access unauthorized data.
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
7
8. Information leaks
Specifically: Visible Error Handling
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
8
9. Low Security Hashes (Encryption)
Always salt your hashes!
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
9
!
Don’t just use MD5!
!
So many other options:
SHA512 SHA256 Blowfish etc!
!
Heck, even SHA1 is better!
!
!
For passwords
Use the latest PHP 5 password_hash
library!
Don’t roll your own.
10. Password guessing
Even the big boys don’t always get
Login forms should not allow unlimited guesses
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
10
!
Can be mitigated
!
this right
!
11. Password guessing mitigation
Rate-limiting
After a few attempts require
CAPTCHA
After more attempts lock
user out
Two-factor authentication
“Something you know,
something you have”
Log & notify
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
11
12. Various Attack Vectors
Now moving on to true ‘attacks’ …
!
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
12
13. SQL Injection
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
13
!
A user having the ability to send data
that is directly interpreted by your SQL
engine.
The Security Hole:
$pdo->query("SELECT * FROM users
WHERE name = '{$_POST['name']}' AND pass = '{$_POST['pass']}'");
The Attack:
$_GET['name'] = "' or 1=1; //";
14. SQL Injection
or
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
14
!
A user having the ability to send data
that is directly interpreted by your SQL
engine.
The Solution:
$query = $pdo->prepare("SELECT * FROM users WHERE name = ? AND pass = ?");
$query->execute(array($_POST['name'], $_POST['pass']));
$name = $pdo->quote($_POST['name']);
$pass = $pdo->quote($_POST['pass']);
$pdo->query("SELECT * FROM users WHERE name = {$name} AND pass = {$pass}");
15. Other Injection
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
15
!
Command Injection:
The user being able to inject code into a command line.
!
Unchecked File Uploads:
The user being allowed to upload an executable
file.
!
Code Injection:
User being able to directly inject code. (DON’T USE
EVAL!)
16. Session Hijacking
Avoid “Session Fixation”
Don’t use URL cookies for your
Always regenerate Session
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
16
!
One user ‘becoming’ another by taking
over their session via impersonation.
!
sessions.
!
IDs on a change of access
level.
!
Save an anti-hijack token to another cookie & session.
Require it to be present & match. Salt on unique data (such
as User Agent)
17. Session Fixation
The Solution:
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
17
!
A user being able to provide a
known session ID to another user.
The Attack:
session.use_cookies = 1
session.use_only_cookies = 1
session.cookie_httponly = 1
!
Don’t use URL cookies for your
sessions.
18. Session Fixation (Take 2)
session_start();
if ($user->login($_POST['user'], $_POST['pass'])) {
session_regenerate_id(TRUE);
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
18
!
Protect from more complicated fixation attacks,
by regenerating sessions on change of access
level.
The Solution:
and
}
session_start();
$user->logout();
session_regenerate_id(TRUE);
19. Session Anti-Hijack Measures
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
19
!
Finally use anti-hijack measures to ensure user
is legit
The Solution:
!
Note that IP changes or can be shared.
As happens with most other headers
too.
!
Not a few lines of code.
Store whatever unique you can about this user/browser
combination and verify it hasn’t changed between loads.
20. Session Anti-Hijack Measures
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
20
private function _sessionStart() {
session_start();
if (!empty($_SESSION)) { // Session not empty, verify:
$token = $this->_hijackToken();
$sh = empty($_SESSION['hijack']) ? NULL : $_SESSION['hijack'];
$ch = empty($_COOKIE['data']) ? NULL : $_COOKIE['data'];
if (!$sh || !$ch || ($sh != $ch) || ($sh != $token)) { // Hijacked!
session_write_close();
session_id(md5(uniq_id(mt_rand, true)));
session_start();
setcookie('data', 0, -172800);
header("Location: http://www.example.com/");
}
} else { // Empty/new session, create tokens
$_SESSION['started'] = date_format(new DateTime(), DateTime::ISO8601);
$_SESSION['hijack'] = $this->_hijackToken();
setcookie('data', $_SESSION['hijack']);
}
}
private function _hijackToken() {
$token = empty($_SERVER['HTTP_USER_AGENT']) ? 'N/A' : $_SERVER['HTTP_USER_AGENT'];
$token .= '| Hijacking is Bad mmmkay? |'; // Salt
$token .= $_SESSION['started']; // Random unique thing to this session
return sha1($token);
}
21. XSS (Cross Site Scripting)
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
21
!
A user sending data that is executed as
script
!
Many ways this attack can come in, but in all cases:
Everything from a user is suspect (forms, user-agent, headers, etc)
When fixing, escape to the situation (HTML, JS, XML, etc)
FIEO (Filter Input, Escape Output)
!
Don’t forget about rewritten URL strings!
22. XSS - Reflected XSS
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
22
!
Reflected XSS
Directly echoing back content from the
user
The Security Hole:
<p>Thank you for your submission: <?= $_POST['first_name'] ?></p>
The Attack:
23. XSS - Reflected XSS
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
23
!
Reflected XSS
Directly echoing back content from the
user
The Solution (HTML):
$name = htmlentities($_POST['first_name'], ENT_QUOTES, 'UTF-8', FALSE);
The Solution (JS):
$name = str_replace(array("rn","r","n"),
array("n","n","n"),addslashes($_POST['first_name']));
The Solution (XML):
$name = iconv('UTF-8', 'UTF-8//IGNORE',
preg_replace("#[x00-x1f]#msi", ' ',
str_replace('&', '&', $_POST['first_name'])));
24. XSS - Stored XSS
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
24
!
Stored XSS
You store the data, then later display it
The Security Hole:
<?php
$query = $pdo->prepare("UPDATE users SET first = ? WHERE id = 42");
$query->execute(array($_POST['first_name']));
?>
[...]
<?php
$result = $pdo->query("SELECT * FROM users WHERE id = 42");
$user = $result->fetchObject();
?>
<p>Welcome to <?= $user->first ?>’s Profile</p>
25. XSS - Stored XSS
The Solution (HTML):
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
25
!
Stored XSS
You store the data, then later display it
$name = htmlentities($user->first, ENT_QUOTES, 'UTF-8', FALSE);
The Solution (JS):
$name = str_replace(array("rn","r","n"),
array("n","n","n"),addslashes($user->first));
The Solution (XML):
$name = iconv('UTF-8', 'UTF-8//IGNORE',
preg_replace("#[x00-x1f]#msi", ' ',
str_replace('&', '&', $user->first)));
!
The Same!
26. XSS - DOM XSS
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
26
!
DOM XSS
What happens in JavaScript, stays in JavaScript
The Security Hole:
<script>
$('#verify').submit(function() {
var first = $(this).find("input[name=first]").val();
$(body).append("<p>Thanks for the submission: " + first + "</p>");
return false;
});
</script>
27. XSS - DOM XSS
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
27
!
DOM XSS
What happens in JavaScript, stays in JavaScript
The Solution (Simple):
<script>
function escapeHTML(str) {
str = str + ""; var out = "";
for (var i=0; i<str.length; i++) {
if (str[i] === '<') { out += '<'; }
else if (str[i] === '>') { out += '>'; }
else if (str[i] === "'") { out += '''; }
else if (str[i] === '"') { out += '"'; }
else { out += str[i]; }
}
return out;
}
</script>
But you have to deal with attr vs HTML vs CSS etc
So use this: https://github.com/chrisisbeef/jquery-encoder/
28. CSRF (Cross Site Request Forgery)
Simplistically via IMG tag or POST
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
28
!
A user having the ability to forge or force
a request on behalf of another user.
!
Complicated via JavaScript
!
forms
29. CSRF (Cross Site Request Forgery)
or
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
29
!
A user having the ability to forge or force
a request on behalf of another user.
The Attack:
<img width="1" height="1"
src="http://quackr.example.com/quackit?msg=CSRF+Attacks+Rock!" />
<script>
$.post({
url: 'http://quackr.example.com/quackit',
data: { msg: 'CSRF Attacks Rock!'}
});
</script>
30. CSRF (Cross Site Request Forgery)
!
Protect via CSRF
token
The Solution (on form):
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
30
<?php
function generateToken() {
$token = empty($_SESSION['token']) ? false : $_SESSION['token'];
$expires = empty($_SESSION['tExpires']) ? false : $_SESSION['tExpires'];
if (!$token || ($expires < time())) {
$token = md5(uniqid(mt_rand(), true));
$_SESSION['token'] = $token;
}
$_SESSION['tokenExpires'] = time() + 14400;
return $token;
}
?>
<form method="POST" action="">
<input name="msg" value="" />
<input type="hidden" name="token" value="<?= generateToken() ?>" />
<input type="submit" />
</form>
31. CSRF (Cross Site Request Forgery)
!
Protect via CSRF
token
The Solution (on submission):
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
31
<?php
$token = empty($_SESSION['token']) ? false : $_SESSION['token'];
$expires = empty($_SESSION['tExpires']) ? false : $_SESSION['tExpires'];
$check = empty($_POST['token']) ? false : $_POST['token'];
if ($token && ($token == $check) && ($expires > time())) {
// SUCCESS - Process the form
} else {
// FAILURE - Block this:
header('HTTP/1.0 403 Forbidden');
die;
}
?>
32. Clickjacking
One of the newest
Lots of publicity when Twitter was
Tricks user into physically making a click on the remote
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
32
!
threats
!
hit
!
website without realizing it, getting around any CSRF
protection.
!
Watch a demo:
33. Clickjacking
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
33
iframe { opacity: 0 }
34. Clickjacking - Solution 1
or
Became IETF standard RFC 7034 in October 2013
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
34
The Solution:
!
Use specific header, to disallow site
framing:
header('X-Frame-Options: DENY');
!!!
header('X-Frame-Options: SAMEORIGIN');
!
Doesn’t work in all
browsers!
!
35. Clickjacking - Solution 2
Ensure you aren’t displayed in
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
35
The Solution:
<html>
<head>
<style> body { display : none;} </style>
</head>
<body>
<script>
if (self == top) {
var theBody = document.getElementsByTagName('body')[0];
theBody.style.display = "block";
} else {
top.location = self.location;
}
</script>
</body>
</html>
!
iFrame
36. Questions?
For this presentation & more:
http://musketeers.me/
Twitter: @OMerida
php[architect]: http://phparch.com/
musketeers: http://musketeers.me/
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
36
Rate this talk!
https://joind.in/10823
37. Bonus Slides!
Doubt we’ll get here, but just in case
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
37
38. Brute force password attacks…
When they just won’t give up.
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
38
39. How to protect?
Really only two primary defenses:
IP rate limiting
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
39
!
CAPTCHA
!
40. Brute Force Attacks (CAPTCHA)
reCAPTCHA is free and easy to use
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
40
On the Form:
!
<?php require_once('recaptchalib.php'); ?>
<form method="POST" action="">
<label>Username: <input name="user" /></label><br />
<label>Password: <input name="pass" type="password"/></label><br />
<?= recaptcha_get_html("YOUR-PUBLIC-KEY"); ?>
<input type="submit" />
</form>
41. Brute Force Attacks (CAPTCHA)
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
41
On the Server:
<?php
require_once('recaptchalib.php');
$check = recaptcha_check_answer(
"YOUR-PRIVATE-KEY", $_SERVER["REMOTE_ADDR"],
$_POST["recaptcha_challenge_field"], $_POST["recaptcha_response_field"]);
if (!$check->is_valid) {
die("INVALID CAPTCHA");
} else {
// Yay, it's a human!
}
?>
!
https://developers.google.com/recaptcha/docs/php
42. Brute Force Attacks (Rate Limit)
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
42
The Solution:
!
Only allow so many fails per
IP
$blocked = false;
$cachekey = 'attempts.'.$_SERVER['REMOTE_ADDR'];
$now = new DateTime();
$attempts = $memcached->get($cachekey) ?: [];
if (count($attempts) > 4) {
$oldest = new DateTime($attempts[0]);
if ($oldest->modify('+5 minute') > $now) {
$blocked = true; // Block them
}
}
if (!$blocked && $user->login()) {
$memcached->delete($cachekey);
} else {
array_unshift($attempts, $now->format(DateTime::ISO8601));
$attempts = array_slice($attempts, 0, 5);
$memcached->set($cachekey, $attempts);
}
43. Server Level Security
Now moving on to true ‘attacks’ …
!
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
43
44. Keep Your Stack Patched
No excuses. Keep all your software up to
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
44
!
date!
!
Linux
!
Apache
!
MySQL
!
PHP
!
Python
!
Ruby
45. DDOS & Similar Attacks
Rely on firewall features
of your machines &
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
45
!
Good luck!
!
hosting.
!
Hire a good ops team
46. Man in the Middle
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
46
47. Man in the Middle
Web Security and You - Oscar Merida - North East PHP - September 6th, 2014
47
The Solution: Use SSL