|Anonymous email redirection a la Craigslist|
Would like opinions on best technical solution
| 12:38 pm on Sep 4, 2004 (gmt 0)|
I'd like to set up an email redirection system similar to that on Craigslist. For those who don't know, when you place an advert you can add your email address. However, rather than showing email@example.com, you are given a fowarding address similar to anon-9876521@[craigslist domain name]. People replying to your advert email the anon-9876521 address - emails are automatically forwarded to you.
Does anyone have an opinion on the best way of implementing this?
I've got qmail on my linux server (CL also uses qmail) - I was thinking of having a catchall mailbox for all incoming mail. Every minute or so a script would parse all the messages, get the TO addresses, do some DB lookups and forward the mail accordingly.
Can anyone see any problems with this approach? Any better solutions I don't know about?
Thanks in advance,
| 1:28 pm on Sep 4, 2004 (gmt 0)|
Have you looked at using address extensions (normally using the "+" character) which are probably set-up on your box already. If you have an account called "anon", any email to firstname.lastname@example.org will be delivered to "anon".
You then have all email to "anon" piped to a script that does the lookup and forwarding. You can easily pipe incoming email to a script using procmail or similar.
| 3:47 pm on Sep 4, 2004 (gmt 0)|
Whatever else you do, don't do a catch-all! You'll regret it within 6 months :-(
| 10:48 pm on Sep 4, 2004 (gmt 0)|
This is easy with qmail.
Mail to email@example.com is delivered to the address in the file ".qmail-123456" in user "anon"'s home directory.
So when the user does the relevant thing that requires a forwarder set up, use a CGI to parse out the user's real email address, and stick it into a file in "anon"'s home directory.
Picking the name of the file (and therefore email address) is slightly harder. There's a perl module that implements mktemp(), which will create a unique filename according to your constraints. Search CPAN for File::Temp.
Another way to pick the filename would be to set up an incrementer, but you want to avoid easily guessable forwarding addresses to reduce the risk of abuse.
| 9:01 am on Sep 6, 2004 (gmt 0)|
Excellent - thanks very much for the replies. It's given me a much better idea of how to tackle it.
| 2:07 am on Sep 15, 2004 (gmt 0)|
quesera is right, and if you aren't expecting an enormous number of messages then just dropping in the .qmail files should work.
Important caveat: qmail may not like it if those are readable by people other than the user ("anon" or whatever). I'm a bit rusty on the qmail permissions quirks but that could be an issue... check up on cr.yp.to or qmail.org or just test it in the shell. If it turns out to be a problem you can always use djb's other tools to set up a service that creates those as root and chown/chmod's them appropriately.
If you're expecting very large numbers of users then you may have performance trouble with all those .qmail files.
In that case there are various ways to deal with it. One would be to have qmail deliver everything to anon-* piped to a program that decoded the * part and forwarded the mail accordingly. The downside of that is running another process every time you get a mail, and possibly doing a database lookup (assuming here you're dealing with registered users). Having a cron job go through anon's Maildir/new would be safer but of course that implies a delay.
Another approach would be to just store off the forwarding addresses in some other directory tree (like /firstname.lastname@example.org for anon-123 for example, and have that writable by your web server). Then your mail-catcher script could be very light (just a shell script, no need for perl or a database).
Hope that helps, at least as food for thought.
| 2:14 am on Sep 15, 2004 (gmt 0)|
sorry, double-posted there.
| 2:52 am on Sep 15, 2004 (gmt 0)|
Permissions: that's a very good point.
It's not the readability that qmail cares about, it's the ownership and the writeability...
The .qmail files have to be owned by the user, and they can't be world-writeable. Group writeable, not sure.
One solution, again in perl: make the script setuid anon, and set the umask properly. The script will be run by the Apache user (httpd or www or apache etc, depending on your distribution/decision) but the files will be created owned by anon.
frostman's other ideas are good too. If you had an extraordinarily large number of forwarders, (several thousands), it would take qmail slightly longer to read the .qmail files, and a hashed directory tree or a database would be a good solution.
| 6:04 am on Sep 15, 2004 (gmt 0)|
Doh, the other thing you could do (if you were going for the fewer-users, multiple-.qmail-files approach) would be to have apache run as the user you want to use for this - ie, "anon" - or of course just give apache a Maildir and forward all anon's stuff there, that should work too. Check the docs on the qmail alias directory for the latter.
IMHO you don't want to get into letting apache setuid, that can be a real nightmare to secure.
| 3:40 pm on Sep 15, 2004 (gmt 0)|
Setuid anon, not setuid root!
A setuid anon perl script isn't a big security risk. Even if it's written very poorly, the worst that can happen is that someone can execute arbitrary commands as user "anon" -- which is *easier* to do if Apache is running as user "anon", which is why Apache should always be running as a user that owns no files (except logfiles).
Any time you're using a setuid script, you need to think about what you're doing, true. But in this case, the setuid perl script wouldn't need any extra security measures.
If you wanted to be as safe as possible, you could have the apache user run one cgi, which parsed the form input and sanity-checked it, making sure the email address looked like an email address and had no unusual characters. This address could be passed as an arg to the second perl script (this one setuid-anon), which would check the address again for special characters, create the file properly and set the contents, then return the number. (all NOT using the system() or backtick calls, etc). The original cgi would then generate the return page, including the number as part of the forwarder email address.
If done properly, the worst anyone could do is create lots of files in anon's homedir (with filenames that they cannot control) and contents that at least superficially resemble email addresses.
But you could do that from the web interface too, and the process exactly resembles the service he wants to provide, so you can't really protect against that.