I’m increasingly of the opinion, as you might have guessed from reading past articles on my blog, that if you can encrypt a thing, you must encrypt a thing, if it’s sent over the internet especially  after adding Google Analytics to WordPress.

So, since more crypto use is always a good thing, I wanted to find a way to encrypt email sent from my WordPress blog. Specifically, I wanted to encrypt my “hire me” contact form, which is emailed to me and quite often contains sensitive information. Sometimes clients are quite forthcoming in their initial messages, so I think it’s professional to protect that.

Although the contact form is the primary use case, this code should work for any email (with only one recipient) sent via wordpress’ internal code, providing the address has a valid (non expired encrypting) public key on file. Adding a key is, in this code, a manual process, however it’d be trivial to extend the code to chat to a key server.

So, anyway, you need to find the functions.php for your theme (I wanted to do this quickly, so I didn’t write a plugin), and put in the following code.

You’ll also need to install the gnupg extension for php. If you’re on debian, this should just be a matter of apt-get install php5-gnupg.

/**
Recursively find a non-expired encryption key for a given address.
*/
function find_encryption_key($keys) {

    $fingerprint = null;
    foreach ($keys as $k) {
        
        if ((!$k['expired'])  && ($k['can_encrypt']) && (!$fingerprint) && (isset($k['fingerprint']))) {
            $fingerprint = $k['fingerprint'];
        }
        
        if (!$fingerprint && isset($k['subkeys'])) {
            $fingerprint = find_encryption_key($k['subkeys']);
        }
    }
    
    return $fingerprint;
}

/**
Encrypt $message for delivery to $address
*/
function encryptto($message, $address) {
    
    $gpg = new gnupg();
    
    if (is_array($address)) {
        $address = $address[0];
    }
    
    // Find keys
    $keys = $gpg->keyinfo($address);
    if ($keys) {
        $fingerprint = find_encryption_key($keys);

        $gpg->addencryptkey($fingerprint);
        
        return $gpg->encrypt($message);
    }
    
    return false;
}

// Attempt to send encrypted email.
add_filter( 'wp_mail', function ($args) {
    $new_wp_mail = array(
        'to'          => $args['to'],
        'subject'     => $args['subject'],
        'message'     => $args['message'],
        'headers'     => $args['headers'],
        'attachments' => $args['attachments'],
    );
    
    if ($encrypt = encryptto($args['message'], $args['to'])) {
        $new_wp_mail['message'] = $encrypt;
    }
    
    return $new_wp_mail;
}, 1);

This code will try and find a key for the to address and attempt PGP encryption.

It’s not perfect, for example, if encryption fails for whatever reason, the message will be sent in the clear. I did it this way since not everyone’s public key will be on file, but I still wanted the email sent, so this is probably a good thing.

Also, for jetpack contact forms & comments at least, the code will fire the clear message text to Akismet, if you have the plugin installed. The latest version of Akismet will default to sending the message over TLS, so this isn’t the end of the world if you’re worried about passive monitoring.

Anyway, the more encrypted traffic on the net the better. Have fun!

Every page on a Known site can potentially be an API endpoint, which means that pretty much everything you can do with the interface, you can write a script or client to connect to.

As things are still being built out there are no libraries, or indeed detailed instructions, out there yet (although you might want to look at some of my sample code) so I’ve been fielding a bunch of questions from folk.

Overview

At its simplest level, all pages on a Known site are API endpoints.

When you post a status update, the form you fill in POSTs the form fields to the API endpoint /status/edit. So, if you were to write a client to do this, you’d send the same variables to the same endpoint, but you’ll also have to do a couple of extra things as well.

Authentication

In order to access restricted content, or to post to your stream, you will need to authenticate yourself. There is currently only one built in way to do this, however Known supports extensible authentication methods, so it is of course possible to hook in other methods.

For example, if you don’t want to use the built signed HTTP request method, you could use an OAuth2 server.

  • Get your API Key: from your Tools and Apps page.
  • Generate a signature: You need to generate a base64 encoded HMAC, which is a SHA256 digest of the path of the api (i.e. if you’re using “`https://yoursite.com/status/edit“`, your path should be “`/status/edit“`) using your API key as the key.

    From the shell:

    HMAC=$(echo -n "/status/edit" | openssl dgst -binary -sha256 -hmac '***APIKEY***' |  base64 -w0)

    In PHP:

    base64_encode(hash_hmac('sha256', "/status/edit", $api_key', true));
  • Sign your HTTP requests by sending the following HTTP headers:
    X-KNOWN-USERNAME: 
    X-KNOWN-SIGNATURE: 
  • Tell Known you want the API to return a machine understandable response by sending the following header:
    Accept: application/json

Some points to remember…

  • Remember to follow forwards: Known makes use of forwards, for example when creating a post, you’ll be forwarded to the completed object. So, you need to tell cURL (if that’s what you’re using) to follow forwards CURLOPT_FOLLOWLOCATION
  • Create a cookie jar: In order to preserve your session, and to avoid getting 403 errors after a successful post, you’ll need to store your cookies. Again, if you’re using cURL you can do this by passing the CURLOPT_COOKIEJAR option.

Hope this helps!

I have previously mentioned my two factor authentication plugin for Known. Using this plugin, you are able to grant your Known users an extra level of security on their account, allowing them to enter a secondary authentication code (usually from an authentication application like Google Authenticator on their phone).

This is a very powerful way of preventing an attacker from accessing your account by brute forcing your password (and combined with a syslog plugin + fail2ban combo makes breaking in the front door even harder).

To make it easy to register your application for two factor auth, the plugin generates a QR code that you can scan. Originally I used a Google API to generate this (connecting over TLS), which presented a problem that you had to expose your access code to a US company. For security, it’s probably better that the QR is served locally if it’s safe to do so.

Therefore, I made a small modification to the code to incorporate (a slightly modified / bugfixed) version of Terence Eden’s PHP QR Code generator.

Now, if you visit your two factor settings page over TLS, the QR code that you get will be served from your local server. If you’re site is hosted on a non-secure server (a really really bad idea, but sometimes unavoidable) it’ll fall back to serving the qr code using a secure connection to googles servers, by way of a least worst option.

Have a play!

» Visit the project on Github...