2008-10-31 22:50Make your own censored webmail!Censoring webmail is considered bad, especially when someone is censoring someone else’s email, and even more so when they are doing so without permission. I don’t know much about those sorts of situations, because I don’t rely on sites outside of my control to host my webmail (any more), but nor do I rely on myself to host my webmail, because the only software I’ve found is not designed for mobile phones or not in Debian yet. Failing to follow basic security practise, I exempt software which I have written myself from my stringent, Debian-based, quality control rules, and so, when I really needed a webmail service (or at least a subset of it), I was forced to write my own implementation. Fortunately the subset I required was very, very small, to such an extent that it is arguable whether what I’ve written is a webmail system at all. Basically it allows you to view on a webpage whether a person has sent you an email, and when they sent it, but no attacker could ever discover more information than this. By changing the source code you can restrict it to only consider emails based on certain criteria. In my particular case, this allows me to check if a certain special somehow has emailed me, which lets me look forward to the pleasure of reading their message when I get home. ArchitectureThe architecture of the system is quite simple, but consists of three parts. Firstly there is an Exim rule which causes all emails delivered to my user account to be processed by a BASH script. Then there is the BASH script itself, which is about 20 lines long, resides in my home directory, and conditionally calls wget. Part three is a PHP script which exists on a remote server and has two methods of operation: show status and update status, the latter of which is invoked by the wget. The status is actually stored in fourth file, but a database could equally be used for this purpose. The Exim ruleExim, like all old-fashioned MTAs, supports .forward files, which are (hidden) files a user can put in their home directory to control how email for their account is handled. Sadly one has to worry about creating infinite loops, as there is no feedback about the file you create until the email server explodes in a ball of spam fire, but once you get it working it is quite reliable. My .forward file, then, contains just the simple lines: \username
"| /home/username/filter-script.sh" or something similar. The only real important thing to notice here is the use of a full path, instead of using ~ or ., because you can’t be sure what those shortcuts will expand to. From this file, you can see what the next stage of the process is… The filter scriptDepending on your BASH skills, you can make this step as complicated as you like, but I’ll just give an example script which catches two different email addresses. #!/bin/bash FROM=`grep "^From: .*<.*>" | sed ’s/.*<\(.*\)>.*/\1/’` if [[ $FROM = "me@example.com" ]] then UNTRUSTED=TEST fi if [[ $FROM = "someone-else@example.com" ]] then UNTRUSTED=REAL fi if [[ $UNTRUSTED != "" ]] then wget -O /dev/null "http://yourdomain.tld/~hostingusername/censoredwebmail.php?action=update&untrustedString=$UNTRUSTED" fi There are a few things to point out about this script. Firstly, it genuinely acts as a filter, in the sense that the contents of the email is piped through it, which means the grep and sed together extract the From address out of the headers. This address (or any other header value you collect) can be tested against whatever conditions you want, and used to create a variable called “UNTRUSTED” here. It is called UNTRUSTED as a reminder that this is input to a PHP script, and like all user input it should be sanitised before further processing. The way it gets to the PHP script, though, is by the BASH script invoking wget to ostensibly download a file from a URL, but that URL is the address of the script, and the query parameters at the end of the URL are GET variables to the PHP script, effectively making a remote procedure call. The PHP scriptThis stage is more complicated again than the previous one, not least because it has two methods of operation. Obviously the script allows you to store information, and to view that information again, and it has to interact with a separate text file each time, which requires quite a bit of code. <?php function addLine($untrustedString) { if (!$untrustedString) return; $safeString = preg_replace("/[^A-Z]/", "", substr(strtoupper($untrustedString), 0, 10)); $handle = fopen("textfile.txt", "a"); $isoDate = date(‘Y-m-d’); $storeArray = array($isoDate,$safeString); fputcsv($handle, $storeArray); fclose($handle); } function getLines() { $i = 0; $outputArray = array(); $handle = fopen("textfile.txt", "r"); while (($data = fgetcsv($handle, 20, ",")) !== FALSE) { if (sizeof($data) == 1) continue; $outputArray[$i++] = $data; } fclose($handle); return $outputArray; } function head() { $head = <<<HEAD <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Censored Webmail</title> <style type="text/css">dt { font-weight: bold; }</style> </head> <body> <h1>Censored Webmail</h1> <dl> HEAD; echo $head; } function tail() { echo "\t</dl>\n</body>\n</html>"; } if ($_GET[‘action’] == ‘update’) { addLine($_GET[‘untrustedString’]); } elseif ($_GET[‘action’] == ‘show’) { head(); $linesArray = getLines(); foreach ($linesArray as $line) { list($date, $text) = $line; echo "\t\t<dt>$date</dt><dd>$text</dd>\n"; } tail(); } ?> The idea is to have functions for spitting out the HTML header and footer, a loop to turn an array of data into the HTML contents, and two functions for serialising and unserialising the data to and from a text file. As the text file is just a pair of comma-separated values on each line, the parsing isn’t particularly difficult, but the PHP functions fgetcsv and fputcsv are quite helpful here. It just remains to point out that the sanitising routine I use is to remove all characters that aren’t ASCII upper case letters, and to restrict to just 10 of them, which is perfectly expressive enough for my purposes. SecurityThese strings should be safe in just about any programming or markup language, you could even try enumerating them all, so I suppose the only danger is that if a malicious hacker found your script online, and managed to guess the names of your variables (because you wouldn’t be stupid enough to use the same variable and file names as the ones in a public blog post), they could keep making arbitrary GET requests and causing your script to write out the entire contents of a copyright work and get you in trouble for having it on your web hosting. At some point your hosting provider would also complain at the size of your text file too, as this script doesn’t have a cap on the number of writes it can perform. Similarly, an attacker might try to forge emails to you which cause the BASH script to make excessive requests to the PHP, but hopefully you or the attacker’s email service provider would spot the large number of emails being sent. If you wanted to upload a novel of 70 000 words, it would take 70 000 forged emails or PHP requests to upload just the words, one at a time, (assuming there weren’t any p10y long words in it) but how long would it take if you wanted to upload the punctuation and formatting tooQUESTNMARK Trackbacks
Trackback specific URI for this entry
No Trackbacks
|
QuicksearchCategoriesSyndicate This BlogBlog Administration |