{"id":950,"date":"2015-03-09T19:25:40","date_gmt":"2015-03-10T02:25:40","guid":{"rendered":"https:\/\/galencharlton.com\/blog\/?p=950"},"modified":"2015-03-17T06:56:01","modified_gmt":"2015-03-17T13:56:01","slug":"notes-on-making-my-wordpress-blog-https-only","status":"publish","type":"post","link":"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/","title":{"rendered":"Notes on making my WordPress blog HTTPS-only"},"content":{"rendered":"<p>The other day I made this blog, <a href=\"https:\/\/galencharlton.com\/blog\/\">galencharlton.com\/blog\/<\/a>, HTTPS-only. \u00a0In other words, if Eve\u00a0want to sniff what Bob is reading on my blog,\u00a0she&#8217;ll need to do more than just capture packets between my blog and Bob&#8217;s computer to do so.<\/p>\n<p>This is not bulletproof: perhaps Eve is in possession of truly spectacular computing capabilities or a breakthrough in cryptography and can break the ciphers. Perhaps she works for any of the sites that host external images, fonts, or analytics for my blog and has access to their server logs containing referrer headers information. \u00a0Currently these sites are Flickr (images), Gravatar (more images), Google (fonts) or WordPress (site stats \u2013 I will be changing this soon, however). Or perhaps she&#8217;s installed a keylogger on Bob&#8217;s computer, in which case anything I do to protect Bob is moot.<\/p>\n<p>Or perhaps\u00a0<strong>I<\/strong> am Eve and I&#8217;ve set up a dastardly plan to entrap people by recording when they read\u00a0about MARC records, then showing up at Linked Data conferences and disclosing that activity. \u00a0Or vice versa.\u00a0<em>(Note: I will not actually do this.)<\/em><\/p>\n<p>So, yes \u2013 protecting the privacy of one&#8217;s website visitors is hard; often the best we can do is be better at it than we were yesterday.<\/p>\n<p>To that end, here are some notes on how I made my blog require HTTPS.<\/p>\n<h2>Certificates<\/h2>\n<p>I got my SSL certificate from Gandi.net. Why them? \u00a0Their price was OK, I already register my domains through them, and I like their corporate philosophy: they support a number of free and open source software projects; they&#8217;re not annoying about up-selling, and they have never (to my knowledge) run sexist advertising, unlikely some of their larger and more well-known competitors. But there are, of course, plenty of options for getting SSL certificates, and once <a href=\"https:\/\/letsencrypt.org\/\">Let&#8217;s Encrypt<\/a>\u00a0is in production, it should\u00a0be both cheaper and easier for me to replace the certs next year.<\/p>\n<p>I have three subdomains of galencharlton.com that I wanted a certificate for, so I decided to get a multi-domain certificate. \u00a0I consulted\u00a0this <a href=\"https:\/\/rtcamp.com\/wordpress-nginx\/tutorials\/ssl\/multidomain-ssl-subject-alternative-names\/\">tutorial<\/a> by rtCamp to generate the CSR.<\/p>\n<p>After following the tutorial to create a modified version of <code>openssl.conf<\/code>\u00a0specifying the\u00a0subjectAltName values I needed, I generated a new private key and a certificate-signing request as follows:<\/p>\n<pre class=\"lang:sh decode:true\" title=\"Making a CSR\">openssl req -new -key galencharlton.com.key \\\r\n  -out galencharlton.com.csr \\\r\n  -config galencharlton.com.cnf \\\r\n  -sha256<\/pre>\n<p>The <code>openssl<\/code> command asked me a few questions; the most important of which being the value to set the common name (CN) field; I used &#8220;galencharlton.com&#8221; for that, as that&#8217;s the primary domain that the certificate protects.<\/p>\n<p>I then entered the text of the CSR into a form and paid the cost of the certificate. \u00a0Since I am a library techie, not a bank, I purchased a domain-validated certificate. \u00a0That means that all I had to prove to the certificate&#8217;s issuer that I had control of the three domains that the cert should cover. \u00a0That validation could have been done via email to an address at galencharlton.com or by inserting a special TXT field to the DNS zone file for galencharlton.com. I\u00a0ended up choosing to go the route of placing a file on the web server whose contents and location were specified by the issuer; once they (or rather, their software) downloaded the test files, they had some assurance that I had control of the domain.<\/p>\n<p>In due course, I got the certificate. \u00a0I put it and the intermediate cert specified by Gandi in the <code>\/etc\/ssl\/certs<\/code> directory on my server and the private key in <code>\/etc\/private\/<\/code>.<\/p>\n<h2>Operating System and Apache configuration<\/h2>\n<p>Various vulnerabilities in the OpenSSL library or in HTTPS itself have been identified and mitigated over the years: suffice\u00a0it to say that it is a BEASTly CRIME to make a POODLE suffer a HeartBleed &#8212; or something like that.<\/p>\n<p>To avoid the known problems, I wanted to ensure that I had a recent enough version of OpenSSL on the web server and had configured Apache to disable insecure protocols (e.g., SSLv3) and eschew bad ciphers.<\/p>\n<p>The server in question is running Debian Squeeze LTS, but since OpenSSL 1.0.x is not currently packaged for that release, I ended up adding Wheezy to the APT repositories list and upgrading the openssl and apache2 packages.<\/p>\n<p>For the latter, after some Googling I ended up adapting the recommended Apache SSL virtualhost configuration from this <a href=\"https:\/\/testbit.eu\/apache-sslciphersuite-without-poodle\/\">blog post<\/a> by Tim Janik. \u00a0Here&#8217;s what I ended up with:<\/p>\n<pre class=\"lang:apache decode:true \" title=\"SSL virtualhost config\">&lt;VirtualHost _default_:443&gt;\r\n    ServerAdmin gmc@galencharlton.com\r\n    DocumentRoot \/var\/www\/galencharlton.com\r\n    ServerName galencharlton.com\r\n    ServerAlias www.galencharlton.com\r\n\r\n    SSLEngine on\r\n    SSLCertificateFile \/etc\/ssl\/certs\/galencharlton.com.crt\r\n    SSLCertificateChainFile \/etc\/ssl\/certs\/GandiStandardSSLCA2.pem\r\n    SSLCertificateKeyFile \/etc\/ssl\/private\/galencharlton.com.key\r\n    Header add Strict-Transport-Security \"max-age=15552000\"\r\n\r\n    # No POODLE\r\n    SSLProtocol all -SSLv2 -SSLv3 +TLSv1.1 +TLSv1.2\r\n    SSLHonorCipherOrder on\r\n    SSLCipherSuite \"EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+\r\naRSA+SHA384 EECDH+aRSA+SHA256 EECDH+AESGCM EECDH EDH+AESGCM EDH+aRSA HIGH !MEDIUM !LOW !aNULL !eNULL\r\n!LOW !RC4 !MD5 !EXP !PSK !SRP !DSS\"\r\n\r\n&lt;\/VirtualHost&gt;<\/pre>\n<p>I also wanted to make sure that folks coming in via old HTTP links would get permanently redirected to the HTTPS site:<\/p>\n<pre class=\"lang:apache decode:true\" title=\"HTTP to HTTPS redirects\">&lt;VirtualHost *:80&gt;\r\n    ServerName galencharlton.com\r\n    Redirect 301 \/ https:\/\/galencharlton.com\/\r\n&lt;\/VirtualHost&gt;\r\n\r\n&lt;VirtualHost *:80&gt;\r\n    ServerName www.galencharlton.com\r\n    Redirect 301 \/ https:\/\/www.galencharlton.com\/\r\n&lt;\/VirtualHost&gt;<\/pre>\n<h2>Checking my work<\/h2>\n<p>I&#8217;m a big fan of the Qualsys SSL Labs <a href=\"https:\/\/www.ssllabs.com\/ssltest\/\">server test<\/a>\u00a0tool, which does a number of things to test how well a given website implements HTTPS:<\/p>\n<ul>\n<li>Identifying issues with the certificate chain<\/li>\n<li>Whether it supports vulnerable protocol versions such as SSLv3<\/li>\n<li>Whether it supports \u2013 and request \u2013 use of sufficiently strong ciphers.<\/li>\n<li>Whether it is vulnerable to common attacks.<\/li>\n<\/ul>\n<p>Suffice it to say that I required a couple iterations to get the Apache configuration just right.<\/p>\n<h2>WordPress<\/h2>\n<p>To be fully protected, all of the content embedded on a web page served via HTTPS must also be served via HTTPS. \u00a0In other words, this means that image URLs should require HTTPS \u2013 and the redirects in the Apache config <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=418354\">are not enough<\/a>. \u00a0Here is the sledgehammer I used to update image links in the blog posts:<\/p>\n<pre class=\"wrap:true lang:mysql decode:true\" title=\"Update links\">create table bkp_posts as select * from wp_posts;\r\n\r\nbegin;\r\nupdate wp_posts set post_content = replace(post_content, 'http:\/\/galen', 'https:\/\/galen') where post_content like '%http:\/\/galen%';\r\ncommit;<\/pre>\n<p>Whee!<\/p>\n<p>I also needed to tweak a couple plugins to use HTTPS rather than HTTP to embed their icons or fetch JavaScript.<\/p>\n<h2>Finishing touches<\/h2>\n<p>In the course of testing, I discovered a couple more things to tweak:<\/p>\n<ul>\n<li>The web sever had been using Apache&#8217;s mod_php5filter \u2013 I no longer remember why \u2013 and that was causing some issues when attempting to load the WordPress dashboard. \u00a0Switching to mod_php5 resolved that.<\/li>\n<li>My domain ownership proof on keybase.io failed after the switch to HTTPS. \u00a0I eventually tracked that down to the fact that keybase.io doesn&#8217;t have a bunch of intermediate certificates in its certificate store that many browsers do. I resolved this by adding a <a href=\"http:\/\/wiki.gandi.net\/en\/ssl\/intermediate\">cross-signed intermediate certificate<\/a> to the file referenced by SSLCertificateChainFile in the Apache config above.<\/li>\n<\/ul>\n<p>My blog now has an A+ score from SSL Labs. Yay! \u00a0Of course, it&#8217;s important to remember that this is not a static state of affairs \u2013 another big OpenSSL or HTTPS protocol vulnerability could turn that grade to an F. \u00a0In other words, it&#8217;s a good idea to test one&#8217;s website periodically.<\/p>\n<div class=\"sharedaddy sd-sharing-enabled\"><div class=\"robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing\"><h3 class=\"sd-title\">Share this:<\/h3><div class=\"sd-content\"><ul><li class=\"share-twitter\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-twitter-950\" class=\"share-twitter sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/?share=twitter\" target=\"_blank\" title=\"Click to share on Twitter\"><span>Twitter<\/span><\/a><\/li><li><a href=\"#\" class=\"sharing-anchor sd-button share-more\"><span>More<\/span><\/a><\/li><li class=\"share-end\"><\/li><\/ul><div class=\"sharing-hidden\"><div class=\"inner\" style=\"display: none;\"><ul><li class=\"share-tumblr\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-tumblr sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/?share=tumblr\" target=\"_blank\" title=\"Click to share on Tumblr\"><span>Tumblr<\/span><\/a><\/li><li class=\"share-reddit\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-reddit sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/?share=reddit\" target=\"_blank\" title=\"Click to share on Reddit\"><span>Reddit<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-print\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-print sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/\" target=\"_blank\" title=\"Click to print\"><span>Print<\/span><\/a><\/li><li class=\"share-end\"><\/li><\/ul><\/div><\/div><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>The other day I made this blog, galencharlton.com\/blog\/, HTTPS-only. \u00a0In other words, if Eve\u00a0want to sniff what Bob is reading on my blog,\u00a0she&#8217;ll need to&#8230;<\/p>\n<div class=\"sharedaddy sd-sharing-enabled\"><div class=\"robots-nocontent sd-block sd-social sd-social-icon-text sd-sharing\"><h3 class=\"sd-title\">Share this:<\/h3><div class=\"sd-content\"><ul><li class=\"share-twitter\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"sharing-twitter-950\" class=\"share-twitter sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/?share=twitter\" target=\"_blank\" title=\"Click to share on Twitter\"><span>Twitter<\/span><\/a><\/li><li><a href=\"#\" class=\"sharing-anchor sd-button share-more\"><span>More<\/span><\/a><\/li><li class=\"share-end\"><\/li><\/ul><div class=\"sharing-hidden\"><div class=\"inner\" style=\"display: none;\"><ul><li class=\"share-tumblr\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-tumblr sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/?share=tumblr\" target=\"_blank\" title=\"Click to share on Tumblr\"><span>Tumblr<\/span><\/a><\/li><li class=\"share-reddit\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-reddit sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/?share=reddit\" target=\"_blank\" title=\"Click to share on Reddit\"><span>Reddit<\/span><\/a><\/li><li class=\"share-end\"><\/li><li class=\"share-print\"><a rel=\"nofollow noopener noreferrer\" data-shared=\"\" class=\"share-print sd-button share-icon\" href=\"https:\/\/galencharlton.com\/blog\/2015\/03\/notes-on-making-my-wordpress-blog-https-only\/\" target=\"_blank\" title=\"Click to print\"><span>Print<\/span><\/a><\/li><li class=\"share-end\"><\/li><\/ul><\/div><\/div><\/div><\/div><\/div>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"Notes on making my WordPress blog HTTPS-only #code4lib","jetpack_is_tweetstorm":false},"categories":[4,6,17,55],"tags":[58,59],"jetpack_featured_media_url":"","jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p3gJ9y-fk","_links":{"self":[{"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/posts\/950"}],"collection":[{"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/comments?post=950"}],"version-history":[{"count":10,"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/posts\/950\/revisions"}],"predecessor-version":[{"id":978,"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/posts\/950\/revisions\/978"}],"wp:attachment":[{"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/media?parent=950"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/categories?post=950"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/galencharlton.com\/blog\/wp-json\/wp\/v2\/tags?post=950"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}