Every now and then, I'm reminded off this little dilemma, as did a recent posting on the monastery: securing your scripts/passwords.

The use of databases in conjunction with Perl (or whatever language for this matter) to generate dynamic content websites increases, and usually you need an account (username/password) to connect to the database daemon.

As I explained in my reply to the before mentioned posting, the problem comes in when there are other users on a server (and a lot of us can't afford our own dedicated machine ... I think). Since Apache (or whatever http daemon) needs to be able to read the script and passwords, so can almost every other user on the system.

I once had an account on a webhosting company's machine and just used a few shell commands to grep through some files in the other users' "web directories". Shockingly (or not), I got close to all users' passwords to the databases, which -how bad- were identical to their shell accounts (please note: I had the company's OK on this little test). A few users with some clue disabled reading permissions in their "web directory" making the quest a little harder, but a less /path/to/specific/dir/index.cgi did wonders (you can read what files are included from there on).

How to prevent such horrible people (like myself) to snoop the passwords, or better yet, how to make the damage as little as possible? I've heard that RSBAC on Linux machines could possibly help, but how many people are going to persuade a company to implement this (and succeed)?

So far, I have thought of the following things that could shoo clueless scriptkiddies away:

• Read permissions of "webdir".
As mentioned above, turning these permissions off doesn't prevent other users from reading the files, but at least they can't grep through your files.
• Store passwords in a different file with "nobody" as only user to be able to read the file.
Again, this doesn't solve the problem, but users won't be able to snoop through the file from a command line and need to write a webinterface, possibly shying them away. In other words, security through obscurity (which usually doesn't work, as with this example ;).
• Using read-only database accounts only.
If the database is only used to feed the website, it's far from necessary to let that (database)user have write (or update) permissions. Have another user with write/update permissions and add files manually, not through a webinterface (or ask the password on the webinterface (SSL?)). This is probably not too hard to ask a sysadmin to do for you.

So far, I think the last option is the best (don't store passwords for updating accounts at all, but prompt for them), but I'm curious about other "solutions".

--
b10m

All code is usually tested, but rarely trusted.

Replies are listed 'Best First'.
Re: Securing your scripts on webhoster's server
by Ovid (Cardinal) on Jan 31, 2004 at 22:02 UTC

    Better yet, don't store passwords at all. Use something like Digest::MD5 to store a digest of the password and when the user enters a username and password, create a new digest of their password and compare that against what is in the database. It's not fullproof (nothing ever is), but at the very least, it's considerably more difficult for someone getting a copy of the password digests to determine what those passwords actually are.

    Also, you mentioned "security by obscurity". This has a tendency to induce a knee-jerk "if it's reasonably secure, you don't have to obscure it" reaction. However, if it's secure, obscurity can add to security by increasing the difficult of getting information useful for an attack. For example, if your code is secure, having use CGI::Carp 'fatalsToBrowser' in your code will theoretically not reduce the security. In reality, unanticipated bugs might trigger that and give information that might encourage the cracker. In other words, so long as true security is a top priority, obscurity is not such a bad thing.

    Also, though it's peripheral to your question, you might find the LiveJournal login rather interesting. They use JavaScript to create a digest out of the password, remove the plaintext password from the form and then they submit. They don't transmit the plaintext at all. This increases security over a non-encrypted request.

    Update: Oh, you meant database passwords. Sigh.

    Cheers,
    Ovid

    New address of my CGI Course.

Re: Securing your scripts on webhoster's server
by Zaxo (Archbishop) on Jan 31, 2004 at 22:10 UTC

    I have two techniques that I use together.

    I pick a host where suExec is in force. That permits me to make best use of unix filesystem permissions for security. Nothing needs to be world-readable. I have to be cautious about the security of my programs, but I'd do that anyway (wouldn't I?).

    User passwords don't need to be stored. Standard practice is to store a digest of the password, and compare the digest of the offered password to authenticate.

    After Compline,
    Zaxo

      I pick a host where suExec is in force. That permits me to make best use of unix filesystem permissions for security. Nothing needs to be world-readable.

      I have heard of this approach before, but always wonder: if your script uses suExec, why couldn't mine do the same to get to your files?

      Update: I got suEXEC and sudo confused :(

      User passwords don't need to be stored. Standard practice is to store a digest of the password, and compare the digest of the offered password to authenticate.

      Very true for user accounts stored in a database, but you still need to connect to the database daemon, in order to check the digests, and thus you need to store a password somewhere, or have numerous accounts with read access on the database daemon itself, which might be a problem, when you have an account on some webhoster's machine.

      Update: I'm more interested in limiting other users on your system from altering your database, than limiting them from "stealing" passwords of users on your website.

      --
      b10m

      All code is usually tested, but rarely trusted.

        SuExec causes your [cgi] script to run with your uid. Other users' will run under theirs. They cannot 'donate' a snooper [or clobber] script to your uid because chown to another user is a privileged operation.

        [If you give your db password-containing module 0600 permissions, nobody can see inside but root and you.]

        After Compline,
        Zaxo

Re: Securing your scripts on webhoster's server
by Abigail-II (Bishop) on Feb 01, 2004 at 00:24 UTC
    I don't think there's much hope. If others run their programs with the same permissions as your programs, they can not only read the same files as your programs - they can write to them as well. They'll be able to send your programs signals, and use /proc to query your processes (assuming the OS you are running on uses /proc).

    I'd say switch to a provider that gives you the privacy you need. Yes, this may cost money, but as the old saying goes there's no such thing as a free lunch.

    Abigail

Re: Securing your scripts on webhoster's server
by hardburn (Abbot) on Jan 31, 2004 at 23:36 UTC

    If your hosting company is smart, they'll chroot customer's shell login. Chroot can be defeated if the user has shell access (which is difficult in most chroot environments, since it's hard to get at a shell in the first place) and possibly under other conditions, but it's better than nothing.

    Also, if you want your own coloc box, get yourself hooked up with people in your area who'd also like their own server. Perl Mongers groups and LUGs are good places to find people. You can chip in on a business-class DSL or cable modem (something you can run a server on without the ISP coming down on you). Split three or four ways, you should be able to do it for $25 a month in the US (not sure about other countries, sorry)--about the same as a regular hosting provider. Find some old machines to run servers on (for a low traffic site, P100s are fine), and be sure that someone in the group has enough know-how to get a firewall up.

    Most DSL/Cable modem ISPs give a lot more download speed than upload, even on buisness-class lines. The person who's house is storing all the servers should be willing to cover the cost of electricity, etc. for the extra downstream bandwidth they're given.

    Then go to the hosting person's house with a camera, get your site posted on Slashdot, and take pictures as your server bursts into smoke :)

    ----
    I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
    -- Schemer

    : () { :|:& };:

    Note: All code is untested, unless otherwise stated

Re: Securing your scripts on webhoster's server
by bradcathey (Prior) on Jan 31, 2004 at 22:23 UTC
    As the poster mentioned in the first line of b10m's OP, I find this an interesting follow-up. Asking for user and password obviously wouldn't work for database-driven sites where content is being served up for any number of anonymous visitors.

    And as I mentioned in my my reply to b10m, I'm trying to avoid hardcoding the DBI connect info into the scripts, just to make it more transportable.

    What's the feeling around the monastery towards encrypting sensitive info somehow? Encrypt it when it is put into the text file, and decrypt in the scripts connecting to the DB. Maybe this falls into the realm of "security through obscurity" (still relatively new to Perl, I'm not even sure if descent codable encrytion exists).

    It will be interesting to see how other monks weigh in on this one.

    Update: After a Saturday of researching, I have started encrypting my DBI log-ons with Crypt::CBC. Yes, it does use a key which I plan to store in a SSL protected directory on the host's server (they offer a shared certificate for $1/mo). That's the best I can hope for now.

    Update 2: My host said storing my key in a SSL protected directory won't gain me a thing. They did suggest chmoding a file with my key to 600 and putting it into the root, outside all web folders.

    —Brad
    "A little yeast leavens the whole dough."

      But what do you do about the key? I have been trying to figure out a similar construct for our sites, but if you encrypt your data with a two-way algoritm, you will need a key to decrypt it. The question then becomes, where do i put that key. One option we are (sort of) considering is that the server will not restart without a user entering a pass-phrase that the server startup script then uses as the key for decryption. The obvious problem is that if the server goes down at 2 a.m. (U.S.) EST, then none of us are going to know about it until 9 the next morning. Many of our applications are deployed globally, so this just wont work.

      Right now we keep the DB username/password in a single file stored outside of the web accessable directories, and since we are always on a dedicated server (or Virtual Private Server) its reasonably secure. In the end, it really comes down to the clients security needs too, some care more than others.

      -stvn
Re: Securing your scripts on webhoster's server
by etcshadow (Priest) on Feb 01, 2004 at 05:32 UTC
    Here's a simple one: set the password as an environment variable in your apache config file, and make the file readable only by you. The fact that the process is gonna setuid to user nobody (as is pretty common), is not an issue, because the config file is read before that happens.

    This would, of course, not really help if you don't have your own apache process space (because, let's say, you and all of your peers are sharing port 80 via virtual hosts, and no reverse proxying -- ack!). Of course, if you don't have your own process space, then, really, how can you expect to have any kind of security at all?

    I suppose, though, that in the case of not having your own process space at all, you could ask the server administrators to let you drop your own apache config section inside the <VirtualHost ...> block that applies to you, and then make use of the same idea.

    ------------ :Wq Not an editor command: Wq
Re: Securing your scripts on webhoster's server
by zentara (Cardinal) on Feb 01, 2004 at 17:19 UTC
    "Securing your scripts on webhoster's server"

    I think this country would be better off spending billions of dollars to put fiber-optics into every house and business, rather than sending men to mars. That would eliminate the problem of using remote servers.

      Good one.

      Another answer (and similarly lame), is that if you can't trust your host to properly configure their servers, pick a host that does not allow shell accounts. This will mean more work on your end to debug things (you'll pretty much need your own *NIX box and FTP the site back when you are through), but you won't have any permissions problems.

      As far as I can tell, my host is both using suEXEC type behavior *and* is disabling shell accounts. Paranoid? You bet. I like that in a host.

        Another answer (and similarly lame), is that if you can't trust your host to properly configure their servers, pick a host that does not allow shell accounts. This will mean more work on your end to debug things (you'll pretty much need your own *NIX box and FTP the site back when you are through), but you won't have any permissions problems.

        I would never pay for some account that didn't include a SSH shell, but that's besides this point. Of course you still have permissions problems, since Perl gives you a "shell".

        For example the following (horrible) piece of code will give you some nice information. Of course you should regexp on something more useful than just "connect", but this piece of code isn't meant to be too successful ;) And yes, in the loop of found matches, you could open the specific file and/or copy it to your own directory or something ...

        #!/usr/bin/perl print "Content-type: text/plain\n\n"; $\ = "\n"; $http_dir = "public_html"; open(PASSWD, "</etc/passwd"); while(<PASSWD>) { chomp; @user = split(":", $_); if($user[2] >= 1000) { print "Found user \"$user[0]\", scanning \"$user[5]/$http_dir\": +"; print "="x70; if(@found = `grep -slr 'connect' $user[5]/$http_dir/*`) { chomp(@found); foreach (@found) { print "Match in: $_"; } } else { print "nothing found..."; } print "\n"; } } close PASSWD;
        As far as I can tell, my host is both using suEXEC type behavior *and* is disabling shell accounts. Paranoid? You bet. I like that in a host.

        I like the suEXEC bit of your host. The no-shell policy would drive me crazy, but luckily I have my own server(s) :)

        Note: I chose to use the backticks for the grep command, because a) it shows you still have a shell when you have FTP access and you're allowed to run CGI apps, and b) it's easier than a native Perl way ;)

        --
        b10m

        All code is usually tested, but rarely trusted.