Remove all array elements except what I want?

PhpArrays

Php Problem Overview


I have controller that takes post parameters from HTML form, it will then send them to model that will insert the array into Cassandra database.

It is SQLInjection proof, because it's NoSQL, however what I'm afraid is that user can just simulate 100k post parameters or just add some that I don't need and it will be inserted into database. How can I make sure that only the values I need will stay in my array.

Example:

$post = ['parent_id', 'type', 'title', 'body', 'tags']; // Good
$post = ['parent_id', 'type', 'title', 'body', 'tags', 'one', 'two', 'three'] // Bad

How do I make sure that my array will unset all the elements that are not in good example?

Php Solutions


Solution 1 - Php

By whitelisting the entries you do expect.

<?php
$post = array( 
	'parent_id' => 1,
	'type' => 'foo', 
	'title' => 'bar', 
	'body' => 'foo bar', 
	'tags' => 'foo, bar', 
	'one' => 'foo',
	'two' => 'bar',
	'three' => 'qux'
);

$whitelist = array(
	'parent_id',
	'type',
	'title',
	'body',
	'tags'
);

$filtered = array_intersect_key( $post, array_flip( $whitelist ) );

var_dump( $filtered );

Anyway, using Cassandra as a data-store is of course not a reason not to do validation on the data you're receiving.

Solution 2 - Php

You are looking for array_intersect:

$good = ['parent_id', 'type', 'title', 'body', 'tags'];
$post = ['parent_id', 'type', 'title', 'body', 'tags', 'one', 'two', 'three'];

print_r(array_intersect($good, $post));

See it in action.

Of course this specific example does not make much sense because it works on array values, but there is also array_intersect_key that does the same based on keys.

Solution 3 - Php

What about multidimensional array? I was researched for a couple of hours for this solution, nowhere found an optimal solution. so, i wrote it by myself

function allow_keys($arr, $keys)
    {
        $saved = [];

        foreach ($keys as $key => $value) {
            if (is_int($key) || is_int($value)) {
                $keysKey = $value;
            } else {
                $keysKey = $key;
            }
            if (isset($arr[$keysKey])) {

                $saved[$keysKey] = $arr[$keysKey];
                if (is_array($value)) {

                    $saved[$keysKey] = allow_keys($saved[$keysKey], $keys[$keysKey]);
                }
            }
        }
        return $saved;
    }

use: example

$array = [
        'key1' => 'kw',
        'loaa'=> ['looo'],
        'k'    => [
            'prope' => [
                'prop'  => ['proo', 'prot', 'loolooo', 'de'],
                'prop2' => ['hun' => 'lu'],
            ],
            'prop1' => [

            ],
        ],
    ];

call: example

allow_keys($array, ['key1', 'k' => ['prope' => ['prop' => [0, 1], 'prop2']]])

output:

Array ( [key1] => kw [k] => Array ( [prope] => Array ( [prop] => Array ( [0] => proo [1] => prot ) [prop2] => Array ( [hun] => lu ) ) ) ) 

so you get only needed keys from the multidimensional array. it is not limited only for "multidimensional", you can use it by passing an array like

['key1', 'loaa']

output you get:

Array ( [key1] => kw [loaa] => Array ( [0] => looo ) )

cheers!

Solution 4 - Php

This will output the same as $post_allowed. What it does is only allow the values in $post_input that are also present in $post_allow.

$post_allowed = ['parent_id', 'type', 'title', 'body', 'tags'];
$post_input   = ['parent_id', 'type', 'title', 'body', 'tags', 'one', 'two', 'three'];
$post = array_intersect($post_input, $post_allowed);

Solution 5 - Php

This is called white listing, your example is misleading as the $_POST is an association array.

$post = [
    'parent_id' => 'val',
    'type' => 'val',
    'title' => 'val',
    'body' => 'val',
    'tags' => 'val',
    'one' => 'val',
    'two' => 'val',
    'three'=>'val',
];

$whitelist = ['parent_id', 'type', 'title', 'body', 'tags'];

$sanitized_post = array_whitelist_assoc($post, $whitelist);

This is a whitelisting function I created for associative arrays.

if(!function_exists('array_whitelist_assoc')){

    /**
     * Returns an associative array containing all the entries of array1 which have keys that are present in all the arguments when using their values as keys.
     *
     * @param array $array The array with master keys to check.
     * @param array $array2 An array to compare keys against its values.
     * @return array $array2,... A variable list of arrays to compare.
     * 
     */

    function array_whitelist_assoc(Array $array1, Array $array2) {

        if(func_num_args() > 2){
            $args = func_get_args();
            array_shift($args);
            $array2 = call_user_func_array('array_merge', $args);
        } 
        return array_intersect_key($array1, array_flip($array2)); 
    }
}

Solution 6 - Php

In case you’re dealing with associative arrays and you don’t want to use array_intersect_key() for any reason, you can also do a simpler approach of manually build a new array using the values you want from the old one.

$post = array(
    'parent_id' => 1,
    'type' => "post",
    'title' => "Post title",
    'body' => "Post body",
    'tags' => "Post tags",
    'malicious' => "Robert'); DROP TABLE students;--"
);
$good = array(
    'parent_id' => $post['parent_id'],
    'type' => $post['type'],
    'title' => $post['title'],
    'body' => $post['body'],
    'tags' => $post['tags']
);

Solution 7 - Php

Its worth remembering that while array_intersect and array_intersect_key are good they might well be overkill. In my situation I only wanted 1 element left, therefore the simplest option was just to rebuild the array I wanted based on the key/values I needed. I wonder at what point therefore the array_intersect's don't become worth it and you are simply better off with $new = array('whatI'=>'want');. I believe in the OP this is worth it but in smaller cases it might be overkill.

Alternatively in response to the original question simply using unset might have been a cheaper option - unset($post['one'],$post['two'],$post['three']). Again though, it relates to the point at which this becomes too inefficient and the array_intersect functions are better.

Solution 8 - Php

Use array intersection. array intersect, it will help you.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionStanView Question on Stackoverflow
Solution 1 - PhpBerry LangerakView Answer on Stackoverflow
Solution 2 - PhpJonView Answer on Stackoverflow
Solution 3 - PhpGeorgy SharapovView Answer on Stackoverflow
Solution 4 - PhpChristian RiesenView Answer on Stackoverflow
Solution 5 - PhpTarranJonesView Answer on Stackoverflow
Solution 6 - PhpdanillonunesView Answer on Stackoverflow
Solution 7 - PhpAntonyView Answer on Stackoverflow
Solution 8 - Phpime.devdesksView Answer on Stackoverflow