The vast majority of scripts I write are CGI-related in some way. Naturally, I've found that placing a script online, where anyone can take a shot at it, can be a little unnerving at times. Luckily Perl eliminates many security problems (buffer overflows for one :), but there are still many areas of concern. I've attempted to list the essential security practices that, when followed, enable one to write more secure CGI scripts.
Taint Mode - I use this in every CGI script I write. It has helped me catch many mistakes that I would have otherwise overlooked. Sometimes though it gets me feeling a little too secure, as perlsec says "The tainting mechanism is intended to prevent stupid mistakes, not to remove the need for thought."
User Input - As a CGI Course I recently read stated "always trust your users. Never trust their input." Legitimate users can send unexpected data, malicious users will send everything you can think of - and more. This includes manipulating hidden form field values. Don't depend on the referrer (or "referer" ;) either, for it can be easily spoofed as well.
Also worthy of noting, when filtering user input don't try to think up every possible symbol/combination to filter out. Instead, simply state the form of the data your script will except and return an error for everything else.
Error Messages - Don't give out more information than necessary. If a user supplies an incorrect password for a legitimate user name, print out 'Incorrect Login' not 'Incorrect Password.' Also make sure to get all those use CGI::Carp qw/fatalsToBrowser/; out of production code.
Make %ENV safer - From perlsec "... set $ENV{'PATH'} to a known value, and each directory in the path must be non-writable by others than its owner and group." It also goes on to suggest that including something along the lines of delete %ENV{qw(IFS CDPATH ENV BATH_ENV)}; would be a good idea.
Encryption - Essential for any sensitive data. There are a whole bunch of modules on cpan dealing with encryption but currently I have very limited experience with them. Any advice from the more experienced monks on this subject would be appreciated.
Those are the essential security practices that I currently employ. Inevitably I have overlooked something so please reply with any suggestions for improving this node. Thanks.
Re: Essential CGI Security Practices
by BazB (Priest) on Feb 02, 2002 at 19:34 UTC
|
use CGI or die; - Don't try and reinvent the CGI module - it works, and it's been well tested.
You should avoid attempting to roll your own module - it's unlikely to be any better, and if it is, then you've spent too much time on it :-)
Think beyond Taint and warnings - Although taint mode, warnings and so forth should not be overlooked - make sure the rest of the code is written in a sensible/secure manner.
Don't use a script if you don't know what it's doing - Probably more for newbie Perl users who think that Matt's Perl scripts are good.
Spend time reading through a script if you didn't write it yourself and don't know the quality of the author's other work.
I think that's about all I can think of for the moment.
BazB.
| [reply] |
Re: Essential CGI Security Practices
by dws (Chancellor) on Feb 02, 2002 at 21:11 UTC
|
Good list, to which I would add:
Peer Review - Apply several pairs of competent eyeballs to the code. A skilled colleague, reading the code with a "how would I break this" hat on is a great way to uncover subtle problems.
Data Security - Keep sensitive, missions critical data off of the web server box, especially if you're dealing with credit cards. Encryption isn't always enough.
| [reply] |
|
I'd like to add Subsection 1 to Peer Review. This section would be called QA.
QA - Put your code into a replication of your production environment and get a dedicated QA person to go thru' your application as if it was live on the web. A skilled QA person is a seriously good weapon to have in your arsonal. While youre there you may as well set up a dedicated UAT to test your application as well. Keep in mind you shouldnt tell your QA 'guy' about how or what your app does as this may influence the nature of their testing.
| [reply] |
|
| [reply] |
Re: Essential CGI Security Practices
by pjf (Curate) on Feb 03, 2002 at 02:07 UTC
|
I'm occasionally employed to do security audits for programs both written in Perl and in other languages, and there's one mistake that consistantly pops up and earns me my wage. I've had to give the same piece of advice many many times:
If you're dealing with a database, quote your values - properly.
If you're using DBI and placeholders, then you should never have this problem. Alas, I've seen far too much code that will bomb unexpectedly when Ms O'Donnell enters her surname, or will cost thousands when someone requests a username of '; rollback; begin; drop table customers; commit;. (Note the leading tick.)
If you're doing database work and don't know about the DBI module and placeholders, then go and read up on them now. They're worth it. Really.
Cheers,
Paul Fenwick
Perl Training Australia | [reply] |
Re: Essential CGI Security Practices
by footpad (Abbot) on Feb 03, 2002 at 06:20 UTC
|
And I would add:
Don't accept more than is absolutely necessary. Example: MSA's formmail.pl can be used as a spam relay because it accepts recipient as a CGI parameter. Don't do that.
Only accept the minimum number of parameters you need.
Don't trust anything from the client--period. I've seen cases where CGI scripts claim to be secure because they check REFERER, which is trivial to spoof.
Don't ever use a script that tells you to chmod a publically accessible file to 777 (Example: here).
Be highly suspicious of code that's been updated one or fewer times in the last several years. (Theory: No one was defending against DoS attacks in 1996.)
Not that I'm picking on anyone, of course. However, I have been helping a buddy run down various spammers that are playing on his system. Guess where the currently identified problems have come from?
--f
| [reply] |
Re: Essential CGI Security Practices
by gellyfish (Monsignor) on Feb 04, 2002 at 12:02 UTC
|
As far as the CGI::Carp qw/fatalsToBrowser/ goes I would suggest an alternative to removing it altogether. CGI::Carp has had the facility to alter the output message for a quite a while - you can supply a coderef to a subroutine that will be called with the error message and which should print the text of the message to be output - you can set a $DEBUGGING variable to determine whether the actual error message gets output:
use CGI::Carp qw(fatalsToBrowser set_message);
use vars qw($DEBUGGING);
BEGIN
{
$DEBUGGING = 1;
my $error_handler = sub
{
my $message = shift;
print "<h1>Oooh I got an error</h1>";
print $message if $DEBUGGING;
}
set_message($error_handler);
}
This allows you to easily switch on or off the detailed error messages and means you don't have to take the 'or die' out of potentially hundreds of lines of code.
/J\
| [reply] [d/l] [select] |
Re: Essential CGI Security Practices
by belg4mit (Prior) on Feb 02, 2002 at 22:34 UTC
|
I'm not sure about that Error Messages,
or at least the example. I know I always
find it incredibly frustrating to get
meaningless fluff back as error messages
from a website. If the error has some
meat to it I might be able to remedy the
thing myself by altering the form data
or hand-parsing the URL.
Simply saying "Invalid Login" doesn't
buy you any securty if you're sending
in the clear over the wire, so why make
it harder on the user? There are other
ways of handling brute-force attacks
that you are expressing worry about
here (think escalating delays for failures).
On the other hand, if you are truly
paranoid minimizing the information
you betray about your system is
probably a good thing e.g. paths,
server information (HTTP headers, etc.)
(unfortunately?) that means no
"Powered by Apache" feathers either ;-)
As for %ENV I stand by "Re: perlsec question".
--
perl -pe "s/\b;([st])/'\1/mg"
| [reply] |
|
| [reply] |
|
| [reply] |
|
|
I wouldn't go so far as to say that "Invalid login" fails
to buy any security - it prevents users from
trivially determining whether a username is valid or not,
thus significantly increasing the search space for a
brute-force attack. Not a silver bullet by any means (not
even a very shiny one, really), but still enough to be
significant in many cases.
(Yeah, escalating delays are good, too, but a little
trickier to implement in an environment, such as CGI, where
you can't reliably maintain state.)
| [reply] |
|
I know I always find it incredibly frustrating to get meaningless fluff back as error messages from a website.
Would it be less frustrating to get, "Error committing
to table: incorrect number of fields"?
For errors the use can maybe fix (e.g., username and
password don't match), I try to give a meaningful and
complete explanation of the problem. (At least, in
theory that's what I intend to try to do.) For
internal errors, I like to do something like the
following:
mydie("Error Condition One in Section Gimmel");
My mydie subroutine starts out by explaining that there
is some internal problem, that it is probably not the
user's fault, that it's something the webmaster needs
to fix, and that he might be able to do so more
easily given the technical error code below. Then
I give contact info for whoever maintains the script,
and print out the string that was passed by the caller,
labelled as a Technical Error Code.
(The important thing about this string is that it is
unique, and I can grep for it to find exactly where
the problem was encountered.)
All of this can be nicely formatted in a
<div class=\"error\">...</div> so that
the site CSS can easily style it as desired. This
is MUCH nicer to the user than "Internal Server Error",
but it doesn't give a potential malicious user a great
deal of information, other than how to contact the
admin. (Then you just have to make sure this person
is not susceptible to social engineering... but you
have to do that anyway)
$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}}
split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
| [reply] [d/l] [select] |
|
|