I recently discovered that sending a variable containing an "unexpected" array (instead of a string) can cause fatal errors or other undesirable behavior. Example:
We have the following array:
$list = array( "a" => "first", "b" => "second" );
The user sends $_REQUEST["key"]
, which will be used to find an element in the list:
echo ($list[$_REQUEST["key"]] ?? null);
If $_REQUEST["key"]
is of type string
, int
, float
, bool
or null
, the script will display the found entries or nothing (= null). This is the desired behavior.
If $_REQUEST["key"]
is an array
, the script will stop with a fatal error.
Now, the obvious solution is to add thousands of type checks (is_scalar()
or !is_array()
) throughout the code. But I'm wondering if the following alternatives sound reasonable from a security perspective:
At the beginning of each request, the following script will run:
$_COOKIE = array_map(function($e) { return (is_array($e) ? json_encode($e, JSON_INVALID_UTF8_IGNORE) : $e); }, $_COOKIE); $_REQUEST = array_map(function($e) { return (is_array($e) ? json_encode($e, JSON_INVALID_UTF8_IGNORE) : $e); }, $_REQUEST); $_POST = array_map(function($e) { return (is_array($e) ? json_encode($e, JSON_INVALID_UTF8_IGNORE) : $e); }, $_POST); $_GET = array_map(function($e) { return (is_array($e) ? json_encode($e, JSON_INVALID_UTF8_IGNORE) : $e); }, $_GET);
This effectively disables sending the array to the server. If any positions in the code array are actually needed, they will be decoded manually using json_decode()
.
Is this a good idea?
P粉6681137682024-04-02 14:31:09
You manually decode each input variable to avoid manually validating each input variable. To me, this is the same annoyance, but more confusing, with worse performance, and has new bugs (e.g. json_encode()
will break if the input is not valid UTF-8) .
In general, it pays to be explicit about your application inputs. Making sure you use a set of known variables in a known state can really save time and trouble.
$key = isset($_REQUEST['key']) && is_string($_REQUEST['key']) ? $_REQUEST['key'] : null; $item = $list[$key] ?? null;
If \TypeError
is your only concern, you can wrap everything in a try/catch block.
You can use functions to avoid code duplication:
function get(string $name, ?string $default = null): ?string { if (isset($_REQUEST[$name]) && is_string($_REQUEST[$name])) { return $_REQUEST[$name]; } return $default; }
You can build a validation framework or use a third-party framework.
I do a lot of things before messing with all the inputs, just to prevent specific corner cases.