views:

62

answers:

3

I have a page that creates an associative array, then passes it as a hidden value to a new PHP page. I have a foreach loop waiting to iterate through it but it coughs up an "invalid argument" error, as if the page doesn't know the value it's working with is an array (Despite "print" showing simply "Array"). This is essentially what I have:

//Hidden input to be passed to the next page
print "<input type=\"hidden\" name=\"price_list\" value=\"$item_list\">


//Code on the second page for the foreach loop
extract($_POST);
foreach($price_list as $name=>$price) {
...
}

But I am simply given "Warning: Invalid argument supplied for foreach() in /home/cut/mg299/public_html/PHP/invoice.php on line 17". The array works fine, as I can iterate on the previous page, and other values are parsed fine to this second page.

Do I need to 'reinitialise' this array value?

+2  A: 

If print on your array just outputs "Array", then I suspect that's how it's getting written to the hidden field. Try viewing source and see how that actually gets rendered to the browser. If it is, then you'll need some method of serializing and deserializing the array.

Yuliy
+3  A: 

Use serialize() and base64_encode():

print '<input type="hidden" name="price_list" value="' . base64_encode(serialize($item_list)) . '">'

and unserialize() and base64_decode():

//Code on the second page for the foreach loop

$price_list = unserialize(base64_decode($_POST['price_list']));
foreach($price_list as $name=>$price) {
...
}

serialize() converts your array to a string. base64_encode() encodes this string to make it safe for transmission via HTTP. Maybe it works without it but better to be on the safe side. In addition, your array gets a little obfuscated.

Update:

Actually I just noticed that serializing an array containing strings e.g. array('foo', 'bar') results in a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}.
In order to insert this correctly in to 'value="' . serialized_value . '"' you have to encode it, otherwise the result will be messed with the double quotes ":

value="a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}"

Update 2: Tom made a good point regarding security. You cannot make sure that $_POST['price_list'] contains the values you provided. They could be tampered by an attacker.
To make sure, that the data is valid, you can prepend some kind of secret key:

$secret_key = base64_encode('secret');

$value = $secret_key . base64_encode(serialize($item_list))

and later:

$value = $_POST['price_list'];
if(!substr($value,0,strlen($secret_key)) === $secret_key) {
    echo "Attempted attack!!";
    exit();
}
$price_list = unserialize(base64_decode(substr($value, strlen($secret_key)));
foreach($price_list as $name=>$price) {
...
}

Of course this can be improved further but should give the right idea.

Felix Kling
These comments have helped me understand the problem going on (i.e. The Array is being written just as a String of "Array"). I have attempted to serialize and then unserialize the value, but I am given the following error:"Notice: unserialize() [function.unserialize]: Error at offset 0 of 3 bytes in /home/cut/mg299/public_html/PHP/invoice.php on line 16"What would cause this?
Wintermute
I'd be wary of unserializing() user submitted data. a malicious user would then be able to instantiate arbitrary objects on the server, which could potentially cause you a problem, even though unlikely.
Tom Haigh
@Wintermute: What does your array contain?
Felix Kling
Associative Array, Strings as the Keys and Integers as the Values.
Wintermute
e.g. in this case, if you had a class which implemented `iterator` and you were directly printing out `$price`, I could maliciously construct an instance of that by sending the right string to be unserialised, then i might get to see the results of that iterator
Tom Haigh
@Tom Haigh: Good point updated my answer.
Felix Kling
@Wintermute: It should work...Do you do bas64 encoding? You have to do this if you enclose the value in `""`.
Felix Kling
Prepending a secret key is not sufficient. At this point you should also encrypt with a key only your app knows. That way, the attacker could not inject anything without you being able to detect an error.
Yuliy
+1  A: 

'

you might try:

foreach ($item_list as $item)
{
    $value = base64_encode($item);
    echo "<input type='hidden' name='price_list[]' value='$value'>";
}
dar7yl