PHP Constants can make your application insecure

PHP constants work in a strange way to make the language more programmer friendly. When you use a constant inside your script, and by any chance that constant is not defined before its use, php issues a Notice. Example:

<?php
echo MY_CONST;
//MY_CONST is not already defined;
?>

The output is

<br />
<b>Notice</b>: Use of undefined constant MY_CONST - assumed 'MY_CONST' in <b>/path/to/file.ext
</b> on line <b>13</b><br />MY_CONST

Notice the emboldened My_CONST that is not part of the Notice. What php did is first tried to get the value of constant MY_CONST but when it found that no constant of this name was ever defined, it issued a Notice and then used MY_CONST as a string 'MY_CONST'. At next step the same string is outputted to the browser.

When you are running your site on production server with error reporting disabled, php will simply echo the name of constant (as it did in above example) and no error Notice will be displayed to you at all. Here lies vulnerability.

It is common in many applications that when the script successfully determines the status of the user (loggedin/not-loggedin), the status is saved in a constant. See the following example:

<?php

function authentication()
{
     //Some Authentication Stuff
     //IF Successful
     define('USER_LOGGED_IN' , true );
}

function is_login()
{
     return USER_LOGGED_IN;
}

if( is_login() )
{
     include('/private_stuff');
}

authentication();

if( is_login() )
{
     //allow private access
}

if( is_login() )
{
     //SHOW Tools
}

if( is_login() )
{
     //Show username
}

?>


There are two problems in this script. First:
If the authentication is successful and USER_LOGGED_IN is set to true, everything will go fine. Call to function is_login() will happily return true and if( is_login() ) results in success. But when the authentication is failed the authentication() function has not enough explanation of what to do in case of failure. USER_LOGGED_IN constant is not defined in case of failure, and whenever is_login() is called it will behave strangely (as explaned at the top of this post) and will return a string 'USER_LOGGED_IN'. Strings other than empty ("") or "0" are true in php, so the if( is_login() ) results in if( 'USER_LOGGED_IN' ) which still results in a success. So whether or not a user is logged in, it will get access to private contents.

Second: The if condition used before the call to authentication() function, still results in success even the authentication is not yet done (this is because of the same problem as in the first case).

A simple solution to these problems may be:

<?php

authentication();

function authentication()
{
     //Some Authentication Stuff
     //IF Successful
     define('USER_LOGGED_IN' , true );
     
     //IF Not Successful
     define('USER_LOGGED_IN' , false );
}

function is_login()
{
     if( !defined( 'USER_LOGGED_IN' ) )
          authentication();
     //The above call will probabily never happen unless you forgot it elsewhere
     return USER_LOGGED_IN;
}

if( is_login() )
{
     include('/private_stuff');
}

if( is_login() )
{
     //allow private access
}

if( is_login() )
{
     //SHOW Tools
}

if( is_login() )
{
     //Show username
}

?>

No doubt there were bugs in this script, but this problem may also occur in many other cases like misspelling constants, or not defining constant at all when you think it already has been defined in some other included file. When you misspell a constant and you have error reporting disables, you hardly get any clue where the problem lies unless you use a good php editor (like in Notepad++ highlighting a word highlights all instances of the same word. This makes finding spelling mistakes easy).

Also in this case, you have to take extra care to ensure that whenever the function is_login() is called, the authentication process is already completed, or in other words you have to authenticate at the top of your script.

May be no one developing a secure application is this much stupid to make an application where an insecurity exists at such basic level. But errors and oversights do occur. Also the the scenario may be different. You might think that a constant is defined in some other file but may be that file is not included at all or the constant is not be defined as expected or not defined at all.

When you check you application under the development environment, the possibilities are limited. No matter how thoroughly you test your application, it is always possible that something unexpected occur. The parameters that you work with may work fine for you under testing environment, but when your application is open for public, it may start behaving unexpectedly.

I recommend using Variables and Arrays unless you really want/need to use constants, for example: keeping settings in constants that you never want any one else to change through out the running of the script.