Bugs, Security, and Other Tweaks

There were supposed to be only eight parts to this series, but as we started releasing them, Chris and I realized that there was going to need to be a follow-up post to address some of the bug fixes, security patches, and a few other minor changes.

NOTE: All the changes we're going to cover in this article are already included in the source code.

Bug Fixes

After releasing the live app, a handful of bugs showed up in the comments. We tried to address these as quickly as possible to keep the app from causing unnecessary grief for our users. We'll go over the major bugs here.

Account Created, List Failed Error

The first thing we saw was that when there were more than just one or two people trying to create accounts, the app started failing to create user lists after an account was created. Upon reviewing the code, I found that the error seemed to be coming from the following line:

$userID = $this->_db->lastInsertId();

$userID seemed to be unreliable, and therefore the query to insert a new list into the database was failing regularly. To fix this, we implemented a complex query that worked around the use of lastInsertId():

            /* 
             * If the UserID was successfully 
             * retrieved, create a default list. 
             */
 
            $sql = "INSERT INTO lists (UserID, ListURL) VALUES 
                    ( 
                        ( 
                            SELECT UserID 
                            FROM users 
                            WHERE Username=:email 
                        ), 
                        ( 
                            SELECT MD5(UserID) 
                            FROM users 
                            WHERE Username=:email 
                        ) 
                    )"
;

Performance-wise, this is going to be slower than the original post, but it's incredibly more reliable (since implementing this fix, we've had no reports of this error). Any MySQL supergeeks who may have a better solution, please post it in the comments!

Double-Clicking "Add" Sometimes Added Duplicate Entries

One little user interface quirk that was discovered was that you could click multiple times in succession on the "Add" button. In our original JavaScript, we only cleared the value of the input field upon a successful AJAX result. That is ideal, since when that text disappears that is your visual queue that it's been added to your list successfully. Plus, generally that happens quickly enough it feels pretty instant. However, if you straight up "double-click" on that add button (which people absolutely still do that), you might get two or more requests off before the success comes back and clears the fields (when the field is clear, the submit button will do nothing).

One method to fix this could have been to clear the field as soon as a click happens, but the problem there is that if the save is unsuccessful you'll lose your text. Instead, we just add a little more smarts. When the submit button is clicked and there is text ready to add, the AJAX request is made and the button is disabled. Upon success, the field is cleared and the button is re-enabled. This ensures only one submission is possible.

In /js/lists.js, we added the following at line 114:

    $('#add-new').submit(function(){ 
 
       //  ... variables and whitelist stuff ... 
 
        if (newListItemText.length > 0{ 
            // Button is DISABLED 
            $("#add-new-submit").attr("disabled", true); 
 
            $.ajax({ 
 
                // Ajax params stuff 
 
                success: function(theResponse){ 
 
                    // list adding stuff 
 
                    // field is cleared and button is RE-ENABLED 
                    $("#new-list-item-text").val(""); 
                    $("#add-new-submit").removeAttr("disabled"); 
     }

NOTE: As you can see, we remove the "disabled" attribute entirely upon a successful response from our query. That is the only way to re-able a submit button. Setting disabled to "false" has no effect.

Changing Email Address with a Blank Email Crippled Account

It was also brought to our attention that clicking the "Change Email" button with a blank field would not only succeed, but would cripple the account and make it unusable. Fixing this was as simple as making sure the email address submitted was valid by inserting the following in the updateEmail() method in /inc/class.users.inc.php:

        ifFALSE === $username = $this->validateUsername($_POST['username']) ) 
        { 
            return FALSE
        }

Then, instead of binding the $_POST value to the query, we bind the new variable $username, which contains the valid email address if the check didn't fail. Note the use of the new function validateUserEmail()—we'll go over that in the next section on security.

Security Issues

After we worked the bugs out of our app, we turned to the security holes that were pointed out by commenters. Some of these were simple oversights on our part, and some of the problems were news to us. With the help of our readers, though, we tried to patch everything up.

JavaScript Could Be Inserted Into Edited Items

When creating new items, we checked for any JavaScript in the input using the cleanHREF() function, then stripped out unwanted tags on the server side using strip_tags() and a whitelist of acceptable tags. However, we had missed that JavaScript could be inserted into existing items when they were edited. To correct this issue, we turned to a preexisting input sanitizing function (lines 00326-00384) posted by Zoran in the comments of Part 8.

We wrapped the code in a method called cleanInput() and placed it in /inc/class.users.inc.php:

    /** 
     * Removes dangerous code from the href attribute of a submitted link 
     * 
     * @param string $input        The string to be cleansed 
     * @return string            The clean string 
     */
 
    private function cleanInput($data
    { 
        // http://svn.bitflux.ch/repos/public/popoon/trunk/classes/externalinput.php 
        // +----------------------------------------------------------------------+ 
        // | Copyright (c) 2001-2006 Bitflux GmbH                                 | 
        // +----------------------------------------------------------------------+ 
        // | Licensed under the Apache License, Version 2.0 (the "License");      | 
        // | you may not use this file except in compliance with the License.     | 
        // | You may obtain a copy of the License at                              | 
        // | http://www.apache.org/licenses/LICENSE-2.0                           | 
        // | Unless required by applicable law or agreed to in writing, software  | 
        // | distributed under the License is distributed on an "AS IS" BASIS,    | 
        // | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or      | 
        // | implied. See the License for the specific language governing         | 
        // | permissions and limitations under the License.                       | 
        // +----------------------------------------------------------------------+ 
        // | Author: Christian Stocker <chregu@bitflux.ch>                        | 
        // +----------------------------------------------------------------------+ 
        // 
        // Kohana Modifications: 
        // * Changed double quotes to single quotes, changed indenting and spacing 
        // * Removed magic_quotes stuff 
        // * Increased regex readability: 
        //   * Used delimeters that aren't found in the pattern 
        //   * Removed all unneeded escapes 
        //   * Deleted U modifiers and swapped greediness where needed 
        // * Increased regex speed: 
        //   * Made capturing parentheses non-capturing where possible 
        //   * Removed parentheses where possible 
        //   * Split up alternation alternatives 
        //   * Made some quantifiers possessive 
 
        // Fix &entity\n; 
        $data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data); 
        $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u''$1;'$data); 
        $data = preg_replace('/(&#x*[0-9A-F]+);*/iu''$1;'$data); 
        $data = html_entity_decode($dataENT_COMPAT'UTF-8'); 
 
        // Remove any attribute starting with "on" or xmlns 
        $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data); 
 
        // Remove javascript: and vbscript: protocols 
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data); 
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data); 
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data); 
 
        // Only works in IE: <span style="width: expression(alert('Ping!'));"></span> 
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data); 
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data); 
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data); 
 
        // Remove namespaced elements (we do not need them) 
        $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data); 
 
        do 
        { 
            // Remove really unwanted tags 
            $old_data = $data
            $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data); 
        } 
        while ($old_data !== $data); 
 
        return $data
    }

Then, we modified updateListItem() on line 239 to call the new method:

        $newValue = $this->cleanInput(strip_tags(urldecode(trim($_POST["value"])), WHITELIST));

CATCH: This function appears to encode any non-English characters. Foreign language users may see some unexpected behavior.

Cross-Site Request Forgeries

Another risk we hadn't considered when building this app was the possibility that a malicious user could send bogus requests to our app by piggybacking on a Colored Lists user's session in a form of attack called Cross-Site Request Forgeries (CSRF). The snippet of JavaScript below, placed on any site, would be executed if a user that was logged in to our app were to visit the page. (Huge thanks to Dan at Sketchpad for pointing this out and providing the above sample attack.)

    <script type="text/javascript">    
    
        var form = document.createElement("form"); 
        form.setAttribute("method""post"); 
        form.setAttribute("action""http://coloredlists.com/db-interaction/users.php"); 
            
        var fields = new Array(); 
        fields["user-id"] = "158"
        fields["action"] = "deleteaccount"
            
        for(var key in fields) 
        { 
            var hiddenField = document.createElement("input"); 
            hiddenField.setAttribute("type""hidden"); 
            hiddenField.setAttribute("name", key); 
            hiddenField.setAttribute("value", fields[key]); 
 
            form.appendChild(hiddenField); 
        } 
 
        document.body.appendChild(form); 
        form.submit(); 
 
    </script>

To remedy this, we need to generate a token to include with each form submission that is also stored in the session. That way we can make sure the two match before executing any requests. In doing this, CSRF is virtually eliminated.

In /common/base.php, we added following at line 19:

    if ( !isset($_SESSION['token']) ) 
    { 
        $_SESSION['token'] = md5(uniqid(rand(), TRUE)); 
    }

This creates a unique token for the user's session. Then, on every form on our site, we added the following hidden input:

                <input type="hidden" name="token" 
                    value="<?php echo $_SESSION['token']; ?>" />

And updated both /db-interaction/lists.inc.php and /db-interactions/users.inc.php with the following starting at line 15:

if ( $_POST['token'] == $_SESSION['token'
    && !empty($_POST['action']) 
    && isset($_SESSION['LoggedIn']) 
    && $_SESSION['LoggedIn']==1 )

Now any request made without a valid token will fail. For more on CSRF, visit Chris Shiflett's blog.

Some Input Was Improperly Sanitized

Above, we talked about the problem with blank email change requests breaking accounts, and we created a method called validateUsername() that made sure only valid email addresses were allowed to change an existing user email. That method looks like this:

    /** 
     * Verifies that a valid email address was passed 
     * 
     * @param string $email    The email address to check 
     * @return mixed        The email address on success, FALSE on failure 
     */
 
    private function validateUsername($email
    { 
        $pattern = "/^([+a-zA-Z0-9])+([+a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/"
        $username = htmlentities(trim($email), ENT_QUOTES); 
        return preg_match($pattern$username) ? $username : FALSE
    }

Essentially, it uses a regular expression to match the pattern of a valid email address, and either returns the validated email address of boolean FALSE.

Other Changes

Aside from bugs and security patches, there were a couple parts of the site that we just felt should have been better.

Made Public URLs Tougher to Guess

First, the original public list URLs were determined using dechex(), and they were short and easy to guess. We modified them to use MD5 instead to create longer, much more difficult to guess public URLs. This happens right at the list's creation when the query calls SELECT MD5(UserID) in createAccount() on line 100.

Allowed "Safe" Links in List Items

Some links are acceptable, and we felt that our app would be much more useful if safe links were allowed in list items. To allow this, we simply removed the call to strip_tags() in formatListItems() (found in /inc/lists.inc.php on line 173):

        return "\t\t\t\t<li id=\"$row[ListItemID]\" rel=\"$order\" " 
            . "class=\"$c\" color=\"$row[ListItemColor]\">$ss" 
            . $row['ListText'].$d 
            . "$se</li>\n";

The items are now sanitized on the way in, so we don't need to worry about them on the way out.

Summary

The steps we took above helped make our app more secure and dependable. However, we know that nothing is ever perfect, so if you've got other bugs, security holes, or suggestions, let us know in the comments!



Comments

  1. Uttenlyzoot
    Uttenlyzoot on date Tuesday 16 November 2010 18:16
    <a href="http://www.decorative-concrete.me/">decorative concrete</a> Not just is cement functional, it lends itself to your vast selection of design options which may make a dramatic difference in home landscaping plans, as well as boost residence values. In addition on the conventional appear, concrete can possess the decorative appearance, really feel, and coloration of brick, tile, slate, or stone. Today Cement Finishes have expanded to include an astounding array of decorative options. Occasionally referred to as a cement driveway or painted cement, Decorative Concrete is one with the most fair techniques to spruce up the entrance to a house. Even though Plain gray Cement is nonetheless set up most generally, a lot more people today are catching on on the dazzling effects achievable with decorative concrete, and seeing the instant curb charm a ornamental driveway can provide to any home, no matter what the fashion. You will discover several reasons why you really should have the vertical stamping to your concrete slabs. They boost the charm of the house and in the exact same time, add value for your house so that you will be benefited in the event you market your property in long term. The vertical overlays have numerous benefits in compared towards the other possibilities available. For instance, it is possible to accomplish an outstanding amount of particulars within the design with this kind of decoration. They're perfect to possess the delicate and refined hand curving that can be extremely exact. What's much more, there is no reason to walk about the floor to do the stamping. <a href="http://www.decorative-concrete.me/">decorative concrete</a> <a href="http://www.decorative-concrete.me/">decorative concrete</a> <a href=http://www.decorative-concrete.me/>decorative concrete</a> <a href="http://www.decorative-concrete.me/">decorative concrete</a> <a href='http://www.decorative-concrete.me/'>decorative concrete</a>
  2. Megan Price
    Megan Price on date Tuesday 14 December 2010 09:50
    <a href={url}>{keyword}</a> <a href={url}>{keyword}</a> <a href={url}>{keyword}</a>
  3. pluddydaubamp
    pluddydaubamp on date Monday 20 December 2010 11:53
    BeenSpert http://blogg.blush.no/?p=1217 http://www.lotterypicksports.com/post/2010/08/25/SHAQ-SUPPOSEDLY-IS-ENGAGEDI-DONT-BELIEVE-IT.aspx http://earthcaringart.com/?attachment_id=903 http://www.porthill-yokohama.jp/blog/2010/07/post-25.html http://www.articlenews.info/?p=326 http://www.porthill-yokohama.jp/blog/2010/02/post-16.html http://valje.se/?p=36 http://miyazaki-kotei.bokin-net.com/?p=220 http://momon.muse-lab.net/archives/235 http://www.thainar.com/942.html http://brash.glam.jp/nagatomo/archives/52706 http://blog.almago.com/tabs/passatempos/ja-comecou-o-pior-emprego-do-mundo http://winbackwashington.com/post/2010/02/16/TBW-Pics.aspx http://www.idiaocha.com/2010/11/samsung-fascinate-review http://www.whatlivetoday.com/games/5-interesting-facts-about-nintendo http://www.meidanpariisi.net/?p=1085 http://hulkur.pri.ee/wiimaneweerg/?p=686 http://www.astralis-saga.com/?p=77 http://heatherriggio.featuredblog.com/?p=27&inm=200805 http://www.rcmart.info/?p=91 http://toudai.ex5.biz/articles/103.html http://alanwwolf.com/?p=50 http://www.actufoot06.com/morlino/index.php?blog=5&title=les_eagles_a_anvers_le_18_juin_2009&more=1&c=1&tb=1&pb=1 http://www.hanatoiro.sakura.ne.jp/blog/?p=1589 http://highstandard.nl/wordpress/?attachment_id=420 Sconsurrere
  4. whagmavingeme
    whagmavingeme on date Tuesday 11 January 2011 18:50
    thanks exchange for this tips <a href="http://www.turf.asso.fr">pmu</a> <a href="http://www.turf.asso.fr">turf</a>
  5. viagra
    viagra on date Wednesday 19 January 2011 11:34
    thanks for this nice article <a href="http://www.mrsviagra.com/de/">viagra</a> viagra kaufen viagra
  6. bTusaStuff
    bTusaStuff on date Tuesday 14 June 2011 05:49
    how to unlock iphone 4 unlock iphone 4 unlock iphone 4 http://dxzy.net/story.php?id=62275#discuss http://www.digg-insurance.info/story.php?id=7723#comments <a href="http://unlockiphone421.com">iphone 4 unlock</a> how to unlock iphone 4 iphone 4 unlock <a href=http://unlockiphone421.com>how to unlock iphone 4</a> how to unlock iphone 4 unlock iphone 4
  7. Rerantafehamn
    Rerantafehamn on date Wednesday 11 January 2012 20:39
    <a href=http://pornfreetube.ru/><img>http://pornfreetube.ru/pt/47acf135ad.jpg </img></a> <a href=http://pornfreetube.ru/><img>http://pornfreetube.ru/pt/3dd4ab9904.jpg </img></a> <a href=http://pornfreetube.ru/>порно фильмы </a>
  8. creative writing help
    creative writing help on date Tuesday 06 March 2012 15:22
    I think there are really a lot of ways of changing some problems here!
  9. essay writer
    essay writer on date Tuesday 06 March 2012 17:46
    I didn't know about it! It's an interesting idea really
Post your comment






© 2008-2010 KPSOFT INC. KPCMS