14.12.1 Problem
You want to
send encrypted email messages. For example, you take orders on your web site and
need to send an email to your factory with order details for processing. By
encrypting the email message, you prevent sensitive data such as credit card
numbers from passing over the network in the clear.
14.12.2 Solution
Encrypt the body of the email message with GNU Privacy Guard
(GPG) before sending it:
$message_body = escapeshellarg($message_body);
$gpg_path = '/usr/local/bin/gpg';
$sender = 'web@example.com';
$recipient = 'ordertaker@example.com';
$home_dir = '/home/web';
$user_env = 'web';
$cmd = "echo $msg | HOME=$home_dir USER=$user_env $gpg_path " .
'--quiet --no-secmem-warning --encrypt --sign --armor ' .
"--recipient $recipient --local-user $sender";
$message_body = `$cmd`;
mail($recipient,'Web Site Order',$message_body);
The email message can be decrypted by GPG, Pretty Good Privacy (PGP) or an
email client plug-in that supports either program.
14.12.3 Discussion
PGP is a popular public key encryption program; GPG is an
open-source program based on PGP. Because PGP in encumbered by a variety of
patent and control issues, it's often easier to use GPG.
The code in the Solution invokes /usr/local/bin/gpg to encrypt the message in
$message_body. It uses the private key belonging to $sender
and the public key belonging to $recipient. This means that only
$recipient can decrypt the email message and when she does, she knows
the message came from $sender.
Setting the HOME and USER environment
variables tells GPG where to look for its keyring: $HOME/.gnupg/secring.gpg. The --quiet and
--no-secmem-warning options suppress warnings
GPG would otherwise generate. The --encrypt and --sign options
tell GPG to both encrypt and sign the message. Encrypting the message obscures
it to anyone other than the recipient. Signing it adds information so that the
recipient knows who generated the message and when it was generated. The
--armor option produces plaintext output instead of binary, so the
encrypted message is suitable for emailing.
Normally, private keys are protected with a passphrase. If a
private key protected by a passphrase is copied by an attacker, the attacker
can't encrypt messages with the private key unless he also knows the passphrase.
GPG prompts for the passphrase when encrypting a message. In this recipe,
however, we don't want the private key of $sender to have a passphrase.
If it did, the web site couldn't send new-order email messages without a human
typing in the passphrase each time. Storing the passphrase in a file and
providing it to GPG each time you encrypt offers no additional security over not
having a passphrase in the first place.
The downside of using a key without a passphrase for encryption
is that an attacker who obtains the secret key can send fake order emails to
your order processor. This is a manageable risk. Since orders can be submitted
via a web site in the first place, there is already a place where false
information can be injected into the order process. Any procedures for catching
bad orders can also be triggered by these potential fake emails. Also, once the
key theft is discovered, and the problem that enabled the theft is fixed, the
attacker is easily disabled by switching to a new private key.