Never Use $_GET Again
You don’t need to use $_GET
or $_POST
anymore. In fact, you probably shouldn’t use $_GET
and $_POST
anymore. Since PHP 5.2, there is a new and better way to safely retrieve user-submitted data.
How many times have we heard about security issues in PHP applications stemming from unescaped GET and POST parameters? Proper escaping of input is a perennial problem with web development in general, and for whatever reason PHP seems to have had more than its fair share of bad publicity on this front.
On the database side, many worries over SQL injection have been squelched. The clever developers of PDO, for example, have constructed a library that analyzes data and escapes it appropriately. But the problem of validating and sanitizing input is still a substantial issue. To my surprise, many seasoned PHP developers still spend precious development cycles building custom code to filter input.
Why is this surprising? Because PHP (from 5.2 onward) has a built-in filtering system that makes the tasks of validating and sanitizing data trivially easy. Rather than accessing the $_GET
and $_POST
superglobals directly, you can make use of PHP functions like filter_input()
and filter_input_array()
. Let’s take a quick look at an example:
<?php $my_string = filter_input(INPUT_GET, ‘my_string’, FILTER_SANITIZE_STRING); ?>
The code above is roughly the equivalent of retrieving $_GET[‘my_string’]
and then running it through some sort of filter that strips HTML and other undesirable characters. This represents data sanitization, one of the two things that the filtering system can do. These are the two tasks of the filtering system:
- Validation: Making sure the supplied data complies with specific expectations. In this mode, the filtering system will indicate (as a boolean) whether or not the data matches some criterion.
- Sanitizing: Removing unwanted data from the input and performing any necessary type coercion. In this mode the filtering system returns the sanitized data.
By default, the filter system provides a menagerie of filters ranging from validation and sanitization of basic types (booleans, integers, floats, etc.) to more advanced filters which allow regular expressions or even custom callbacks.
The utility of this library should be obvious. Gone are the days of rolling our own input checking tools. We can use a standard (and better performing) built-in system.
But I would take things one step further than merely presenting this as an option. I would go so far as to say that we should no longer directly access superglobals containing user input. There is simply no reason why we should. And the plethora of security issues related to failure to filter input provides more than sufficient justification for my claim. Always use the filtering system. Make it mandatory.
“But,” one might object, “what if I don’t want my data filtered?” The filtering system provides a null filter (FILTER_UNSAFE_RAW
). In cases where the data needn’t be filtered (and these cases are rare), one ought to use something like this:
<?php $unfiltered_data = filter_input(FILTER_GET, ‘unfiltered_data’, FILTER_UNSAFE_RAW); ?>
I don’t suggestion this out of madness or fanaticism. Following this pattern provides a boon: I can very quickly discover all of the unfiltered variables in my code by running a simple find operation looking for the FILTER_UNSAFE_RAW
constant. This is much easier than hunting through calls to $_GET
to find those that are not correctly validated or sanitized. Risky treatment of input can be managed more efficiently by following this pattern.
Filters won’t solve every security-related problem, but they are a tremendous step in the right direction when it comes to writing safe (and performant) code. It’s also simpler. Sure, the function call is longer, but it relieves developers of the need to write their own filtering systems. These are darn good reasons to never use $_GET
(or $_POST
and the others) again.
Leave a comment
Use the form below to leave a comment:
Responses and Pingbacks
July 8th, 2010 at 1:32 pm
thanks… good info and reason enough to go to 5.2….
July 8th, 2010 at 1:42 pm
The one filter I would stay away from is validate_url. validate_url uses parse_url which says, in its documentation, “This function is not meant to validate the given URL”.
In testing I have found that it does not validate a url properly. Invalid urls do pass in some cases.
July 8th, 2010 at 2:00 pm
Very very cool stuff. I had no idea that this even existed.
July 9th, 2010 at 4:27 am
To be honest, it’s the first time I heard / read about this function and I’m a bit stunned. This is just such an easy way to solve a thing that as a developer you have to deal with in every project.
I’m also a bit confused why this function hasn’t already find it’s way into the Zend Framework. Anyhow, nice article!
July 9th, 2010 at 10:23 am
Cool article Matt! Curious though…in the two examples you are emphasizing the difference of the filtering options (FILTER_SANITIZE_STRING v FILTER_UNSAFE_RAW), but two things I noticed:
1. The first example uses INPUT_GET and the second uses FILTER_GET, but you don’t explain those differences.
2. You say you don’t need to use POST anymore, but you don’t offer a similar example. Granted the documentation probably elucidates this concept better, I was just curious if you had a good example for POST?
Thanks!
July 9th, 2010 at 11:26 am
That’s just wrong. You’re supposed to escape your data right before printing it/inserting into DB etc.
What will you do if you want to send submitted data in plain text email – deescape it? Or send it with with html entities? No. Use e.g. PDO – it will take are of proper escaping in the right time. Use some templating system which will take care of proper escaping in the right time.
July 9th, 2010 at 11:27 am
Excellent post!
However I second Adam on his comment… Could you provide a little bot more detailed explaination ? Thanks
July 9th, 2010 at 11:53 am
I’m informing you of the following project, as a public service: http://github.com/funkatron/inspekt
July 9th, 2010 at 12:18 pm
Thanks for this great tip!
Still, I haven’t used $_GET or $_POST for quite a while now anyway. I trust frameworks to handle all that stuff for me.
Still, this will come in handy when I have to write something quick in vanilla PHP.
Cheers!
July 9th, 2010 at 1:44 pm
Very cool! I never came across this. Awesome, thank you!
July 9th, 2010 at 5:59 pm
@Adam: FILTER_GET must be a typo. filter_input() allows INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV as type, see http://www.php.net/manual/en/function.filter-input.php.
Using POST works just the same, just use INPUT_POST instead of INPUT_GET.
July 9th, 2010 at 7:08 pm
How do you test code that depends on values that come from filter_input()? (i.e. how do you arrange for filter_input() to return values of your own choosing?)
July 10th, 2010 at 5:48 am
FILTER_GET parameter seems to be a typo actually. Options are : INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER or INPUT_ENV.
source : http://www.php.net/filter_input
July 12th, 2010 at 2:40 am
Good post. Before using any filter functions at all please write a short testcase to see if the function is really doing what you are expecting. In the beginning you will find that some results are not what you expected to get. I have written a post about this subject too http://www.sjoerdmaessen.be/2010/06/03/the-filter-functions-family-input-validation/
July 12th, 2010 at 2:46 am
[…] Use $_GET Again عنوان همین مطلب در این سایت است که این مطلب رو تشریح کرده… دستهها:PHP برچسبها: PHP […]
July 12th, 2010 at 3:08 am
Thanks, it was really nice article.
July 12th, 2010 at 10:09 am
Haven’t used this function before now, but from reading this article and the docs on php.net I’m highly skeptical about its overall effectiveness on security. At least if you do use it, you really need to think about your data and where its going, don’t just blindly use it everywhere and forget about it. So doesn’t that defeat what its appeal is?
No doubt now there are numerous developers that will use it this way because “some senior developer on a blog said so”. To those that do this. Don’t. There is no magic security bullet. You have to do the work to secure your software.
July 12th, 2010 at 8:42 pm
Wow, that is a nice feature.
Good Article.
July 14th, 2010 at 8:06 am
[…] on July 14, 2010 Posted Under: Link del díaGracias a @Analton llegué a un artículo llamado Never user $_GET again, que habla de cómo ya no deberíamos utilizar más las archi-conocidas variables globales de GET y […]
July 14th, 2010 at 12:51 pm
[…] na minha jornada rumo a Certificação PHP, me deparei com um artigo na PHP Architect, que exemplificava a utilização das funções filter_input e filter_input_array. Tais funções […]
July 14th, 2010 at 3:15 pm
@a: Don’t confuse *validating* and *sanitizing* data with *encoding* data for output.
Validating: Our interest is in verifying that input followed specifications. e.g. We want to know that submitted string contains only digits.
Sanitization: We want to remove data that does not follow the specification. For example, we might want to strip any letters out of a string that is supposed to contain only digits.
Escaping/Encoding: (Roughly) we want to prepare data in such a way that it abides by rules of another system. HTML entity encoding and database escaping are two examples of this.
Yes, there is sometimes overlap in the algorithms that do these things. They are, however, discrete tasks. Incidentally, you can use the filter system to filter variables (`filter_var()`). Thus, it might prove useful for escaping or encoding in some situations.
@Chris, you are correct. FILTER_GET was a typo, and should have been INPUT_GET. Mea culpa.
July 17th, 2010 at 1:04 pm
[…] Never Use $_GET Again | php|architect. […]
July 19th, 2010 at 9:52 am
Hello
Its not surprising this function is not much used yet because of the slow adoption of PHP 5.0 and >. Not even talking about rewriting older apps 🙂
Still, its a very nice step towards fully integrated security functions into PHP core.
July 19th, 2010 at 10:29 am
[…] friend of mine just sent me this link about built in validation/sanitizing with php […]
July 25th, 2010 at 12:59 pm
[…] http://www.phparch.com/2010/07/08/never-use-_get-again/ […]
August 4th, 2010 at 1:09 pm
Hi ,
I have used this occasionally but still I am filter input by my custom code.
You have struck me now.
Thanks for your post.
August 27th, 2010 at 9:08 pm
To tell the truth, I’m not very familiar with PHP. But I really learn something useful from your article.
August 29th, 2010 at 10:23 am
Thanks Matt. I will look into this more in apps where I am not using a framework that has its own filtering. However, this does lead me to research if the said frameworks are using this.
January 23rd, 2011 at 5:19 pm
[…] 今晚上phparch扒文,看到一篇Never Use $_GET Again. […]
February 10th, 2011 at 6:20 pm
Hello Matt I’ve just tried the above.
But, in my application it behaved “odd”, the snippet that works gives the possibility
to show all items by clicking “Show All”. The snippet that didn’t worked showed all items on page load.
number_of_rows > $max_rows_to_display){
echo ‘‘ . TEXT_DISPLAY_ALL . ‘‘;
}
// This did not work:
$max_rows_ini_display = MAX_DISPLAY_PRODUCTS_NEW;
$max_rows_to_display = MAX_DISPLAY_PRODUCTS_NEW;
$display = filter_input(INPUT_GET, ‘display’, FILTER_SANITIZE_STRING);
if ($display = ‘all’){
$max_rows_to_display = 100;
} else {
$max_rows_to_display = $max_rows_ini_display;
}
if(($display != ‘all’) && $listing_split->number_of_rows > $max_rows_to_display){
echo ‘‘ . TEXT_DISPLAY_ALL . ‘‘;
}
?>
So, what am I doing wrong?
February 14th, 2011 at 4:19 pm
Sara,
if ($display = ‘all’)
should be
if ($display == ‘all’)
If that doesn’t fix it, I’d try to check the value of $display and see what the filter is returning.
May 26th, 2011 at 12:36 pm
thanks very much for these usefull information .
IF you could send me a source code to handle security with $_GET and $_POST before php 5.0 it would be nice because support of php 5.0 is rare.
Even thought this is a great article .
October 17th, 2011 at 12:26 pm
[…] cannot use $_GET and $_POST superglobals. OK we can use then but we shouldn’t use them. Normally web frameworks do this work for us, but not all is a […]
November 7th, 2011 at 1:44 pm
[…] we don’t use $_POST $_GET superglobals directly. We need to filter the input. Because of that I wrote […]
March 6th, 2015 at 12:06 am
[…] As far as I can see, filter_* functions are the best way (even better than WP core functions, in my opinion) to handle validation, filtering and sanitation. Also, they are part of PHPNG (therefore, PHP 7) and everywhere I can see posts saying to use these functions (here in SO, as well as elsewhere). […]
March 8th, 2015 at 3:32 am
[…] As far as I can see, filter_* functions are the best way (even better than WP core functions, in my opinion) to handle validation, filtering and sanitation. Also, they are part of PHPNG (therefore, PHP 7) and everywhere I can see posts saying to use these functions (here in SO, as well as elsewhere). […]
December 1st, 2016 at 4:11 am
[…] Articolo tradotto de me dal post di Matt Butcher su php architect con il titolo Never Use $_GET Again. […]
March 28th, 2017 at 10:34 pm
[…] There good info at this old article Never use $_GET again. […]