If Client-Side Security Is a Condom, You Must be a Father Already!

So, you’re a web developer. Did you know that anyone who uses your website can edit its HTML?

“ARE YOU f*cking KIDDING ME!! That’s so simple! Of course I know that!”.

Hold your horses pal! I know, I know. But sometimes programmers, especially inexperienced ones, forgot this stupid fact. They thought that client-side security is enough to handle bad users. Until someone figure out that it is only a front-end security and starts to mess with the HTML.

Here are some examples to elaborate this simple security issue. Please keep in mind that the following examples focus only on the HTML-editing attacks not on SQL injection and other hacks. So stop whining like a retard about the following code snippets.

That code is still vulnerable to SQL Injection.

Meet Joe. A fresh graduate student, a self-taught PHP programmer, and a freelancer. Take note, he is just an imaginary character for example purposes only. Any similarities in real life is pure coincidence. So if you’re name is Joe, keep it cool bro.

So Joe made a simple forum application for a small client. Let’s scrutinize some of his implementation of different features and try to learn from his mistakes.

Lets start with the little comment form.

<form action="save_comment.php" method="POST">
  <input type="hidden" name="thread_id" value="<?php echo thread_id; ?>"/>
  Author: <input type="text" name="author"/>
  Comment: <textarea name="body"/>

  <input type="submit" name="submit"/>
</form>
// save_comment.php

$thread_id = $_POST['thread_id'];
$author = $_POST['author'];
$body = $_POST['body'];

sql.query("INSERT INTO comments (thread_id, author, body) VALUES ($thread_id, '$author', '$body');");

The clients wanted a feature that forbids users to view and comment on locked threads. Maybe threads that contain porn or time machine conspiracies. Joe’s implementation is simple — When a thread is already locked, it will not be shown on the list of threads. However, that is only a client-side security and users can still still access the thread if they have the direct URL of the thread. Joe, realizing the weakness of client-side security in that situation, added a small server-side check each time a user tries to view a thread that redirects to an error page if that thread is locked. So, by this time all is safe. Since users don’t have a way to view the locked threads. Right?

If you will analyze Joe’s implementation closely, you will see that he put a hidden field (line 02) on the comment form to know which thread the comment is intended for. There’s no problem about that. However look at his code for save_comment.php, the script that is responsible for processing the comment form. As you can see, he is not performing any server-side validation to check whether the passed thread_id is locked or not. As an attacker, with a tool like Firebug, one can simply change the value of thread_id to any value to be able to comment on any thread that he wants, even the locked threads.

“I don’t need to access the thread to post a comment to it. All I need to do is know the thread’s ID.”.

That’s the first b*tch slap for Joe.

Adding a simple validation on the server-side will look something like this.

// save_comment.php

$thread_id = $_POST['thread_id'];

if ( is_locked($thread_id) ){
  // redirect to error page
}
else {
  $author = $_POST['author'];
  $body = $_POST['body'];
  sql.query("INSERT INTO comments (thread_id, author, body) VALUES ($thread_id, '$author', '$body');");
}

Now, even if someone changed the value of the thread_id hidden field, we can still forbid comments on locked threads since we are already performing server-side validation.

Let’s continue putting Joe to shame. This time, let’s look at his implementation of thread creation.

<?php if ( is_banned() ): ?>
  <form action="save_thread.php" method="POST">
    Author: <input type="text" name="author"/>

    Title: <input type="text" name="title"/>

    Category: <select name="category">
     <option value="Adult">Adult</option>
     <option value="Humor">Humor</option>
    </select>

    <textarea name="body"></textarea>

    <input type="submit" name="submit"/>
  </form>
<?php endif; ?>
// save_thread.php

$author = $_POST['author'];
$title = $_POST['title'];
$category = $_POST['category'];
$body = $_POST['body'];

sql.query("INSERT INTO threads (author, title, category, body) VALUES ('$author', '$title', '$category', '$body');");

We will start with the simple and less destructive one. Look at the thread form. It looks innocent and seems nothing malicious about it. However there is one simple client-end security present on the form. There is a select field (line 07) that limits the user with only 2 options, Adult and Humor. Select field is a common front-end security used to limit the user’s input with only the valid ones. But Joe! That is only a front-end constraint! An attacker can simply change the select field into a text field and input any crap values.

...
Category: <input type="text" name="category"/>

With the modified code above, a user will be able to enter invalid categories. It may not be destructive, but it can ruin the data of the site. The solution here is to add a server-side validation that checks the category against a whitelist.

// save_thread.php

$author = $_POST['author'];
$title = $_POST['title'];
$category = $_POST['category'];
$body = $_POST['body'];

$valid_categories = ("Adult", "Humor"); // whitelist

if ( !in_array($category, $valid_categories) ){
  // redirect to error message
} 

sql.query("INSERT INTO threads (author, title, category, body) VALUES ('$author', '$title', '$category', '$body');");

The select field is one of the illusions that fool most web developers. They thought that they already limit the users from inputting bad data since they already provide a set of options. But then again, users can edit the form, so you’re still f*cked.

Lastly, let’s have a look at this little security Joe put before rendering the form. At line 01, Joe put a simple if statement that renders the form only if the user is not banned. So if you are banned, due to whatever reasons, you cannot see the thread form and thus can’t post any. Yeah, that’s what Joe thought about. But now, rather than editing the HTML, we can create our own form using our favorite text editor. We just need to know the form action and the name of the fields and we’re ready to go.

The solution, again, is to add a server-side validation after the form was submitted.

// save_thread.php

$author = $_POST['author'];
$title = $_POST['title'];
$category = $_POST['category'];
$body = $_POST['body'];

if ( is_banned() ) {
  // redirect to error message
}

$valid_categories = ("Adult", "Humor"); // whitelist

if ( !in_array($category, $valid_categories) ){
  // redirect to error message
} 

sql.query("INSERT INTO threads (author, title, category, body) VALUES ('$author', '$title', '$category', '$body');");

This is another mistake that most programmers do. They thought that if user’s can’t see it, they can’t use it. But with little HTML knowledge, you can just replicate any user interface. So hiding a form or any user interface for security’s sake is useless at all. It’s like hiding your mouse so no one else can play Counter-Strike on your PC.

“I have my own mouse b*tch!”

These are just some examples why client-side security is not reliable. They are just your application’s first line of defense. Most of the time enemies will get pass through them so you must have also a defense waiting on the server-side. Look at your own codes, and see if you and Joe are doing the same mistakes. Just always remember — DO NOT TRUST ANYTHING FROM THE CLIENT-END.

>

8 Comments

  • January 6, 2012 - 12:40 am | Permalink

    Love the use of rage meme into this insightful post. Keep it up. :)

    • Karl Paragua
      January 6, 2012 - 12:42 am | Permalink

      Thanks for dropping by sir. Are you a fellow 9gagger? :)

  • January 8, 2012 - 7:34 pm | Permalink

    I agree. Even without professional experience, I know not to trust anything client side with websites.

    I like your blog, it’s entertaining (nerd-wise). You write good posts, a skill I’m trying to learn.

    Also, thanks for commenting on my blog on the post about IE :D.

    • Karl Paragua
      January 8, 2012 - 10:06 pm | Permalink

      Yo Paulo!

      Thanks for those kind words. My tip about writing is that, don’t try to sound very professional and don’t be afraid expressing yourself (especially if you are writing on your PERSONAL blog). As you can see on my post, I’m a real trash-talker and I’m not hiding it. Just give your post some taste of your personality bro. :)

  • January 19, 2012 - 9:59 am | Permalink

    nice breakdown of the code. Actually, insertion is just one of the many things every web developer hates. There are more things out there that most developers just don’t notice and will just slap their face once they got busted. :)

  • February 2, 2012 - 9:53 am | Permalink

    Do you have a Facebook fan page for your site?

  • Pingback: PHP Security Tips from the Community

  • Creative Commons License
    This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.