22/12/2013

How to solve the max_input_vars limitation

max_input_vars

Josep Sanz

Since PHP 5.3.9, has been added max_input_vars configuration directive which limits the number of variables that can get for $_GET, $_POST and $_COOKIE. This has led to applications using forms with many input type checkbox / hidden / text, may have problems in receiving data loss from the server.

This directive basically mitigates the possibility of denial of service attacks using hash collisions. Therefore, it may happen that in some hostings this variable exists and is restricted to its default value (1000) and have no option to increase the value. If this happens, you must either:
  • Rethinking application not to exceed the maximum number of variables (some really expensive in some cases)
  • Use the technique that I will describe below will solve this problem in a transparent manner without touching anything on the server side.
The technique is to anticipate the server, ie, if we know in advance the limitation may be the server that will receive the form with more than N variables, we can do the following:

1) From a php, inform the client about the value of the variable max_input_vars:

<?php
function ini_get_max_input_vars() {
	return '".intval(ini_get("max_input_vars"))."';
}
?>

2) From the client, before sending the form, add this code:

<script>
var max_input_vars=ini_get_max_input_vars();
if(max_input_vars>0) {
	var total_input_vars=$("input,select,textarea",jqForm).length;
	if(total_input_vars>max_input_vars) {
		var fix_input_vars=new Array();
		$("input[type=checkbox]:not(:checked):not(:visible)",jqForm).each(function() {
			if(total_input_vars>=max_input_vars) {
				$(this).remove();
				total_input_vars--;
			}
		});
		$("input[type=checkbox]:checked:not(:visible)",jqForm).each(function() {
			if(total_input_vars>=max_input_vars) {
				var temp=$(this).attr("name")+"="+rawurlencode($(this).val());
				fix_input_vars.push(temp);
				$(this).remove();
				total_input_vars--;
			}
		});
		$("input[type=hidden]",jqForm).each(function() {
			if(total_input_vars>=max_input_vars) {
				var temp=$(this).attr("name")+"="+rawurlencode($(this).val());
				fix_input_vars.push(temp);
				$(this).remove();
				total_input_vars--;
			}
		});
		fix_input_vars=base64_encode(utf8_encode(implode("&",fix_input_vars)));
		$(jqForm).append("<input type='hidden' name='fix_input_vars' value='"+fix_input_vars+"'/>");
	}
}
</script>

3) Since php has to receive the data, add this code before you start processing the request:

<?php
if(intval(ini_get("max_input_vars"))>0) {
	$temp=getParam("fix_input_vars");
	if($temp!="") {
		$temp=querystring2array(base64_decode($temp));
		if(isset($_GET["fix_input_vars"])) {
			unset($_GET["fix_input_vars"]);
			$_GET=array_merge($_GET,$temp);
		}
		if(isset($_POST["fix_input_vars"])) {
			unset($_POST["fix_input_vars"]);
			$_POST=array_merge($_POST,$temp);
		}
	}
}
?>

Notes:
  • The concept is:
    • Report to the client the max_input_vars server value
    • From the client, proceed to reduce the variables if necessary until the condition that the number of variables is less than the maximum.
    • Thus, it can add an auxiliary variable which will contain the other variables embedded
    • From reception, detect this case and do the reverse step to leave everything as it has to be
  • In the code snippet 2:
  • In the code snippet 3:
    • The functions getParam y querystring2array are functions provided by the SaltOS project (see the file php/strutils.php)
  • For more info, you can look at the source code of SaltOS that incorporates this trick.

XML lines
60,895
PHP lines
18,637
JS lines
11,611
XSLT lines
2,498
CSV lines
1,919
CSS lines
577