[L3] Automatic Filtering Input in Laravel 3.*

cereal 1 Tallied Votes 377 Views Share

This is for Laravel 3.*

Hello,

With this snippet I'm providing a simple way to automatically filter Input::get() and Input::old(). To achieve this result we need to make few changes:

  • extend Input and Redirect classes by creating new files in application/libraries/, the files are: input.php and redirect.php;
  • comment the aliases of these classes in application/config/application.php file, otherwise the application will ignore the extended libraries.

The extended Input class overwrites Input::query() so it can use the new method Input::sanitize(), which is where you can place your filters, in this example I'm using filter_var_array():

public static function sanitize($array)
{
    $result = filter_var_array($array, FILTER_SANITIZE_STRING);
    return $result === false || is_null($result) === true ? array() : $result;
}

But it can be rewritten as you like, this method is just a container for your prefered approach, it can be an implementation of CodeIgniter xss_clean() method, or you can simply use strip_tags(). Just make sure to return an array of keys and values.

Below there is a version working with the HTMLPurifier library, in this case I'm using the porting published in the bundles archive. Once installed, you can change the above method with:

/**
 * Filter the query string array
 *
 * <code>
 *      // Return an empty array if data is removed
 *      $array = Input::sanitize(array('msg' => 'hello'));
 * </code>
 *
 * @param  array   $array
 * @param  array   $input
 * @return array
 */

public static function sanitize($array, $input = array())
{
    $purifier = IoC::resolve('HTMLPurifier');
    $array = filter_var_array($array, FILTER_SANITIZE_STRING);

    switch($array)
    {
        case is_array($array) === true:
            foreach($array as $key => $value)
            {
                $input[$key] = $purifier->purify($value);
            }
            break;
        case $array === false:
        case is_null($array) === true:
        default:
            return $input;
            break;
    }
    return $input;
}

Usage

Here is a basic test controller:

<?php

class Test_Controller extends Base_Controller
{

    $restful = true;

    public function get_index()
    {
        $input = Input::get();
        echo $input['msg'];

        print_r($input);
        return Response::json(true);
    }

}

Then you can try to pass some variables to the controller, for example:

http://local.dev/test?msg=hello<script>alert('world');</script>

To make the changes available also to Input::old() we need to extend Redirect::with_input(), there is no changes to do, just copy and paste this method to the extended class. It is necessary because the extended libraries are not used by the other core libraries of Laravel 3.* so we need to pull out what is needed.

Note 1

By default input is not escaped, but it will, if you use the Form library or the database libraries as Fluent or Eloquent. The "problem" comes if you send a request back to the form and paste the error message and the field value, without using the Form library as in this example:

Form::text('email',Input::old('email'))

By using "traditional" practices the same becomes unsafe:

<input type="text" name="email" value="<?php echo Input::old('email'); ?>" />

I have to say that Laravel offers also HTML::entities() or e() which is an alias of the previous, but this forces to keep in mind to use those methods:

echo e(Input::old('email'));

But I prefer my approach since this allows me, by the other things, to send data to session or to cache it without bothering anymore with sanitization.

Note 2

This snippet will not work in Laravel 4, it's really different and the escape can be done at Blade level (the template engine) by surrounding the input with three {:

{{{ Input::old('email') }}}

against the normal rule to echo variables which request only two curly brackets: {{ $bye }}.

# File 1: save it in application/libraries/input.php

<?php
class Input extends Laravel\Input {

	/**
	 * Filter the query string array
	 *
	 * <code>
	 *		// Return an empty array if data is removed
	 *		$array = Input::sanitize(array('msg' => 'hello'));
	 * </code>
	 *
	 * @param  array   $array
	 * @return array
	 */
	public static function sanitize($array)
	{
		$result = filter_var_array($array, FILTER_SANITIZE_STRING);
		return $result === false || is_null($result) === true ? array() : $result;
	}

	public static function query($key = null, $default = null)
	{
		$array = static::sanitize(Request::foundation()->query->all());
		return array_get($array, $key, $default);
	}

	public static function flash($filter = null, $keys = array())
	{
		$flash = ( ! is_null($filter)) ? static::$filter($keys) : static::get();
		$flash = static::sanitize($flash);
		Session::flash(Input::old_input, $flash);
	}




# File 2: save it in application/libraries/redirect.php

<?php

class Redirect extends Laravel\Redirect {

	public function with_input($filter = null, $items = array())
	{
		Input::flash($filter, $items);
		return $this;
	}
	
}