Analysis of php character checking from in_array

Keywords: Programming SQL PHP Database github

The pit of PHP in_array

ps: Should be the pit of weakly typed languages

php document

As the name implies, in_array is to find out whether a value is in an array.

problem

Assuming the scene of the accident

The test code for an sql injection is as follows:

$type = $_GET['type'];
$types = [2,3,4,5,6];
if(!in_array($type, $types)) {
	throw new ParamsException('Parameter error');
}

$sql = sprintf('select * from test where `type` = %s', $type);

$result = $db->fetch($sql);

emm, let's take a look.

  1. Getting type from parameters
  2. Testing the Legality of type
  3. Spell sql
  4. Find data

That seems to be the case.

In practice, however, there is a risk of sql injection into this interface.

Problem analysis and search

Why is there a risk of sql injection?

ok, let's first understand what sql injection is common.

The so-called SQL injection is to cheat the server to execute malicious SQL commands by inserting the SQL commands into the query string of Web form submission or input domain name or page request. Specifically, it is the ability to inject (malicious) SQL commands into the background database engine to execute by using existing applications. It can get a database on a website with a security vulnerability by inputting (malicious) SQL statements in the web form, rather than executing the SQL statements according to the intention of the designer. [1] For example, many previous video websites leaked VIP membership passwords mostly by submitting query characters via WEB forms, which are particularly vulnerable to SQL injection attacks.

Take a chestnut:

select * from a where id = 10; normal sql

select * from a where id = 10 or 1; inject sql

In our scenario, if type = 3 becomes type = 3 or 1, then all the values are returned.

Uh huh? But didn't we check the type? How could it be injected like this?

Well, practice is the only criterion for testing truth.

$arr = [3,4,5];
$str = '3 or 1';
var_dump(in_array($str, $arr));

Print out bool(true)!!!!

Must be stunned, the original in_array check data is out of order.

Let me guess again.

$str = '3 or 1';
$int = intval($str);
echo $int; //3
var_dump($a == $int); // bool(true)

soga, originally, the test was so easy to pass. I infer that because no strong type validation is used when value ratios are performed, 3 or 1 = 3 will be used to determine that it is in an array.

Then continue to look carefully at the document and find that in_array has a third parameter, which is used to determine whether the strong type is equal (rigorous validation). But I think it's not suitable for my scenario. My scenario allows strings to be passed in.

Solve the problem

Now that the problem has been found, the solution is naturally simple.

The solution is not to mention, because in different scenarios, you will use different solutions.

The idea is basically the same, that is, to ensure that abnormal data will not be injected.

Source code concerns

github

PHP_FUNCTION(in_array)
{
	php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}

Ps: The final call to array_search is also php_search_array.

	zval *value,				/* value to check for */
		 *array,				/* array to check in */
		 *entry;				/* pointer to array entry */
	zend_ulong num_idx;
	zend_string *str_idx;
	zend_bool strict = 0;		/* strict comparison or not */

	ZEND_PARSE_PARAMETERS_START(2, 3)
		Z_PARAM_ZVAL(value)
		Z_PARAM_ARRAY(array)
		Z_PARAM_OPTIONAL
		Z_PARAM_BOOL(strict)
	ZEND_PARSE_PARAMETERS_END();

	if (strict) {
		//Strong type validation...
		//The fast_is_identical_function (value, entry) used here
	} else {
	   //It can be observed that php detects the worthwhile types of arrays first, rather than comparing them based on finding worthwhile types.
		if (Z_TYPE_P(value) == IS_LONG) {
		//long validation, in php, is shaping
			if (fast_equal_check_long(value, entry)) {
		} else if (Z_TYPE_P(value) == IS_STRING) {
			
		}//... Other types

The prototype of fast_equal_check_string is as follows

static zend_always_inline int fast_equal_check_long(zval *op1, zval *op2)
{
	zval result;
	if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
		return Z_LVAL_P(op1) == Z_LVAL_P(op2);
	}
	compare_function(&result, op1, op2);
	return Z_LVAL(result) == 0;
}

As you can see, when the two parameter types are different, compare_function is used.

The compare_function is provided by the zend Api kernel and the source code is not yet tracked, but according to official instructions, the results are the same as predicted.

For details, please check the official: Does php compare two values equally -- the comparison operator

Epilogue

Don't get out of the framework and you can't write code.

Don't just talk about new standards, you usually have to maintain a lot of old code (can't you solve it with pdo?)

Posted by guttyguppy on Mon, 14 Oct 2019 06:50:32 -0700