Write secure script code with PHP

  • 2020-05-12 02:21:19
  • OfStack

In PHP 4.2, they did away with that old practice! As I will explain in this article, the purpose of making such a change is for security reasons. We'll take a look at PHP's new approach to handling form submissions and other data, and explain why it makes your code more secure.

What's wrong with that?

Take a look at the PHP script below, which authorizes access to an Web page if the user name and password are entered correctly:
 
<?php 
//  Check the username and password  
if ($username == 'kevin' and $password == 'secret') 
$authorized = true; 
?> 
<?php if (!$authorized): ?> 
<!--  Unauthorized users will be prompted here  --> 
<p>Please enter your username and password:</p> 
<form action="<?=$PHP_SELF?>" method="POST"> 
<p>Username: <input type="text" name="username" /><br /> 
Password: <input type="password" name="password" /><br /> 
<input type="submit" /></p> 
</form> 
<?php else: ?> 
<!--  There are safety requirements HTML content  --> 
<?php endif; ?> 

OK, I'm sure about half of your readers will roll their eyes and say, "that was stupid -- I wouldn't have made that mistake!" But I guarantee a lot of readers will think, "hey, that's ok, I'll do that too!" Of course, a few people will be confused by this question (" what is PHP? ). PHP is designed as a "good and easy" scripting language that beginners can learn to use in a short time; It should also prevent beginners from making the above mistakes.
Going back to the previous question, the problem with the above code is that you can easily gain access without having to provide the correct user name and password. Only add at the end of the address bar for your browser? authorized = 1. Because PHP automatically creates a variable for each submitted value -- whether to automate a submitted form, an URL query string, or an cookie -- this sets $authorized to 1, so that an unauthorized user can also breach the security limit.
So, how do you solve this problem simply? Simply set $authorized to false by default at the beginning of the program. This problem does not exist! $authorized is a variable created entirely in the program code. But why should developers worry about variables submitted by every malicious user?

What changes have been made to PHP 4.2?

In PHP 4.2, the register_globals option in the newly installed PHP is turned off by default, so the EGPCS value (EGPCS is short for Environment, Get, Post, Cookies, Server -- this is the full range of external variable sources in PHP) is not created as a global variable. Of course, this option can also be turned on manually, but the developers of PHP recommend that you turn it off. To follow through, you need to use other methods to get these values.
Starting with PHP 4.1, the EGPCS value can be obtained from a specified array:
$_ENV -- contains system environment variables
$_GET -- contains the variables in the query string and the variables in the form submitted by GET
$_POST -- contains the variables in the form submitted as POST
$_COOKIE -- contains all the cookie variables
$_SERVER -- contains server variables, such as HTTP_USER_AGENT
$_REQUEST -- contains the entire contents of $_GET, $_POST, and $_COOKIE
$_SESSION -- contains all registered session variables
Before PHP 4.1, when the developer turned off the register_globals option (which was also considered a way to improve PHP performance), you had to get these variables with a nasty name like $HTTP_GET_VARS. These new variable names are not only short, but they also have other advantages.
First, let's rewrite the code mentioned above in PHP 4.2 (that is, turn off the register_globals option) :
 
<?php 
$username = $_REQUEST['username']; 
$password = $_REQUEST['password']; 

//  Check the username and password  
if ($username == 'kevin' and $password == 'secret') 
$authorized = true; 
?> 
<?php if (!$authorized): ?> 
<!--  Unauthorized users will be prompted here  --> 
<p>Please enter your username and password:</p> 
<form action="<?=$PHP_SELF?>" method="POST"> 
<p>Username: <input type="text" name="username" /><br /> 
Password: <input type="password" name="password" /><br /> 
<input type="submit" /></p> 
</form> 
<?php else: ?> 
<!--  There are safety requirements HTML content  --> 
<?php endif; ?> 

As you can see, all I need to do is add the following two lines to the beginning of the code:
$username = $_REQUEST['username'];
$password = $_REQUEST['password'];
Because we want the username and password to be submitted by the user, we get these values from the $_REQUEST array. Using this array gives the user the freedom to choose how to pass it: by querying the URL string (for example, allowing users to automatically enter their certificates when creating bookmarks), by submitting a form, or by using an cookie. If you want to limit the ability to submit certificates only via forms (or, more precisely, via HTTP POST requests), you can use the $_POST array:
$username = $_POST['username'];
$password = $_POST['password'];
Except for "introducing" these two variables, nothing has changed in the program code. Simply turning off the register_globals option forces developers to take a step further to understand what data is coming from external (untrusted) resources.
Note one minor problem here: the default error_reporting setting in PHP is still E_ALL & ~E_NOTICE, so if the values "username" and "password" are not committed, trying to get them from the $_REQUEST array or $_POST array does not incur any error messages. If your PHP program requires strict error checking, you'll need to add some code to check these variables first.

But does that mean more input?

Yes, in a simple program like the one above, using PHP 4.2 often increases the amount of input. But look on the bright side - your program is safer after all!
But seriously, the designers of PHP haven't completely ignored your pain. In these new arrays there is a special feature that none of the other PHP variables have; they are full global variables. How does that help you? Let's start with a 1 expansion of our example.
In order to enable multiple pages on the site to use the username/password argument, we wrote our user authentication program into an include file (protectme.php) :
 
<?php /* protectme.php */ 
function authorize_user($authuser, $authpass) 
{ 
$username = $_POST['username']; 
$password = $_POST['password']; 
//  Check the username and password  
if ($username != $authuser or $password != $authpass): 
?> 
<!--  Unauthorized users will be prompted here  --> 
<p>Please enter your username and password:</p> 
<form action="<?=$PHP_SELF?>" method="POST"> 
<p>Username: <input type="text" name="username" /><br /> 
Password: <input type="password" name="password" /><br /> 
<input type="submit" /></p> 
</form> 
<?php 
exit(); 
endif; 
} 
?> 

Now, our page will look like this:
 
<?php 
require('protectme.php'); 
authorize_user('kevin','secret'); 
?> 

< ! -- HTML content with security requirements -- >
Very simple, very clear, right? Now it's time to test your eyesight and experience -- what's missing in the authorize_user function?
It is not declared in the function that $_POST is a global variable! In php 4.0, when register_globals is enabled, you need to add 1 line of code to get the $username and $password variables in the function:
function authorize_user($authuser, $authpass)
{
global $username, $password;
...
In PHP, unlike other languages with similar syntax, variables outside functions are not automatically fetched in functions, you need to add 1 line to specify that they come from the global range, as explained above.
In PHP 4.0, when register_globals is turned off to provide security, you can use the $HTTP_POST_VARS array to get the value of your form submission, but you still need to import the array from the global scope:
function authorize_user($authuser, $authpass)
{
global $HTTP_POST_VARS;
$username = $HTTP_POST_VARS['username'];
$password = $HTTP_POST_VARS['password'];
But in PHP 4.1 and later, the special $_POST variable (and the other variables mentioned above) can be used in all scopes. This is why it is not necessary to declare the $_POST variable as a global variable in the function:
function authorize_user($authuser, $authpass)
{
$username = $_POST['username'];
$password = $_POST['password'];

How does this affect session?

The introduction of the special $_SESSION array actually helps simplify the session code. Instead of declaring the session variable as a global variable and then noting which variables are registered, you can now simply reference all of your session variables from $_SESSION['varname'].
Now let's look at another example of user authentication. This time, we use sessions to indicate that a user who continues to stay on your site has been authenticated. First, let's take a look at PHP 4.0 (with register_globals enabled) :
 
<?php 
session_start(); 
if ($username == 'kevin' and $password == 'secret') 
{ 
$authorized = true; 
session_register('authorized'); 
} 
?> 
<?php if (!$authorized): ?> 
<!--  According to HTML Form to prompt the user to log in  --> 
<?php else: ?> 
<!--  There are safety requirements HTML content  --> 
<?php endif; ?> 

Like program 1 at the beginning, this program also has a security flaw. Add it at the end of URL. authorized=1 allows you to bypass security measures to access page content directly. Developers can treat $authorized as one session variable, ignoring the fact that the same variable can easily be set through user input.
When we add our special array (PHP 4.1) and close register_globals(PHP 4.2), our program will look like this:
 
<?php 
session_start(); 
if ($username == 'kevin' and $password == 'secret') 
$_SESSION['authorized'] = true; 
?> 
<?php if (!$_SESSION['authorized']): ?> 
<!--  According to HTML Form to prompt the user to log in  --> 
<?php else: ?> 
<!--  There are safety requirements HTML content  --> 
<?php endif; ?> 

Is it easier? Instead of registering a normal variable as an session variable, you simply set the session variable (in the $_SESSION array) and use it in the same way. The program gets shorter, and there's no confusion about what variable is session!

conclusion

In this article, I explain the deeper reasons why the PHP scripting language has changed. In PHP 4.1, a special set of data was added to access external data. These arrays can be called from any range, making it easier to access external data. In PHP 4.2, register_globals is turned off by default to encourage the use of these arrays to prevent inexperienced developers from writing unsafe PHP code.

How do I know if I haven't tried?

Related articles: