Cody Pendant has asked for the wisdom of the Perl Monks concerning the following question:

I've written my own blogging software in Perl, and I'm kind of quietly proud of it.

It has features like comments, file uploads and a search engine as well as posting and editing and HTML shortcuts in QuickEdit format.

I couldn't have done it without the Monks. I'm not worthy.

Anyway, a friend of mine asked if she could have the script, and it was at that moment I realised that it was still full of hacks and had no interface.

For instance, there's a create button but no delete button for posts. I delete so rarely I just log in and delete it with FTP.

But more importantly, if I was going to let other people who aren't just more perl hackers use it, I'd need to do a couple of things:

  1. Make it easy for people to install.
  2. Build an interface for preferences, like date formats, posts-per-page and so on, that I currently just change by editing Perl variables.
  3. Store those preferences somewhere so that a user can just check a box and save.
  4. Worry about things like Taint, which I don't fully understand.

So, I throw myself on the mercy of the Monks.

What's the best way to store the prefs for an online application? I'm guessing you guys would use a tied hash of some sort?

What things do I have to worry about when it comes to security?

All my script does, in terms of security, is write posts into a certain folder which is world-writable, then read them back again. There's one script for display and another for create/edit, which is in a password-protected directory.

And how does one go about setting up an install for something like this to make it as easy as possible? The very easiest thing would be for them to install and set permissions on one script, which would then figure out things like the path and create the necessary folders and so on.

I'd be grateful for any help. I'm really just asking the questions because, I'll happily admit, I've never tried to do CGI programs professionally at all, just hacked them together and made them work.
--

($_='jjjuuusssttt annootthhrer pppeeerrrlll haaaccckkeer')=~y/a-z//s;print;

Replies are listed 'Best First'.
Re: Moving A Web Application From Hacky To ... Less Hacky
by BrentDax (Hermit) on Mar 01, 2002 at 10:27 UTC
    First of all, well done so far. You're nine-tenths of the way to being finished. Now you just have to make it pretty.

    I'm gonna give you a crash course in tainting. Tainting is activated by the -T switch. What it basically does is it marks all data from outside your program as tainted:

    my $from_outside=<STDIN>; my $from_inside='Hey!'; tainted($from_outside); #true tainted($from_inside); #false tainted($ENV{PATH}); #true
    If one variable in an expression is tainted, the result of that whole expression is too:
    tainted(($untainted1+$untainted2).$tainted); #true tainted(($untainted1+$untainted2).$untainted3); #false
    Taintedness is for individual pieces of scalar data--some elements of an array or hash can be tainted without tainting the whole thing.

    So, what does this taintedness do? Here's your answer:

    open(FH, $tainted); system($tainted); `$tainted`; eval($tainted); exec($tainted);
    All of those blow up. There are other functions that blow up too, but those are the biggies.

    Tainting helps you keep track of what you're doing with data from the outside. The idea is that you don't trust anything that came from outside your program--at least not when it affects something else outside your program. After all, if $tainted were 'rm -rf /', three of those examples would have deleted all your files.

    Of course, data from outside is sometimes trustable. Taint just makes you check that it's trustable. How? Well, the only way to remove a variable's taintedness is to get it out of a regular expression group.

    For example, let's say I'm going to get part of a file name from the outside. I know that the only characters that should be in that part of the file name are \w characters, so I can do this:

    if($file_name=~/^(\w+)$/) { $file_name=$1; #no longer tainted } open(FH, "> users/grades/$file_name") or die "Can't open grades for $f +ile_name: $!";
    If $file_name matches the pattern, it gets untainted, but if it doesn't, it stays tainted. If it stays tainted, you'll get a fatal error like this: Insecure dependency in open while running with -T switch at script.pl line 4. There are two very important things to remember about taint checking.
    1. It is not a silver bullet. There are still many other things that the user could do which taint checking wouldn't catch. In that example, the untainted variable could refer to a file that doesn't exist, blowing your script out of the water.
    2. When writing your validation patterns, always specify what you want, never what you don't. For example, it's probably impossible to come up with a complete regular expression to filter all dangerous things out of shell command. However, if you know that they should only be using one of ten utilities, it's easy to check that they're using only those utilities.
    This is just a quick overview. The perlsec manpage in your Perl distribution has a large section on tainting.

    =cut
    --Brent Dax
    There is no sig.

Re: Moving A Web Application From Hacky To ... Less Hacky
by theguvnor (Chaplain) on Mar 01, 2002 at 02:22 UTC
    Congrats Cody on what you've already achieved.

    Other monks can weigh in with their opinions here, but I don't like the idea of a world-writeable directory for anything... I'd prefer to have the file/directory permissions set to 0644 so it is writeable only by your userid, and run the script with the suid bit set so that it runs as your usergrp rather than the webserver's.. and then make damned sure to read up on tainting (because suid scripts automatically invoke taint checks)!!!

    ..Guv

    Update see here for more CGI security info.

    Update II I saw this article by Ovid referenced by another monk in some node (sorry, don't remember what node / who linked it). Cheers!

      I believe my hosting service recently changed their setup so that scripts are all run with the user's ID rather than the webserver/nobody ID, so that's good, right? I guess I can check by setting that folder to 644 and trying to create a post...

      Doesn't solve the problem for anyone else though. Thanks for the info.

      Anyone care to boil security and taint issues down to "if you're doing any of the following things, you need to worry..." kind of thing?
      --

      ($_='jjjuuusssttt annootthhrer pppeeerrrlll haaaccckkeer')=~y/a-z//s;print;
Re: Moving A Web Application From Hacky To ... Less Hacky
by beebware (Pilgrim) on Mar 01, 2002 at 14:46 UTC
    Ok, security things to consider:
    • Use taint #!usr/bin/perl -t should do it. But always think one step further (Think beyond Taint and warnings)
    • Use strict;
    • Don't use cgi::carp 'fatalstobrowser'; - in production code as it may show things you don't want people to see.
    • Double-check all user entered data and then don't trust it. Do you really need to let them enter a filename? (taint will catch most mistakes, but it's no infalliable).
    • Hardcode important settings. Using stuff like <input type="hidden" name="destination" value="test@example.com"> is just asking for people to send spam through your system - with YOUR name on it!
    • Make sure the script, file and folder it is in has minimum priviledges to do its job. For the sake of your server, never ever ever run a script as root: nor allow it to be. Also, if you are writing files, ensure that they can't be executed or anything. Last thing you want is for someone to upload a file with the contents rm -rf * and then visit the URL of the file
    • The end user has no need to know how or where your files are stored (it's a possible security loophole): having URLs like ?readfile=/usr/myuser/htdocs/files/0102.txt allows them to know where your files are. I'm saying nothing about them being able to alter the query string and read any file they want on your server... (something like ?readfile=0102 would be a lot more secure).
    • use cgi - it's tried, tested and quite secure. Rolling your own form parsing system may allow buglets to slip by... (use CGI or die;)
    • Read things like Essential CGI Security Practices and perlsec.
    • Be careful what error messages you do display to the user. Bad password will allow them to know that they've hit a correct username, Bad username or password doesn't allow them to know quite what went wrong. Lock out (or introduce a time delay) on accounts that have more than X invalid login attempts in a time period.
    There's a lot of work you can do to make sure a system is secure (physical access, open telnet ports, default passwords etc etc are all potential problems and security holes), but the above should give you some assistance.