6.9. Recognizing Your Users
If your application is receiving data from your users via email, it's often necessary to tie incoming emails to a user account. When your system receives an email with an attached image, how does it know which account to associate the image with?
The naive approach is to have the users register their email addresses with your application. When you receive an email, you can look at the "from" address and see which account it matches.
This works fine until somebody sends an email with a faked sender to upload content into another user's account. This kind of identification offers little to no security. Most friends will know each other's email addresses, so an attack becomes easy. Any vaguely savvy user can configure the "from" address in their mail client and cause havoc.
As with any login system, we need one or more tokens that only the service and the user know, such as a username and password. A simple method might be to make users send a username and password in the subject line, but this isn't a great approach. One wrong keystroke and their login credentials are in the hands of another user. We could assign them a separate password for emailing, but this isn't terribly convenient for the user. Either we pick it and it's hard to remember, or the user picks it and he makes it the same as his main password.
If we have a particularly savvy user base, then we can use some form of signing to identify email senders. Integrating GnuPG with your application by shelling out to the GnuPG program for signature verification is a fairly trivial task. If you're going to be receiving a huge amount of email, the processing time may start to matter, but the chances are this will never become a problem. It's more likely that signing is a little beyond your users, or impossible in the case of most mobile devices.
We can get around the whole password issue by instead using the "to" address to uniquely identify users. At Flickr, we provide each user with an email address to send to of the form email@example.com. The something part is unique to every account, and independent of the user's login credentials. We generate this address using a random word and number generator, and so create easily memorable addresses such as firstname.lastname@example.org. If the user accidentally lets someone else know this address, then she can regenerate a new one, to keep it private.
By configuring your mail server to forward all mail for a domain to a single accountyou need only one /etc/aliases line to cover all possible email addresses. Your email handling script only needs to dig into the To and Cc headers to find out who the recipient should be.
BCC'd mails take a little bit of special handling. Bcc headers don't appear in mails, otherwise the recipients would know who was CC'd and defeat the purpose. In this case, your application needs to dig into the Received headers at the top of the mail to check the intended recipient. The delivery headers look something like this:
Received: from localhost (localhost [127.0.0.1]) by neuron.kaius.com (Postfix) with ESMTP id F09B0810036 for <email@example.com>; Fri, 30 Sep 2005 01:00:25 +0100 (BST) Received: from web54602.mail.yahoo.com (web54602.mail.yahoo.com [18.104.22.168]) by neuron.kaius.com (Postfix) with SMTP id 848E881000D for <firstname.lastname@example.org>; Fri, 30 Sep 2005 01:00:25 +0100 (BST) Received: (qmail 51433 invoked by uid 60001); 30 Sep 2005 00:00:25 -0000
We need to read these headers in reverse, as each step in the delivery process adds one to the top. A complex delivery process can add more than 10 of these headers to each mail, but the above example is fairly simplified.
The first header (at the bottom) was added by the local transport of the sender and doesn't tell us much. The second header was added when the mail was received by the destination server from the origin server. Here we can see that the message is intended for email@example.com. The third and final header was added by the destination server's rewriter, after it decided that email intended for firstname.lastname@example.org should be delivered to the local account email@example.com.
By reading the headers from the bottom upwards and storing the first for line, we can see who the mail was intended for, regardless of whether it was sent normally, CC'd, or BCC'd.
Once you have basic incoming email identification, you can provide extra services around it by appending different strings to the address to allow the user to perform different functions. In Flickr, we allow users to specify that the photos attached to the mail should be blogged by adding -blog to the local part (before the @) of the address.
This can be a lot easier on the eyes by using subdomain wildcards in addition to local part wildcards. The address for users to use can be of the format firstname.lastname@example.org. Domain wildcards are sometimes a little harder to set up but follow the same principle as local part wildcards, with rules to send all mail for all subdomains of a domain through to a single local account, which invokes the incoming mail handler.
By using the email address to identify the user, the subject and message bodies are freed up to be used for content and commands. This is especially useful when your users send mail from mobile devices. The address has to be entered into the phonebook only once, and each message sent needs to have only the content (subject and/or body) added by hand each time.