Author Archives: mrjones

Web App Security Cheat Sheet

1 minute, 36 seconds

I recently documented best security practices for writing a web app. Since I was most of the way there for a nice tidy blog post on the topic, I’m using the contents of those docs as a cheat sheet for web app security. Best to refer to OWASP for a more exhaustive list, but here’s mine in PHP/Apache:

title description sample
No Traffic on Port 80 Always use an encrypted channel for web traffic, never send a session cookie in the clear RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
HSTS Set header so after 1st redirect, client goes directly to SSL connection Header always set Strict-Transport-Security
“max-age=15768000”
Forward Secrecy Use ephemeral keys for TLS connections to prevent wholesale decryption if private key is compromised SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
SSLProtocol All -SSLv2 -SSLv3
SSLHonorCipherOrder On
Anti-Clickjacking Prevent your site from being included in an iframe Header always set X-Frame-Options DENY
Anti-CSRF Nonces No one getting tricked to submit pre-filled forms using their existing session cookie $_POST[$NONCEKEY] == $_COOKIE[$NONCEKEY]
(but more to it – see this gist)
XSS escaping NEVER TRUST USER INPUT, save raw values to DB, cleanse on display print htmlspecialchars($user_string_here, ENT_QUOTES, ‘UTF-8’)
Secure cookies Never transmit cookies in the clear, works well with HSTS setcookie( ‘session’, $sessionId, 0, ‘/’, ‘www.example.com’, isset($_SERVER[“HTTPS”]), true);
HttpOnly cookies don’t allow JavaScript/XSS to access your cookies setcookie( ‘session’, $sessionId, 0, ‘/’, ‘www.example.com’, isset($_SERVER[“HTTPS”]), true);
bcrypt hashes Use a *slow* hashing method instead of a *fast* one (I’m looking at you MD5…) password_hash(“rasmuslerdorf”, PASSWORD_DEFAULT)
Though Argon2 is up and coming!
strong session ID don’t store anything but pseudo random ID in the cookie. Again NEVER TRUST USER INPUT! bin2hex(openssl_random_pseudo_bytes(50))
no session fixation on logout, delete cookie **and** server side entry in DB so session is dead dead dead NA
re-prompt for passwords on email, password or username change or when showing/changing sensitive data NA
no SQL injection still NEVER TRUST USER INPUT, parameterize all queries $dbh = new PDO(…);
$stmt = $dbh->prepare(‘SELECT * FROM users where username = :username’);
$stmt->execute( array(‘:username’ => $_REQUEST[‘username’]) );

Penance for forgetting basic debugging

1 minute, 56 seconds

I was working on implemented a nonce to fight CSRF requests on all our forms. The nonce worked liked you expect: by setting a cookie and then putting the same value in the form. When the form was submitted, the back end checked that the form nonce matched the cookie nonce and, bam, bob is your uncle.

The HTML for the form was hosted on foo.domain.com and was fetched via an AJAX call to render it in a modal on bar.domain.com. It used a JavaScript function that looked like this:

  $('.login_action').click(function(){
    Modal.init({
      id: "login",
      primary_button_action: function(){
        $('form #loginform').submit();
      },
      url: URL_ROOT + "modal/login",
    }).open();
  });

Now, you can’t quite tell, but this is ultimately a wrapper for Twitter’s Bootstrap’s Modal call:

$('#login').modal(options)

For the life of me I could not figure out when the AJAX call from foo subdomain to bar subdomain was not setting the nonce cookie. I thought it would be a CORS issue, but quickly dismissed that because we all know that “AJAX Requests Get And Set Cookies Like Any Other HTTP Request“, right. RIGHT?!!?

I did follow the best practice of putting in poor man’s PHP break points to trace the $_POST and $_COOKIE values, but to no avail.

After far too long, but after a healthy discussion with a friend about how we learn and how we trouble shoot, I remembered my mantra I always tell junior engineers: “Simplify the problem! Distill it down. Ideally, continue to cut the code in half again and again until just the bare minimum manifesting the problem remains.”

This lead me to reproduce the problem in two lines of code on foo.domain.com to call bar.domain.com:



The problem was still there! No nonce cookies getting set. Let’s remove jQuery then and go to raw JS requests:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://bar.domain.com', true);
xhr.withCredentials = true;
xhr.send();

Wait ah second! What was that I see in the console?!@@!#%$:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://bar.domain.com. (Reason: CORS header 'Access-Control-Allow-Origin' does not match '*').

Oh…CORS…the thing I dismissed upfront. So, yes, AJAX calls are just HTTP requests per bennadel.com’s blog post, but of course if foo subdomain is calling the modal on bar subdomain via AJAX, it’s an XHR request to another (sub)domain which needs to play by CORS rules.

If any one is interested, I published a gist with our PHP code to do nonces.

Diceware in a box

1 minute, 2 seconds

I’m really happy with my most recent project I did at the shop. For some time now I’ve been wanting to upgrade from a mostly secure password to a phenomenally secure passphrase. For some down home good entropy, you should use the Diceware method to generate a passphrase. In order to achieve this, I made a “Diceware in a box”.  The ingredients for this recipe are:

Here’s what the final product looks like (click to see larger version):


    

The only changes from my original design I would do is instead of scrounging up some rubber feet to put on the side so the dice bounce instead of slide, is that I’d just lay down long strips of hot glue on the inside. This would achieve the same affect and be a lot easier.

Here’s the product in action:

https://vimeo.com/155320354
And here’s the result (please do not post your passphrase to the internet ;)

https://vimeo.com/155320347

range herr vq fr kirby dad pp!!!

Top Posts Update

0 minutes, 54 seconds

A while ago I did a write up of the top blog posts on this site.  Oh…um, I guess that was 5 years ago now.  Wowaa.  Well, Here’s the same script run again to give you an idea of what’s popular:

$ grep 'GET /blog' plip_log/access_log|cut -d" " -f 7|egrep -v '.png|.jpg|wp-includes|.css|/page/|/category/|xmlrpc|wp-trackback|/feed/|wp-login|/wp-content/|/trackback/|wp-comments|wp-app.php|wp-admin|comment-page|index.php|?p=|page_id|comments|feed'|sort|cut -d"/" -f 3|uniq -c|grep -v ' 1 '|sort -nr|head
   1026 free-idea-abstracted-facebook-nonymizer
    959 adendum-to-ashleys-law-problematic-imac-vesa-mounts-and-new-desks
    805 toss-your-salad-code
    693 thoughts-on-very-large-monitors
    650 photos-food-bikes-sunsets-and-stars
    635 sunset-and-rainbows
    385 oakland-sf-photos-coffee-and-scotch-whiskey
    373 wayback-machine-privacy-and-old-plip-com
    361 how-i-make-coffee
    330 our-pet-venus-fly-trap

This may make them all the more popular, but here’s the links for them:

1026 free-idea-abstracted-facebook-nonymizer
    959 adendum-to-ashleys-law-problematic-imac-vesa-mounts-and-new-desks
    805 toss-your-salad-code
    693 thoughts-on-very-large-monitors
    650 photos-food-bikes-sunsets-and-stars
    635 sunset-and-rainbows
    385 oakland-sf-photos-coffee-and-scotch-whiskey
    373 wayback-machine-privacy-and-old-plip-com
    361 how-i-make-coffee
    330 our-pet-venus-fly-trap

A few updates on these post’s topics:

  • A lot of the images are broken on older posts. I think this has to do with going all HTTPS and as well some munging of the markup to rendered HTML.  I’ll try and fix these, but sorry!
  • I’ve been using an AeroPress to make my coffee of late (though the Chemex’s siren song is wooing me)
  • Lastly, I’m proud to see my top 3 posts are what they are ;)

SANOG 27

3 minutes, 17 seconds

IMG_0240

I was lucky enough that my work sent me to both visit our office and team of 4 in Nepal, and as well to attend SANOG 27. After submitting my talk on TLS using Let’s Encrypt, I was humbled to have been selected to present as well.

Tourism

Boudhanath

Boudhanath

This is my first trip to Nepal and the place is amazing. I’m sad to report that on top of the thousands killed and way more displaced, amazing, ancient sites were heavily damaged in the 2015 earthquake. I visited both Kathmandu Durbar Square and Boudhanath both of which suffered devastating damage.

Nyatapola

Thanks to my awesome co-workers, they took me on a motorbike trip to Nagarkot which gave me a chance to get away from the hotel and see something more than nearby Thamel. Kathmandu Valley countryside is beautiful. Though the smog prevented epic views of the Himalayas, staying at the mount side hotel and waking early to see the sunrise was splendid.

On our return ride to Kathmandu, we stopped at Bhaktapur Durbar Square, which suffered much less damage from the quake. The Nyatapola Temple is stunning.

 

Conference

SANOG 27

Conference

This is my first time attending a Network Operators Group (aka “NOG”) meeting. The bar for future NOGs has been set very high given how amazing SANOG 27 was. The conference, though attaining an all time high over 300 attendees, felt quite small. Given that I was a speaker and staying at the hotel where the conference was, I was also treated almost every night with a dinner paid for by a local sponsor (or not so local in Huawei’s case). There were a couple very sales-centric talks (which is frowned upon), otherwise most of the talks were impressive and informative. I attended them all as there was only one track with all attendees present.

YouTube

You can see my slides or you can re-watch the live streamed YouTube recording. I was humbled to receive many compliments on my talk, but I feel I have room for improvement!

Special thanks to Fakrul Alam for letting me do a brief Let’s Encrypt demonstration during the Network Security workshop (and also for doing a quick key signing!).

SANOG Workshop

Workshop

Anyone looking to up their game should seriously consider attending a regional NOG. For example, SANOG 27 was $310 all in: Conference, Tutorial and Workshops with Lunch for all 8 days. Considering flights from the US are well under $1,000, this is quite cheap compared to, say, Black Hat. At over $2,000 for Black Hat, if you took this money to SANOG 27 instead, you would be able to pay for airfare, the full Monty of conference, workshops and tutorials, your hotel AND get a trip to Kathmandu included! I can not underscore enough the level of expertise I saw in the presenters and teachers here. They’re simply amazing.

Favorites

Pottery Square @ Bhaktapur

Pottery Square @ Bhaktapur

Between both the tourism and conference, here’s my favorites:

Thanks

NPIX Install

NPIX Install

Thanks to PCH for sending me!

Thanks very much to SANOG 27, Aftab Siddiqui, Gaurab Raj Upadhaya and GZ Kabir for accepting my proposal to speak!

Most of all my thanks to the totally awesome PCH staff who live in the Kathmandu valley. Thank you thank you thank you to Kabindra Shrestha, Chatur Ratna Shakya, Rustan Shrestha, and Dibya Khatiwada. They extended an amazing amount of hospitality and made me feel very welcome at both SANOG and their homeland at large. The best gift a foreigner could ask for is the welcome and help that I received from these four. You are all amazing!

Detecting IPv6 with JS and PHP

0 minutes, 36 seconds

A friend of mine wanted a way to know if visitors to her site were coming from an IPv6 address. To do this, you would run some PHP to output a JS variable with global scope:

// thanks to:
// https://plugins.svn.wordpress.org/ipv6detector
$ip =  $_SERVER['REMOTE_ADDR'];
if (substr_count($ip, ":") > 0 && substr_count($ip, ".") == 0) {
        $js = 'var is6 = true';
} else {
        $js = 'var is6 = false';
}
print "<script>$js</script>";

And then to give the user a sense of excitement, animate showing them if they are v6 or v4. You’ll need jQuery for this, of course:

<div id="ipwhich" style="font-size:2em">?</div>
<script>
var showMe = 'v4!';
if(is6){
        var showMe = 'v6!';     
}
$("#ipwhich").delay(1000).hide().delay(1000).html(showMe).fadeIn();
</script>
</div>

I did up a live demo: give it a try!

66.3 bits of entropy and grackles

0 minutes, 25 seconds

No relation to each other, aside from happening on the same day: My daughter gave me a note with a password on it:

20151223_160535

I was then to speak the password into the “walkie talkie” (mouse) when I needed help. I liked this because:

  • She even knew what a password was at all
  • She decided that her service needed a password
  • She used a pretty decent password, 66.3 bits of entropy!

Later that night, at sunset, just like the for the past 2 months, the grackles came to roost in our palm trees. Lovely!

https://vimeo.com/150132661

Let’s Encrypt TLS & A+ on SSL Labs

1 minute, 23 seconds

As you may recall, around here we like our TLS, and we like it rare!  I have no idea what “rare” means in the context of web encryption, but you know, that’s the way we like it!  We also like it cheap!  Even better than cheap (free) is open (libre)!  So, put it all together, and you get Let’s Encrypt TLS certs on plip.com and related properties.  Yay!

I saw the EFF/Let’s Encrypt crew speak at at DEF CON this past summer,  and it was super inspiring.  I’d already heard about the project, but it was that much more exciting to hear their talk.  I was dead set on cutting over from my old CA, to the new, definitively open, one.

While I was in my server, I went and visited my fave site for secure web server configs, cipherli.st. The only option I can’t run is SSLSessionTickets, as it requires Apache 2.4.11 or later. We’ll get there later with an Apache upgrade.

Put this all together?  You get a big fat A+ on SSL Labs.  w00t!

Some caveats are that Let’s Encrypt is just in public beta, so there’s some still some kinks to work out. For example, I mistakenly tried to get a cert for a wildcard vhost (eg “*.foo.bar.com”).  I got back an error:

An unexpected error occurred:
The request message was malformed :: Error creating new authz :: Invalid character in DNS name
Please see the logfiles in /var/log/letsencrypt for more details.

So, while I shouldn’t have selected the domain to get a cert (Let’s Encrypt doesn’t do wildcard certs), the error was a bit cryptic.  Fortunately the fine folks in their IRC channel (web IRC client – #letsencrypt @ Freenode to DIY) quickly pointed out to not use wildcards and that there was issue 1683 already open.

Awesome sauce, thanks Let’s Encrypt!

6 Vegas Sunsets, 1 ride

0 minutes, 9 seconds

And one Bay Area one while flying out of OAK ;)

#6 was taken on this evenings quickie MTB ride to my fave local spot.  Here’s what it looked like when you turned the camera around:

20151110_161140

1 year running a Tor middle relay

0 minutes, 22 seconds

pliptorYay!  I’m proud to announce that plip has been running a Tor middle relay for exactly a year. I believe in on-line privacy and anonymity and when I had some time after leaving my last gig, I spent quite a bit of time learning about Tor.  Though I aspired to run an exit relay, the potential hassle was too much. Instead, I was happy to learn about The Amnesic Incognito Live System (aka Tails) and set up a middle relay.  Here’s to another year!