Destroy all incoming vars except those you want

I am sure this has been asked before but I cannot find the answer to it.

Say your app only expects the 2 following vars to appear:


$_GET['id'] ;
$_GET['title'] ;

How can you easily wipe out any other GET values that may have been sent
a) by accident
b) maliciously

Is there a nice easy one liner to do that after:


$permitted_vars = array('id','title') ;

Thanks!

$_GET = array_diff_key($preserve, $_GET);

Although I would recommend making a class for each form that ignores the not-needed values.

"Notice: Undefined index: id " :wink:


$_GET['id'] = 23 ;
$_GET['title'] = "a title" ;
$_GET['sql'] = "bad stuff;" ;

$preserve=array('id', 'title');

$_GET = array_diff_key($preserve, $_GET);

echo $_GET['id'] ;

That seems to wipe out values.

AlienDev had the right idea. Problem is you are passing in an array of values, so we need to flip it to the keys, and then compare. However, we don’t want the keys that are different, but the same:


$_GET = array_intersect_key($_GET, array_flip($preserve)); // This will work

I have this method in my constructor. It will throw an exception as soon as it spots a var that was not in the array of allowed vars.

	foreach ($aQ as $name=>$val) {
		$this->log('cp');
		if (!in_array($name, $this->aValidVars)) {

			$this->log('some request vars were not allowed in this request: '.print_r($aQ, 1));

			throw new LampcmsException('invalid_request_var: '.$name.' $this->aValidVars: '.print_r($this->aValidVars, 1));
		}

		$this->aQ[$name] = $val;

	}

After this method is run, I have $this->aQ array which has only the allowed var => value pairs

Also, I have other methods that validates some values even more. For example, all vars that I expect to be numeric are ending with ‘id’, like userid, threadid, etc.

Then I can throw exception if a such var is not numeric.
Some vars that serve as boolean flags, end with ‘flag’ and these can have only value or 1 or not be set at all.

basically every controller class can just set property
$this->aValidVars = array() and enter array of vars we expect.
presence of unexpected vars will throw exception very early in construction of the object.

I also have another similar function that examines array of $this->aRequiredVars
to make sure that required vars are present or the exception is thrown.

Yeah, as part of a framework I can see that working.

Off Topic:

print_r( $vars, 1 );
Sheesh! All these years I had never spotted that flag, am doing serialize() to inject vars into debug logging and exceptions etc.

@tmapm - thats it - thanks very much.

So now instead of simply checking a white list of values in a particular get Var you can actually filter out any unwanted vars - and at some extreme times, that can mean getting rid of vars you have set up yourself but “temporarily lost track of” (forgotten about).


$colors = array('red', 'blue', 'green' ) ;
$permitted = array( 'color', 'value' ) ;


// silently wipe out vars not required
$_GET = array_intersect_key($_GET, array_flip( $permitted );

if( !in_array( $_GET['color'], $colors ){
// abort or send away
}

Hmm, thinking about it, it’d be nice to also be able to stipulate that those GET vars MUST be there.

Going to think about that.

Just a quick one Cups, I’ve found placing GET/POST in an object which extends ArrayObject. This way, it’s easier to apply various filters (al la Intercepting Filter).

<?php
class Get extends ArrayObject
{
    public function __construct($get = array()){
        parent::__construct($get);
    }
    
    public function has($key){
        return $this->offsetExists($key);
    }
    
    public function get($key){
        if($this->has($key)){
            return $this->offsetGet($key);
        }
    }
    
    public function set($key, $value){
        $this->offsetSet($key, $value);
    }
}

Additionally, the Specification Pattern may help you for mandatory vars.

class BlogSpecification implements SpecificationInterface
{
    protected
        $required = array('blog-title');
        
    public function isSatisfiedBy(Get $get){
        foreach($this->required as $required){
            if(false === $get->has($required)){
                return false;
            }
        }
        return true;
    }
}

Say these are your incoming GET vars.


$_GET['id'] = 23 ;
$_GET['titles'] = array("title one", "title two") ;
$_GET['sql'] = array("bad stuff1", "badstuff2") ;


$preserve=array('id', 'titles' );

$_GET = array_intersect_key($_GET, array_flip($preserve));

if( $a = array_diff( $preserve, array_keys($_GET) ) )
  exit('Did not get: ' . print_r( $a , 1 ));

var_dump( $_GET );

The output silently wipes out those vars not expected.
Output;


array
  'id' => int 23
  'titles' => 
    array
      0 => string 'title one' (length=9)
      1 => string 'title two' (length=9)

if you change the $preserve array to include a new var, one which will be missing from the incoming GET vars:


$preserve=array('id', 'titles' , 'color');

Then the operation stops and output is;


Did not get: Array ( [2] => color ) 

Which seems handy,

To be honest I am not entirely sure where I am going with this idea, I just wanted a line or 2 to help me debug - and a bonus maybe leave something in place to wipe out any unwanted attention.

But my brain is telling me I could be doing something like:


if( $DEBUG ){
 include 'checkVars.php' ;
 checkvars( $_GET, array('id', 'title') ) ;
}

Edit:

Ah, Anthony, hadnt seen your post … leaving this all the same.

…and if any of those variables are empty, or not the required length, type, range…

I guess my point is, using the specifications, you save yourself from an inane amount of ‘down-the-line’ checking of these incoming vars. You’d pass your request object to the router, which in turn would iterate through the registered routes looking for a suitable candidate. The routes would implement the specification pattern, which would automatically validate the vars how you see fit.

$route->isSatisfiedBy($request); #if it has an id field which successfully casts to positive int and the controller is defined as ‘blog’.

Where you add the logging element is upto you, but as usual, the OO aspect provide you greater control/flexibility through a standard interface.

Because I’m bored. :wink:

php_value variables_order "S"
<pre><?php

if(
	preg_match('#^(?:.+&)?a=(?P<a>[^&]+)(?:&.+)?&b=(?P<b>[^&]+)(?:&.+)?$#', $_SERVER['QUERY_STRING'], $_GET) ||
	preg_match('#^(?:.+&)?b=(?P<b>[^&]+)(?:&.+)?&a=(?P<a>[^&]+)(?:&.+)?$#', $_SERVER['QUERY_STRING'], $_GET)
)
{
	print_r($_GET);
}
else
{
	print_r($_GET);
	echo "FAIL: {$_SERVER['QUERY_STRING']}";
}

?></pre>
/?a=2&c=3

Array
(
)
FAIL: a=2&c=3
/?a=1&b=2&c=3

Array
(
    [0] => a=1&b=2&c=3
    [a] => 1
    [1] => 1
    [b] => 2
    [2] => 2
)

:lol:

Ingenious, however equally cringe-worthy. :stuck_out_tongue:

I do think the place to start is that variables_order though. No sense in parsing a bunch of variables if you’re not going to use them. :slight_smile:

@Anthony - thanks, I am familiar with the specification pattern, but tbh I just find that a bridge too far for many of the apps I work on.

With PHP filters and PDO I do a whole lot less worrying about tainted stuff, I know just enough to know that I may well be that I rescind these remarks though.

(if anyone is interested in reading about the specification pattern then I can thoroughly recommend PHPA Guide to PHP Design Patterns by sweatj - especially if you want to get into unit testing and are moving from PHP4 to PHP5 - its an old book now, but cheap).

@joebert - you must be.

Though I cant quite work out why it fails to show $c.

That is… a monstrosity… but also has a cool factor!

You win an internet.

The main thing in play here is setting “variables_order” either in a per_dir php.ini or via htaccess like I did.
By setting it to “S” only the $_SERVER super global is being populated. $_GET, $_POST, $_COOKIE, and $_ENV are all skipped, the processor never tries to populate them.

Technically, $_GET doesn’t exist at all until one of those preg_match calls are executed. Because of the patterns I used in preg_match, the resulting $_GET can only ever contain the numerical indexes for each match, and the named sub matches. I only accounted for a= and b= in the example, but I left the pattern open so that other variables could be present, but ignored.

While .NET allows for having an OR ( | ) type expression and multiple named sub-captures with the same name, Perl compatible expressions and PHP do not. If you tried to combine the two preg_match expressions into one pattern using an OR to account for the order the variables appear in the querystring, you would get a compilation error for preg_match.

I could alter the <a> and <b> in my patterns to say, <one> and <two>, and I could use the same querystring as in the example, but the corosponding entries in $_GET would be $_GET[‘one’] and $_GET[‘two’] instead of $_GET[‘a’] and $_GET[‘b’].

Change the variables order, honestly, a trick I never thought of pulling. So kill off all GET vars, and use a regex to filter the query string.

Thanks for explaining.

I wonder what other filtering there would point in doing in the .htaccess file too?

I wonder what other filtering there would point in doing in the .htaccess file too?

I tried, but I can’t figure out what you’re asking there.

I dont really understand why this is even an issue?

If the application is developed correctly why would you even care if someone tried sending over $_GET[‘override’], $_POST[‘crash’], $_COOKIE[‘i_am_admin’] ?

After all the application will not use any other information than what it has been developed to use, any additional values will be ignored.

There is even a downside with doing something like this (depending on the method used), what if I send you an $_POST request with thousands of variables? By using most of those suggested here, it would slow down the script, while if you did not “care” as they do not affect the script anyway (if its developed well) all it does is slow down initial population of $_POST and use up memory.

Yes, it is not really necessary, but you just want to stop hackers/crachers dead in their track. Someone tries to stuff extra var in the GET or POST request and they will see big red error message, they will get the idea that, hey, this website takes security seriously, better try some other site.

I think it does not make sense to just filter out the extra GET/POST params unless you also show error message and stop all further processing at this early stage in initialization.