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!

8 thoughts on “Extending wordpress to send PGP emails

  1. Cool. Part of me recoils at the idea of entrusting a WordPress environment to perform encryption such as this, even if private keys are properly protected. On the other hand, if you’re the only recipient, you can afford to make throw-away keys and as you point out, if you’re going to do email you might as well PGP it.

  2. Because we use the GnuPG extension (which is basically a PHP wrapper for the gnupg executable), you need to store the public key in your web server user’s keyring. This isn’t ideal, but it’s what we’ve got :/

    I found the easiest way to store your public key is to SU into your web server user and import directly:

    sudo su www-data;
    gpg --import /path/to/pubkey.pgp
    

    The default location for the keyring unfortunately is generally publicly readable, so be sure you block access using .htaccess or similar.

  3. So, following on from the theme of other week’s post, this is a very quick plugin which will opportunistically encrypt email sent by Known.
    It works in much the same way as the similar WordPress code; if a key for a user is in the keyring, the email is encrypted before it is sent. It is particularly handy when combined with my PGP Signin code, since that will provide key discovery.
    I wrote this for my own use, so it’s not perfect. For example, since Known sends all email as HTML (unless my plain text email patch is also applied this patch was merged into core), my plugin currently just strips tags, which at least makes the email somewhat readable.
    Anyway, kick it around.
    » Visit the project on Github…



    Thanks for visiting! If you’re new here you might like to read a bit about me.
    (Psst… I am also available to hire! Find out more…)


    Follow @mapkyca
    !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?’http’:’https’;if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+’://platform.twitter.com/widgets.js’;fjs.parentNode.insertBefore(js,fjs);}}(document, ‘script’, ‘twitter-wjs’);


    Share this:EmailLinkedInTwitterGoogleFacebookReddit

Leave a Reply