php[architect] logo

Want to check out an issue? Sign up to receive a special offer.

Never Use $_GET Again

Posted by on July 8, 2010

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.


Matt Butcher is a software developer and author. He has written six books and dozens of articles. He is active in numerous Open Source PHP projects, including Drupal. He is the maintainer of QueryPath (http://querypath.org), a jQuery-like library for PHP. Matt is a Senior Developer at ConsumerSearch.com, an About.com/New York Times company. His personal blog can be found at http://technosophos.com.
Tags: , ,
 

Responses and Pingbacks

thanks… good info and reason enough to go to 5.2….

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.

Very very cool stuff. I had no idea that this even existed.

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!

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!

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.

Excellent post!
However I second Adam on his comment… Could you provide a little bot more detailed explaination ? Thanks

I’m informing you of the following project, as a public service: http://github.com/funkatron/inspekt

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!

Very cool! I never came across this. Awesome, thank you!

@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.

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?)

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

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/

[…] Use $_GET Again عنوان همین مطلب در این سایت است که این مطلب رو تشریح کرده… دسته‌ها:PHP برچسب‌ها: PHP […]

Thanks, it was really nice article.

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.

Wow, that is a nice feature.
Good Article.

[…] 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 […]

[…] 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 […]

Matt Butcher on
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.

[…] Never Use $_GET Again | php|architect. […]

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.

[…] friend of mine just sent me this link about built in validation/sanitizing with php […]

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.

To tell the truth, I’m not very familiar with PHP. But I really learn something useful from your article.

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.

[…] 今晚上phparch扒文,看到一篇Never Use $_GET Again. […]

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?

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.

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 .

[…] 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 […]

[…] we don’t use $_POST $_GET superglobals directly. We need to filter the input. Because of that I wrote […]

[…] 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). […]

[…] 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). […]

[…] Articolo tradotto de me dal post di Matt Butcher su php architect con il titolo Never Use $_GET Again. […]

[…] There good info at this old article Never use $_GET again. […]

Leave a comment

Use the form below to leave a comment: