Is it possible to trigger an error when $_POST or another superglobal is accessed?
Asked Answered
C

4

5

I have this framework project kind of on the backburner where I want to enforce the use of an Input class to access all superglobals like $_POST, $_GET and $_SERVER. A recent question here reminded me about it.

The class will do a little cleanup of the keys to make sure there's nothing malicious or unexpected, and provide a way to access items without the hassle of using isset() each time. It may do other things depending on configuration, and will possibly clear the superglobals as well. I also don't like the fact that superglobals are not read-only, I want to enforce integrity in the values. I want this class to be used exclusively, and want to warn developers when it is not used.

My question is this, and I fear the answer is "No":

Is it possible to to trigger an error when one of the superglobals is accessed? For example:

$myvar = $_POST['key'];
// Prints "Error: POST cannot be accessed directly, use the Input class instead"

Or when writing to superglobals?:

$_POST['key'] = 'myvalue';
// Prints "Error: POST data cannot be modified"
Champollion answered 18/10, 2012 at 12:27 Comment(0)
N
6

You can use ArrayAccess

Example 1 :

$_POST = new SUPER($_POST);
$_POST['hello'] = "Hello World"; // This would trigger error ;

Example 2 : a.php?var=1&var2=2

$_GET = new SUPER($_GET);
echo $_GET['var'] ; // returns 1
echo $_GET['var2'] ; // returns 2

$_GET['var3'] = 2 ; //return error

Class Used

class SUPER implements \ArrayAccess {
    private $request = array();

    public function __construct(array $array) {
        $this->request = $array;
    }

    public function setRequest(array $array) {
        $this->request = $array;
    }

    public function offsetSet($offset, $value) {
        trigger_error("Error: SUPER GLOBAL data cannot be modified");
    }

    public function offsetExists($offset) {
        return isset($this->request[$offset]);
    }

    public function offsetUnset($offset) {
        unset($this->request[$offset]);
    }

    public function offsetGet($offset) {
        return isset($this->request[$offset]) ? $this->request[$offset] : null;
    }
}
Nonagon answered 18/10, 2012 at 13:3 Comment(10)
This is brilliant, but new territory for me. Everything works great except $_POST = "Hello World"; (assigning specific keys does trigger the error) - any idea?Champollion
@Wesley Thanks .. this can work for all your super globals since they are all arraysNonagon
I tried adding a __set function just for the hell of it but that did not work either, it was still possible to reassign $_POST/SUPER in its entirety. How could that be done?Champollion
I'd love to know how to accomplish that, it would be the last piece of the puzzle.Champollion
Use the setRequest method ..... :) you would be able to reassign $_POST/SUPERNonagon
I want $_POST = "Hello World"; to trigger an error, it seems to not be working even with your edit, but I'm still hacking away at it and flipping through the manual. Everything else seems to be working great!Champollion
Same issue, $_POST can still be overwritten, and I have no intent on using a $SUPER variable instead (no one will have a clue to use it). I will accept defeat if it is not a possibility. Thank you very much for your help, I will accept your answer this afternoon, but if you find an answer to that piece, please share.Champollion
$_POST is a variable name and by default it can be overwritten .... they only thing you can do is always check its Integrity with the original once ... In fact you can not even prevent some type conversionNonagon
If it is not possible, I would remove the part of your answer that suggests it is, where you say "$_POST['hello'] = "Hello World"; // This would trigger error ;"Champollion
Oh you're right, my mistake - I copied a part of your answer that was edited. Thanks so much.Champollion
N
6

How about assigning an object to the $_POST variable and using magic methods?

$_POST = new %your-class%();

Northeaster answered 18/10, 2012 at 12:41 Comment(7)
That doesn't prevent the user from overwriting $_POST in its entirety again, but interesting approach. +1Piano
Yeah this is actually quite clever! I will give this a go this afternoon.Champollion
Indeed, he could even overload the [] operator in that new class, if he's using PHP5.Noachian
@MaximeMorin: That's some pretty wild stuff, thanks so much for the link, I would have never dreamed that was possible.Champollion
I do still wonder about the overwriting aspect, as $_POST = array() would disable my previous assignment. This comment indicated that it was a possibility.Champollion
Yes but who will be overriding $_POST variable in regular code? Don't spend to time on securing your environment. In the end it is a waste of time.Northeaster
@DaveKok: Thank you for the comment but I've seen it done, even by people I respect. I don't consider this a waste of time as it has been very educational.Champollion
N
6

You can use ArrayAccess

Example 1 :

$_POST = new SUPER($_POST);
$_POST['hello'] = "Hello World"; // This would trigger error ;

Example 2 : a.php?var=1&var2=2

$_GET = new SUPER($_GET);
echo $_GET['var'] ; // returns 1
echo $_GET['var2'] ; // returns 2

$_GET['var3'] = 2 ; //return error

Class Used

class SUPER implements \ArrayAccess {
    private $request = array();

    public function __construct(array $array) {
        $this->request = $array;
    }

    public function setRequest(array $array) {
        $this->request = $array;
    }

    public function offsetSet($offset, $value) {
        trigger_error("Error: SUPER GLOBAL data cannot be modified");
    }

    public function offsetExists($offset) {
        return isset($this->request[$offset]);
    }

    public function offsetUnset($offset) {
        unset($this->request[$offset]);
    }

    public function offsetGet($offset) {
        return isset($this->request[$offset]) ? $this->request[$offset] : null;
    }
}
Nonagon answered 18/10, 2012 at 13:3 Comment(10)
This is brilliant, but new territory for me. Everything works great except $_POST = "Hello World"; (assigning specific keys does trigger the error) - any idea?Champollion
@Wesley Thanks .. this can work for all your super globals since they are all arraysNonagon
I tried adding a __set function just for the hell of it but that did not work either, it was still possible to reassign $_POST/SUPER in its entirety. How could that be done?Champollion
I'd love to know how to accomplish that, it would be the last piece of the puzzle.Champollion
Use the setRequest method ..... :) you would be able to reassign $_POST/SUPERNonagon
I want $_POST = "Hello World"; to trigger an error, it seems to not be working even with your edit, but I'm still hacking away at it and flipping through the manual. Everything else seems to be working great!Champollion
Same issue, $_POST can still be overwritten, and I have no intent on using a $SUPER variable instead (no one will have a clue to use it). I will accept defeat if it is not a possibility. Thank you very much for your help, I will accept your answer this afternoon, but if you find an answer to that piece, please share.Champollion
$_POST is a variable name and by default it can be overwritten .... they only thing you can do is always check its Integrity with the original once ... In fact you can not even prevent some type conversionNonagon
If it is not possible, I would remove the part of your answer that suggests it is, where you say "$_POST['hello'] = "Hello World"; // This would trigger error ;"Champollion
Oh you're right, my mistake - I copied a part of your answer that was edited. Thanks so much.Champollion
M
2

It does not trigger anything more than a notice, but if you first copy all your superglobals keys/values inside an object and then do:

unset($_GET,$_POST,$_SERVER);

after that, any read access to those superglobals will simply fail. To forbid writing, you can instance an object of your choice over these variables (i.e. with name $_GET, $_POST, $_SERVER). To keep these accessible via the [$key] array operator, they should be instances of objects implementing the ArrayAccess interface.

Maley answered 18/10, 2012 at 12:45 Comment(2)
I'd like the user to know the reason for the error rather than just be confused, but thanks for the idea.Champollion
Thanks for the addendum, looks like it may very well be exactly what I'm looking for, I will check it out.Champollion
F
1

This is basically what @Baba already showcased, but I've been using a similar input wrapper for a few projects. It's suited for small projects very much, but admittedly I still have to overcome usage reluctance. It simplifies sanitizing and auditing however for sure.

http://sourceforge.net/p/php7framework/svn/66/tree/trunk/php7/input.php?force=True
http://sourceforge.net/p/php7framework/wiki/input/

The ArrayAccess approach is all you need. To prevent input injection at runtime or overwriting offsetSet is enough. Though I'm just printing out notices, but still allow it.

Basically it's for sanitizing however. Any raw access to e.g. $_REQUEST["key"] will go through a default filter for example, but you can also simply invoke various filter chains at runtime:

 print $_POST->html->text["comment"];

Lately I've been allowing a restrained register_globals workaorund, localize multiple variables at once. With PHP 5.4 syntax it looks quite funny:

 extract( $_REQUEST->list->text[[ title, id, email ]] );
 // implicit undef-constant notices here ^^ of course

If you just wrap $_GET, $_POST, $_REQUEST at startup you have already accomplished your goal. The only syntactic drawback is that you can no longer use empty($_POST), all other raw array accesses are still permitted by such wrappers.

Filippa answered 18/10, 2012 at 20:54 Comment(2)
Thanks a lot, will look at the code in detail later on, but this is very cool - I never imagined it was possible. I was thinking of just using a class with static methods for reading, might as well as long as we're using "superglobals" and goofy syntax, right? Thanks a lot for sharing this. I was thinking of printing notices in dev/debug mode and maybe even as harsh as fatal errors in production mode... silent errors seem dangerous even if they are logged. No chance of preventing $_POST = 'something'; though, right?Champollion
You might be able to utilize __destruct() for the latter. But with PHPs garbage collector this will only show up way after the fact; for logging at best. Personally I do like the weird syntax as it visualizes instant sanitization and right where stuff gets accessed. For one project I also use a more rigid setting with fatal errors and exceptions for unfiltered access. So such wrappers do add some flexibility still.Filippa

© 2022 - 2024 — McMap. All rights reserved.