Difference between revisions of "Secure coding guidelines"
From Joomla! Documentation
(Marked this version for translation) |
(Some markup, capitalization changes and a URL correction.) |
||
Line 2: | Line 2: | ||
<translate> | <translate> | ||
<!--T:1--> | <!--T:1--> | ||
− | Joomla includes many features that help with the task of securing applications and extensions built on it. | + | Joomla includes many features that help with the task of securing applications and extensions built on it. You should always use these features if at all possible as they have been tried and tested by the many eyes of the developer community and any updates that might conceivably be required in the future will be automatically available whenever a Joomla update is applied. What follows is a description of best practice in using the Joomla API to ensure that your extensions are as secure as possible. |
− | ==Getting | + | ==Getting Data from the Request== <!--T:2--> |
{{JVer|3.x}} Starting with Joomla version 3.0, [[Retrieving_request_data_using_JInput|JInput]] should be used instead of [https://api.joomla.org/cms-3/classes/JRequest.html JRequest]. | {{JVer|3.x}} Starting with Joomla version 3.0, [[Retrieving_request_data_using_JInput|JInput]] should be used instead of [https://api.joomla.org/cms-3/classes/JRequest.html JRequest]. | ||
<!--T:3--> | <!--T:3--> | ||
− | All input originating from a user must be considered potentially dangerous and must be cleaned before being used. | + | All input originating from a user must be considered potentially dangerous and must be cleaned before being used. You should always use the Joomla [[Retrieving_request_data_using_JInput|JInput]] class to retrieve data from the request, rather than the raw $_GET, $_POST or $_REQUEST variables as the [[Retrieving_request_data_using_JInput|JInput]] methods apply input filtering by default. JInput deals with all aspects of the user request in a way that is independent of the request method used. It can also be used to retrieve cookie data and even server and environment variables. However, it is important to use the correct [[Retrieving_request_data_using_JInput|JInput]] method to ensure maximum security. It is very easy to just use the [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]] method with default parameters and ignore the fact that in many cases it is possible to apply a more stringent requirement on user input. |
<!--T:4--> | <!--T:4--> | ||
Line 15: | Line 15: | ||
<!--T:5--> | <!--T:5--> | ||
− | Using [[Retrieving_request_data_using_JInput|JInput]] also obviates the need to pay attention to the setting of magic_quotes_gpc. [[Retrieving_request_data_using_JInput|JInput]] does the right thing, regardless of whether magic_quotes_gpc is on or off. | + | Using [[Retrieving_request_data_using_JInput|JInput]] also obviates the need to pay attention to the setting of magic_quotes_gpc. [[Retrieving_request_data_using_JInput|JInput]] does the right thing, regardless of whether magic_quotes_gpc is on or off. See http://php.net/manual/en/security.magicquotes.php for further information. |
<!--T:6--> | <!--T:6--> | ||
− | When considering user input you should think about the data type you are expecting to retrieve and apply the most stringent form of [[Retrieving_request_data_using_JInput#Getting_Values|JInput]] that is applicable in each case. | + | When considering user input you should think about the data type you are expecting to retrieve and apply the most stringent form of [[Retrieving_request_data_using_JInput#Getting_Values|JInput]] that is applicable in each case. In particular, avoid the lazy approach of using [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]] as this will return an array that may contain entries that you did not expect and although each of those entries will have been cleaned, it is often the case that additional filtering could have been applied to some individual arguments. For example, the get method treats all arguments as strings, whereas it may be possible to restrict some arguments to be integers. |
<!--T:7--> | <!--T:7--> | ||
− | The first three parameters of each of the JInput get methods are the same. | + | The first three parameters of each of the JInput get methods are the same. Only the first parameter is mandatory. In general, the format is |
− | < | + | <syntaxhighlight lang="php"> |
JFactory::getApplication->input-><data-source>->get<type>( <name>, <default> ) | JFactory::getApplication->input-><data-source>->get<type>( <name>, <default> ) | ||
− | </ | + | </syntaxhighlight> |
where | where | ||
Line 51: | Line 51: | ||
| cookie || Data submitted in cookies. | | cookie || Data submitted in cookies. | ||
|- | |- | ||
− | | request || All the GET, POST and COOKIE data combined. | + | | request || All the GET, POST and COOKIE data combined. This is the default. |
|- | |- | ||
| files || Information about files uploaded as part of a POST request. | | files || Information about files uploaded as part of a POST request. | ||
Line 67: | Line 67: | ||
===Integer=== <!--T:13--> | ===Integer=== <!--T:13--> | ||
− | The following will accept an integer. | + | The following will accept an integer. An integer can include a leading minus sign, but a plus sign is not permitted. |
− | < | + | <syntaxhighlight lang="php"> |
$integer = JFactory::getApplication->input->getInt( 'id' ); | $integer = JFactory::getApplication->input->getInt( 'id' ); | ||
− | </ | + | </syntaxhighlight> |
− | will return the value of the "id" argument from the request (which by default includes all GET, POST and COOKIE data). | + | will return the value of the "id" argument from the request (which by default includes all GET, POST and COOKIE data). The default value is zero. |
− | < | + | <syntaxhighlight lang="php"> |
$integer = JFactory::getApplication->input->cookie->getInt( 'myId', 12 ); | $integer = JFactory::getApplication->input->cookie->getInt( 'myId', 12 ); | ||
− | </ | + | </syntaxhighlight> |
will return the value of the "myId" variable from a cookie, with a default value of 12. | will return the value of the "myId" variable from a cookie, with a default value of 12. | ||
− | ===Floating | + | ===Floating Point Number=== <!--T:14--> |
− | A floating point number can include a leading minus sign, but not a plus sign. | + | A floating point number can include a leading minus sign, but not a plus sign. If the number includes a decimal point, then there must be at least one digit before the decimal point. For example, |
− | < | + | <syntaxhighlight lang="php"> |
$float = JFactory::getApplication->input->getFloat( 'price' ); | $float = JFactory::getApplication->input->getFloat( 'price' ); | ||
− | </ | + | </syntaxhighlight> |
− | will return the value of the 'price' argument from the request. | + | will return the value of the 'price' argument from the request. The default is "0.0". |
− | < | + | <syntaxhighlight lang="php"> |
$float = JFactory::getApplication->input->post->getFloat( 'total', 100.00 ); | $float = JFactory::getApplication->input->post->getFloat( 'total', 100.00 ); | ||
− | </ | + | </syntaxhighlight> |
will retrieve the value of the 'total' argument from a POST request (but not a GET), with a default value of 100.00. | will retrieve the value of the 'total' argument from a POST request (but not a GET), with a default value of 100.00. | ||
− | ===Boolean | + | ===Boolean Value=== <!--T:15--> |
− | Any non-zero value is regarded as | + | Any non-zero value is regarded as true; zero is false. |
− | < | + | <syntaxhighlight lang="php"> |
$boolean = JFactory::getApplication->input->getBool( 'show' ); | $boolean = JFactory::getApplication->input->getBool( 'show' ); | ||
− | </ | + | </syntaxhighlight> |
− | will return false if the value of the 'show' argument in the request is zero, or 1 (true) if the argument is anything else. | + | will return false if the value of the 'show' argument in the request is zero, or 1 (true) if the argument is anything else. The default is false. Note that any string argument will result in a return value of true, so calling the above with a URL containing "?show=false" will actually return true! |
− | < | + | <syntaxhighlight lang="php"> |
$boolean = JFactory::getApplication->input->get->getBool( 'hide', true ); | $boolean = JFactory::getApplication->input->get->getBool( 'hide', true ); | ||
− | </ | + | </syntaxhighlight> |
will retrieve the value of the 'hide' argument from a GET request (but not a POST), with a default value of true. | will retrieve the value of the 'hide' argument from a GET request (but not a POST), with a default value of true. | ||
===Word=== <!--T:16--> | ===Word=== <!--T:16--> | ||
− | A word is defined as | + | A word is defined as a string of alphabetic characters. The underscore character is permitted as part of a word. |
− | < | + | <syntaxhighlight lang="php"> |
$word = JFactory::getApplication->input->cookie->getWord( 'search-word' ); | $word = JFactory::getApplication->input->cookie->getWord( 'search-word' ); | ||
− | </ | + | </syntaxhighlight> |
− | will retrieve the value of the 'search-word' argument from the request. | + | will retrieve the value of the 'search-word' argument from the request. The default is an empty string. |
− | < | + | <syntaxhighlight lang="php"> |
$word = JFactory::getApplication->input->cookie->getWord( 'keyword', '' ); | $word = JFactory::getApplication->input->cookie->getWord( 'keyword', '' ); | ||
− | </ | + | </syntaxhighlight> |
will retrieve the value of the 'keyword' variable from a cookie, with the default being an empty string. | will retrieve the value of the 'keyword' variable from a cookie, with the default being an empty string. | ||
===Command=== <!--T:17--> | ===Command=== <!--T:17--> | ||
− | A command is like a word but a wider range of characters is permitted. | + | A command is like a word but a wider range of characters is permitted. Allowed characters are: all alphanumeric characters, dot, dash (hyphen) and underscore. |
− | < | + | <syntaxhighlight lang="php"> |
$command = JFactory::getApplication->input->getCmd( 'option' ); | $command = JFactory::getApplication->input->getCmd( 'option' ); | ||
− | </ | + | </syntaxhighlight> |
− | will retrieve the value of the "option" argument from the request. | + | will retrieve the value of the "option" argument from the request. The default value is an empty string. |
− | < | + | <syntaxhighlight lang="php"> |
$command = JFactory::getApplication->input->post->getCmd( 'controller', 'view' ); | $command = JFactory::getApplication->input->post->getCmd( 'controller', 'view' ); | ||
− | </ | + | </syntaxhighlight> |
will retrieve the value of the "controller" argument from a POST request (but not a GET), with a default value of 'view'. | will retrieve the value of the "controller" argument from a POST request (but not a GET), with a default value of 'view'. | ||
===String=== <!--T:18--> | ===String=== <!--T:18--> | ||
− | The string type allows a much wider range of input characters. | + | The string type allows a much wider range of input characters. It also takes an optional fourth argument specifying some additional mask options. See [[#Filter options]] for information on the available masks. |
− | < | + | <syntaxhighlight lang="php"> |
$string = JFactory::getApplication->input->getString( 'description' ); | $string = JFactory::getApplication->input->getString( 'description' ); | ||
− | </ | + | </syntaxhighlight> |
− | will retrieve the value of the "description" argument from the request. | + | will retrieve the value of the "description" argument from the request. The default value is an empty string. The input will have whitespace removed from the left and right ends and any HTML tags will be removed. |
− | < | + | <syntaxhighlight lang="php"> |
$string = JFactory::getApplication->input->getString( 'text', '' ); | $string = JFactory::getApplication->input->getString( 'text', '' ); | ||
− | </ | + | </syntaxhighlight> |
− | will retrieve the value of the "text" argument from the request. | + | will retrieve the value of the "text" argument from the request. The default value is an empty string. |
− | < | + | <syntaxhighlight lang="php"> |
$string = JFactory::getApplication->input->getString( 'template', '<html />' ); | $string = JFactory::getApplication->input->getString( 'template', '<html />' ); | ||
− | </ | + | </syntaxhighlight> |
− | will retrieve the value of the "template" argument from the request. | + | will retrieve the value of the "template" argument from the request. The default value is '<html />'. |
===JSON String=== <!--T:19--> | ===JSON String=== <!--T:19--> | ||
− | < | + | <syntaxhighlight lang="php"> |
$json = JFactory::getApplication->input->json->get( 'var_name' ); | $json = JFactory::getApplication->input->json->get( 'var_name' ); | ||
− | </ | + | </syntaxhighlight> |
===Generic and other data types=== <!--T:20--> | ===Generic and other data types=== <!--T:20--> | ||
− | If the above methods do not meet your needs, there is a small number of additional filter types which you can use by calling the [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]] method directly. | + | If the above methods do not meet your needs, there is a small number of additional filter types which you can use by calling the [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]] method directly. The syntax is: |
− | < | + | <syntaxhighlight lang="php"> |
JFactory::getApplication->input->get( <name>, <default>, <type> ); | JFactory::getApplication->input->get( <name>, <default>, <type> ); | ||
− | </ | + | </syntaxhighlight> |
where: | where: | ||
Line 152: | Line 152: | ||
| <name> || the name of the variable to be retrieved (for example, the name of an argument in a URL). | | <name> || the name of the variable to be retrieved (for example, the name of an argument in a URL). | ||
|- | |- | ||
− | | <default> || the default value. | + | | <default> || the default value. There is no default value that will be returned if no default is specified in the call the [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]]. If no default is specified and the argument is not present in the request variable then it will return undefined. |
|- | |- | ||
| <type> || specifies the data type expected (see below). | | <type> || specifies the data type expected (see below). | ||
Line 158: | Line 158: | ||
<!--T:22--> | <!--T:22--> | ||
− | The first three arguments are the same as for the more specific methods described earlier. | + | The first three arguments are the same as for the more specific methods described earlier. Only the first argument is mandatory. |
<!--T:23--> | <!--T:23--> | ||
Line 187: | Line 187: | ||
| HTML || A sanitised string. Equivalent to [[Retrieving_request_data_using_JInput#Getting_Values|JInput->getHtml]]. | | HTML || A sanitised string. Equivalent to [[Retrieving_request_data_using_JInput#Getting_Values|JInput->getHtml]]. | ||
|- | |- | ||
− | | PATH || Valid pathname regex that filters out common attacks. | + | | PATH || Valid pathname regex that filters out common attacks. For example, any path beginning with a "/" will return an empty string. Simliarly, any path containing "/./" or "/../" will return an empty string. Dots within filenames are okay though. Equivalent to [[Retrieving_request_data_using_JInput#Getting_Values|JInput->getPath]]. |
|- | |- | ||
| USERNAME || Removes control characters (0x00 - 0x1F), 0x7F, <, >, ", ', % and &. Equivalent to [[Retrieving_request_data_using_JInput#Getting_Values|JInput->getUsername]]. | | USERNAME || Removes control characters (0x00 - 0x1F), 0x7F, <, >, ", ', % and &. Equivalent to [[Retrieving_request_data_using_JInput#Getting_Values|JInput->getUsername]]. | ||
Line 197: | Line 197: | ||
<!--T:26--> | <!--T:26--> | ||
Filter an array of values. | Filter an array of values. | ||
− | < | + | <syntaxhighlight lang="php"> |
$data = JFactory::getApplication()->input->post->get('data', array(), 'array'); | $data = JFactory::getApplication()->input->post->get('data', array(), 'array'); | ||
$filter = JFilterInput::getInstance(); | $filter = JFilterInput::getInstance(); | ||
Line 206: | Line 206: | ||
$array[] = $filter->clean($value, 'string'); | $array[] = $filter->clean($value, 'string'); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
For more filter types see [https://github.com/joomla/joomla-cms/blob/master/libraries/joomla/filter/input.php#L115 JFilterInput source]. | For more filter types see [https://github.com/joomla/joomla-cms/blob/master/libraries/joomla/filter/input.php#L115 JFilterInput source]. | ||
Line 217: | Line 217: | ||
| JREQUEST_NOTRIM || Does not remove whitespace from the start and ends of strings. | | JREQUEST_NOTRIM || Does not remove whitespace from the start and ends of strings. | ||
|- | |- | ||
− | | JREQUEST_ALLOWRAW || Does not do any filtering at all. | + | | JREQUEST_ALLOWRAW || Does not do any filtering at all. Use with extreme caution. |
|- | |- | ||
| JREQUEST_ALLOWHTML || Does not remove HTML from string inputs. | | JREQUEST_ALLOWHTML || Does not remove HTML from string inputs. | ||
Line 223: | Line 223: | ||
<!--T:30--> | <!--T:30--> | ||
− | Masks can be combined by logically OR'ing them. | + | Masks can be combined by logically OR'ing them. If no filter options are specified, then by default, whitespace is trimmed and HTML is removed. |
===File uploads=== <!--T:31--> | ===File uploads=== <!--T:31--> | ||
− | Web servers already have a good deal of security around handling file uploads, but it is still necessary to take additional steps to ensure that file names and paths cannot be abused. | + | Web servers already have a good deal of security around handling file uploads, but it is still necessary to take additional steps to ensure that file names and paths cannot be abused. A simplified form which requests a file to be uploaded looks like this: |
− | < | + | <syntaxhighlight lang="html4strict"> |
− | <form action="index.php?option=com_mycomponent/form_handler.php" | + | <form action="index.php?option=com_mycomponent/form_handler.php" method="post" enctype="multipart/form-data"> |
<input type="file" name="Filedata" /> | <input type="file" name="Filedata" /> | ||
<input type="submit" /> | <input type="submit" /> | ||
</form> | </form> | ||
− | </ | + | </syntaxhighlight> |
− | On clicking the submit button, the browser will upload the file in a POST request, passing control to Joomla which will call "components/com_mycomponent/form_handler.php". | + | On clicking the submit button, the browser will upload the file in a POST request, passing control to Joomla which will call "components/com_mycomponent/form_handler.php". This will include code like the following. The variable $somepath must be set to some path where the web server has permission to create files. |
− | < | + | <syntaxhighlight lang="php"> |
// Check to ensure this file is included in Joomla! | // Check to ensure this file is included in Joomla! | ||
defined('_JEXEC') or die( 'Restricted access' ); | defined('_JEXEC') or die( 'Restricted access' ); | ||
Line 259: | Line 259: | ||
JFile::upload( $file['tmp_name'], $filepath ); | JFile::upload( $file['tmp_name'], $filepath ); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
===Saving a request variable into user state=== <!--T:37--> | ===Saving a request variable into user state=== <!--T:37--> | ||
− | Because setting a user state variable from a variable in the request is such a common operation, there is an API method to make the task easier. | + | Because setting a user state variable from a variable in the request is such a common operation, there is an API method to make the task easier. This is generally safe to use because it calls [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]] to obtain the input from the request, but remember that none of the input filtering calls will protect against SQL injection attempts. |
− | < | + | <syntaxhighlight lang="php"> |
$app = JFactory::getApplication(); | $app = JFactory::getApplication(); | ||
$app->getUserStateFromRequest( <key>, <name>, <default>, <type> ); | $app->getUserStateFromRequest( <key>, <name>, <default>, <type> ); | ||
− | </ | + | </syntaxhighlight> |
where | where | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
Line 273: | Line 273: | ||
| <name> || the name of the request variable (same as the first argument of a [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]] call). | | <name> || the name of the request variable (same as the first argument of a [[Retrieving_request_data_using_JInput#Getting_Values|JInput->get]] call). | ||
|- | |- | ||
− | | <default> || the default value to be assigned to the user state variable if the request variable is absent. | + | | <default> || the default value to be assigned to the user state variable if the request variable is absent. The default is null. |
|- | |- | ||
| <type> || the type of variable expected. | | <type> || the type of variable expected. | ||
Line 279: | Line 279: | ||
<!--T:38--> | <!--T:38--> | ||
− | For example, getting an integer variable called 'id' from the request with a default value of 0, then saving it into a session variable called 'myid' can be done like this: | + | For example, getting an integer variable called ''id'' from the request with a default value of 0, then saving it into a session variable called ''myid'' can be done like this: |
− | < | + | <syntaxhighlight lang="php"> |
$app = JFactory::getApplication(); | $app = JFactory::getApplication(); | ||
$app->getUserStateFromRequest( 'myid', 'id, 0, 'int' ); | $app->getUserStateFromRequest( 'myid', 'id, 0, 'int' ); | ||
− | </ | + | </syntaxhighlight> |
instead of something like this: | instead of something like this: | ||
− | < | + | <syntaxhighlight lang="php"> |
$app = JFactory::getApplication(); | $app = JFactory::getApplication(); | ||
$app->setUserState( 'myid', $app->input->getInt( 'id', 0 ) ); | $app->setUserState( 'myid', $app->input->getInt( 'id', 0 ) ); | ||
− | </ | + | </syntaxhighlight> |
− | ==Constructing SQL | + | ==Constructing SQL Queries== <!--T:39--> |
− | One of the most common forms of attack on web applications is SQL injection, where the aim of the attacker is to change a database query by exploiting a poorly filtered input variable. | + | One of the most common forms of attack on web applications is SQL injection, where the aim of the attacker is to change a database query by exploiting a poorly filtered input variable. Injecting modified SQL statements into the database can damage data or reveal private information. It is important to ensure that when SQL statements are constructed, they are correctly escaped and quoted so that bad input data cannot result in a bad SQL statement. You cannot rely on the [[Retrieving_request_data_using_JInput|JInput]] methods to do this as they are not SQL-aware. |
===Secure integers and the rest of numeric values=== <!--T:40--> | ===Secure integers and the rest of numeric values=== <!--T:40--> | ||
− | With the MySQL database, numeric fields should not be quoted, so it is important that they be typecast instead. | + | With the MySQL database, numeric fields should not be quoted, so it is important that they be typecast instead. Failure to do this will leave your code vulnerable to an attacker inserting a string containing SQL data. |
<!--T:41--> | <!--T:41--> | ||
Depending on the type, numeric types are cast like this: | Depending on the type, numeric types are cast like this: | ||
− | < | + | <syntaxhighlight lang="php"> |
// For SQL data types: INT, INTEGER, TINYINT, SMALLINT, MEDIUMINT, BIGINT, YEAR | // For SQL data types: INT, INTEGER, TINYINT, SMALLINT, MEDIUMINT, BIGINT, YEAR | ||
$query = 'SELECT * FROM #__table WHERE `id`=' . (int) $id; | $query = 'SELECT * FROM #__table WHERE `id`=' . (int) $id; | ||
Line 305: | Line 305: | ||
// For SQL data types: FLOAT, DOUBLE | // For SQL data types: FLOAT, DOUBLE | ||
$query = 'SELECT * FROM #__table WHERE `id`=' . (float) $id; | $query = 'SELECT * FROM #__table WHERE `id`=' . (float) $id; | ||
− | </ | + | </syntaxhighlight> |
− | It's a good idea to get into the habit of always typecasting integers like this even if the variable was previously obtained using JInput->getInt. Further information on SQL injection attacks can be found | + | It's a good idea to get into the habit of always typecasting integers like this even if the variable was previously obtained using JInput->getInt. Further information on SQL injection attacks can be found at the [https://www.php.net/manual/en/security.database.sql-injection.php PHP.NET SQL Injection page] and here: [[Retrieving_request_data_using_JInput#Getting_Values]]. |
− | ===Secure | + | ===Secure Strings=== <!--T:43--> |
− | In the examples that follow it is assumed that $db is an instance of a Joomla database object. | + | In the examples that follow it is assumed that $db is an instance of a Joomla database object. This can always be obtained from [https://docs.joomla.org/Using_the_JFactory_class JFactory] using |
− | < | + | <syntaxhighlight lang="php"> |
$db = JFactory::getDBO(); | $db = JFactory::getDBO(); | ||
− | </ | + | </syntaxhighlight> |
− | Strings should always be escaped before being used in an SQL statement. | + | Strings should always be escaped before being used in an SQL statement. This is actually very simple as the [https://api.joomla.org/cms-3/classes/JDatabaseQuery.html#method_quote JDatabase->quote] method escapes everything for you. You can also use the [https://api.joomla.org/cms-3/classes/JDatabaseQuery.html#method_escape JDatabase->escape] method directly. The following statements are equivalent: |
− | < | + | <syntaxhighlight lang="php"> |
$query = 'SELECT * FROM #__table WHERE `field` = ' . $db->quote( $db->escape( $field ), false ); | $query = 'SELECT * FROM #__table WHERE `field` = ' . $db->quote( $db->escape( $field ), false ); | ||
<!--T:44--> | <!--T:44--> | ||
$query = 'SELECT * FROM #__table WHERE `field` = ' . $db->quote( $field ); | $query = 'SELECT * FROM #__table WHERE `field` = ' . $db->quote( $field ); | ||
− | </ | + | </syntaxhighlight> |
===Secure on search=== <!--T:45--> | ===Secure on search=== <!--T:45--> | ||
− | Special attention should be paid to LIKE clauses which contain the % wildcard character as these require special escaping in order to avoid possible denial of service attacks. | + | Special attention should be paid to LIKE clauses which contain the % wildcard character as these require special escaping in order to avoid possible denial of service attacks. LIKE clauses can be handled like this: |
− | < | + | <syntaxhighlight lang="php"> |
// Construct the search term by escaping the user-supplied string and, if required, adding the % wildcard characters manually. | // Construct the search term by escaping the user-supplied string and, if required, adding the % wildcard characters manually. | ||
$search = '%' . $db->escape( $search, true ) . '%'; | $search = '%' . $db->escape( $search, true ) . '%'; | ||
Line 330: | Line 330: | ||
// Construct the SQL query, being careful to suppress the default behaviour of Quote so as to prevent double-escaping. | // Construct the SQL query, being careful to suppress the default behaviour of Quote so as to prevent double-escaping. | ||
$query = 'SELECT * FROM #__table WHERE `field` LIKE ' . $db->quote( $search, false ); | $query = 'SELECT * FROM #__table WHERE `field` LIKE ' . $db->quote( $search, false ); | ||
− | </ | + | </syntaxhighlight> |
===Secure dates=== <!--T:47--> | ===Secure dates=== <!--T:47--> | ||
If data is to be entered into a datetime column then you can use the Joomla API to ensure a valid date format: | If data is to be entered into a datetime column then you can use the Joomla API to ensure a valid date format: | ||
− | < | + | <syntaxhighlight lang="php"> |
$date = JFactory::getDate( $mydate ); | $date = JFactory::getDate( $mydate ); | ||
$query = 'UPDATE #__table SET `date` = ' . $db->quote( $date->toMySQL(), false ); | $query = 'UPDATE #__table SET `date` = ' . $db->quote( $date->toMySQL(), false ); | ||
− | </ | + | </syntaxhighlight> |
Note that it is necessary to suppress database escaping as legitimate dates may contain characters that should not be escaped. | Note that it is necessary to suppress database escaping as legitimate dates may contain characters that should not be escaped. | ||
===Secure field names=== <!--T:48--> | ===Secure field names=== <!--T:48--> | ||
In the comparatively rare case where a field name is a variable, that should also be quoted using an API call: | In the comparatively rare case where a field name is a variable, that should also be quoted using an API call: | ||
− | < | + | <syntaxhighlight lang="php"> |
$query = 'SELECT * FROM #__table WHERE ' . $db->quoteName( $field->name ) . '=' . $db->quote( $field->value ); | $query = 'SELECT * FROM #__table WHERE ' . $db->quoteName( $field->name ) . '=' . $db->quote( $field->value ); | ||
− | </ | + | </syntaxhighlight> |
===Secure arrays of integers=== <!--T:49--> | ===Secure arrays of integers=== <!--T:49--> | ||
− | When you have an array of ids, typically used for IN() queries, you have to sanitise it also with JArrayHelper::toInteger($cid); before imploding: | + | When you have an array of ids, typically used for IN() queries, you have to sanitise it also with JArrayHelper::toInteger($cid); before imploding: |
<!--T:50--> | <!--T:50--> | ||
− | < | + | <syntaxhighlight lang="php"> |
... | ... | ||
$catId = JArrayHelper::toInteger($catId); | $catId = JArrayHelper::toInteger($catId); | ||
$query->where($db->quoteName('x.category_id') . ' IN (' . implode(',', $catId) . ')'); | $query->where($db->quoteName('x.category_id') . ' IN (' . implode(',', $catId) . ')'); | ||
− | </ | + | </syntaxhighlight> |
− | ===Short | + | ===Short Aliases of Quote and QuoteName=== <!--T:51--> |
− | Shorter alternatives to the quote methods may also be used. | + | Shorter alternatives to the quote methods may also be used. The following statements are equivalent: |
− | < | + | <syntaxhighlight lang="php"> |
$query = 'SELECT * FROM #__table WHERE ' . $db->quoteName( $field->name ) . '=' . $db->quote( $field->value ); | $query = 'SELECT * FROM #__table WHERE ' . $db->quoteName( $field->name ) . '=' . $db->quote( $field->value ); | ||
$query = 'SELECT * FROM #__table WHERE ' . $db->qn( $field->name ) . '=' . $db->q( $field->value ); | $query = 'SELECT * FROM #__table WHERE ' . $db->qn( $field->name ) . '=' . $db->q( $field->value ); | ||
− | </ | + | </syntaxhighlight> |
==Securing forms== <!--T:52--> | ==Securing forms== <!--T:52--> | ||
− | Apart from cleaning input variables as described above, you can also implement a simple technique which makes it more difficult for a cross-site request forgery attack (CSRF) to succeed. This involves adding a randomly-generated unique token to the form which is checked against a copy of the token held in the user's session. | + | Apart from cleaning input variables as described above, you can also implement a simple technique which makes it more difficult for a cross-site request forgery attack (CSRF) to succeed. This involves adding a randomly-generated unique token to the form which is checked against a copy of the token held in the user's session. By checking that the submitted token matches the one contained in the stored session, it is possible to tie a rendered form to the request variables presented. |
<!--T:53--> | <!--T:53--> | ||
In POST forms you should add a hidden token field using: | In POST forms you should add a hidden token field using: | ||
− | < | + | <syntaxhighlight lang="php"> |
echo JHTML::_( 'form.token' ); | echo JHTML::_( 'form.token' ); | ||
− | </ | + | </syntaxhighlight> |
This outputs the token as a hidden form field looking like this: | This outputs the token as a hidden form field looking like this: | ||
− | < | + | <syntaxhighlight lang="html4strict"> |
<input type="hidden" name="8cb24ae69ffd7828ccecbcf06056e6fc" value="1" /> | <input type="hidden" name="8cb24ae69ffd7828ccecbcf06056e6fc" value="1" /> | ||
− | </ | + | </syntaxhighlight> |
and places a copy of the token into the user's session, for later checking. | and places a copy of the token into the user's session, for later checking. | ||
<!--T:54--> | <!--T:54--> | ||
If you need to add the token to a URL rather than a form then you can use something like this: | If you need to add the token to a URL rather than a form then you can use something like this: | ||
− | < | + | <syntaxhighlight lang="php"> |
echo JRoute::_( 'index.php?option=com_mycomponent&' . JSession::getFormToken() . '=1' ); | echo JRoute::_( 'index.php?option=com_mycomponent&' . JSession::getFormToken() . '=1' ); | ||
− | </ | + | </syntaxhighlight> |
<!--T:55--> | <!--T:55--> | ||
− | In the most common scenario, you will want to check the token following a POST to the form handler. | + | In the most common scenario, you will want to check the token following a POST to the form handler. This can be done by adding this line of code to form handler: |
− | < | + | <syntaxhighlight lang="php"> |
JSession::checkToken() or die( JText::_( 'Invalid Token' ) ); | JSession::checkToken() or die( JText::_( 'Invalid Token' ) ); | ||
− | </ | + | </syntaxhighlight> |
If you need to pass the token in a GET request then you can check it like this: | If you need to pass the token in a GET request then you can check it like this: | ||
− | < | + | <syntaxhighlight lang="php"> |
JSession::checkToken( 'get' ) or die( JText::_( 'Invalid Token' ) ); | JSession::checkToken( 'get' ) or die( JText::_( 'Invalid Token' ) ); | ||
− | </ | + | </syntaxhighlight> |
<!--T:56--> | <!--T:56--> | ||
− | In both cases the code will die if the token is omitted from the request, or the submitted token does not match the session token. | + | In both cases the code will die if the token is omitted from the request, or the submitted token does not match the session token. If the token is correct but has expired, then [[JSession/checkToken|JSession::checkToken]] will automatically redirect to the site front page. |
− | ==Cleaning | + | ==Cleaning Filesystem Paths== <!--T:57--> |
− | If there is any possibility that a filesystem path might be constructed using data that originated from user input, then the path must be cleaned and checked before being used. | + | If there is any possibility that a filesystem path might be constructed using data that originated from user input, then the path must be cleaned and checked before being used. This can be done quite simply like this: |
− | < | + | <syntaxhighlight lang="php"> |
JPath::check( $path ); | JPath::check( $path ); | ||
− | </ | + | </syntaxhighlight> |
− | This will raise an error and terminate Joomla if the path contains a ".." or leads to a location outside the Joomla root directory. | + | This will raise an error and terminate Joomla if the path contains a ".." or leads to a location outside the Joomla root directory. If you want to deal with the error yourself without terminating the application, then you can use code like this: |
− | < | + | <syntaxhighlight lang="php"> |
$path = JPath::clean( $path ); | $path = JPath::clean( $path ); | ||
if (strpos( $path, JPath::clean( JPATH_ROOT ) ) !== 0) { | if (strpos( $path, JPath::clean( JPATH_ROOT ) ) !== 0) { | ||
// Handle the error here. | // Handle the error here. | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
− | The [[JPath:clean]] method can be used in your own code too. | + | The [[JPath:clean]] method can be used in your own code too. It merely removes leading and trailing whitespace and replace double slashes and backslashes with the standard directory separator. |
− | ==Cleaning | + | ==Cleaning Filesystem File Names== <!--T:58--> |
− | As with filesystem paths, if there is any possibility that a file name might be constructed using user-originated data, then the file name must be cleaned and checked before use. | + | As with filesystem paths, if there is any possibility that a file name might be constructed using user-originated data, then the file name must be cleaned and checked before use. This can be done like this: |
− | < | + | <syntaxhighlight lang="php"> |
jimport('joomla.filesystem.file'); | jimport('joomla.filesystem.file'); | ||
$clean = JFile::makeSafe( $unclean ); | $clean = JFile::makeSafe( $unclean ); | ||
− | </ | + | </syntaxhighlight> |
− | This method removes sequences of two or more "." characters and any character that is not alphabetic, numeric or a dot, dash or underscore character. | + | This method removes sequences of two or more "." characters and any character that is not alphabetic, numeric or a dot, dash or underscore character. If there is a leading dot then that is removed too. |
[[Category:Development]][[Category:Security]] | [[Category:Development]][[Category:Security]] | ||
</translate> | </translate> |
Latest revision as of 16:36, 15 October 2022
Joomla includes many features that help with the task of securing applications and extensions built on it. You should always use these features if at all possible as they have been tried and tested by the many eyes of the developer community and any updates that might conceivably be required in the future will be automatically available whenever a Joomla update is applied. What follows is a description of best practice in using the Joomla API to ensure that your extensions are as secure as possible.
Getting Data from the Request[edit]
Starting with Joomla version 3.0, JInput should be used instead of JRequest.
All input originating from a user must be considered potentially dangerous and must be cleaned before being used. You should always use the Joomla JInput class to retrieve data from the request, rather than the raw $_GET, $_POST or $_REQUEST variables as the JInput methods apply input filtering by default. JInput deals with all aspects of the user request in a way that is independent of the request method used. It can also be used to retrieve cookie data and even server and environment variables. However, it is important to use the correct JInput method to ensure maximum security. It is very easy to just use the JInput->get method with default parameters and ignore the fact that in many cases it is possible to apply a more stringent requirement on user input.
It very important to understand that the JInput methods are not SQL-aware and further work is required to guard against SQL injection attacks.There is no default value that will be returned if no default is specified in the call the JInput->get. If no default is specified and the argument is not present in the request variable then it will return undefined.
Using JInput also obviates the need to pay attention to the setting of magic_quotes_gpc. JInput does the right thing, regardless of whether magic_quotes_gpc is on or off. See http://php.net/manual/en/security.magicquotes.php for further information.
When considering user input you should think about the data type you are expecting to retrieve and apply the most stringent form of JInput that is applicable in each case. In particular, avoid the lazy approach of using JInput->get as this will return an array that may contain entries that you did not expect and although each of those entries will have been cleaned, it is often the case that additional filtering could have been applied to some individual arguments. For example, the get method treats all arguments as strings, whereas it may be possible to restrict some arguments to be integers.
The first three parameters of each of the JInput get methods are the same. Only the first parameter is mandatory. In general, the format is
JFactory::getApplication->input-><data-source>->get<type>( <name>, <default> )
where
<type> | the data type to be retrieved (see below for the types available). |
<name> | the name of the variable to be retrieved (for example, the name of an argument in a URL). |
<default> | the default value. |
<data-source> | specifies where the variable is to be retrieved from (see below). |
The following values for <data-source> are supported:
get | Data submitted in the query part of the URL. |
post | Data submitted from form fields. |
method | The same as either GET or POST depending on how the request was made. |
cookie | Data submitted in cookies. |
request | All the GET, POST and COOKIE data combined. This is the default. |
files | Information about files uploaded as part of a POST request. |
env | Environment variables (platform-specific). |
server | Web server variables (platform-specific). |
Notice that the default is REQUEST, which includes cookie data.
The following sections look at each of the data types in more detail.
Integer[edit]
The following will accept an integer. An integer can include a leading minus sign, but a plus sign is not permitted.
$integer = JFactory::getApplication->input->getInt( 'id' );
will return the value of the "id" argument from the request (which by default includes all GET, POST and COOKIE data). The default value is zero.
$integer = JFactory::getApplication->input->cookie->getInt( 'myId', 12 );
will return the value of the "myId" variable from a cookie, with a default value of 12.
Floating Point Number[edit]
A floating point number can include a leading minus sign, but not a plus sign. If the number includes a decimal point, then there must be at least one digit before the decimal point. For example,
$float = JFactory::getApplication->input->getFloat( 'price' );
will return the value of the 'price' argument from the request. The default is "0.0".
$float = JFactory::getApplication->input->post->getFloat( 'total', 100.00 );
will retrieve the value of the 'total' argument from a POST request (but not a GET), with a default value of 100.00.
Boolean Value[edit]
Any non-zero value is regarded as true; zero is false.
$boolean = JFactory::getApplication->input->getBool( 'show' );
will return false if the value of the 'show' argument in the request is zero, or 1 (true) if the argument is anything else. The default is false. Note that any string argument will result in a return value of true, so calling the above with a URL containing "?show=false" will actually return true!
$boolean = JFactory::getApplication->input->get->getBool( 'hide', true );
will retrieve the value of the 'hide' argument from a GET request (but not a POST), with a default value of true.
Word[edit]
A word is defined as a string of alphabetic characters. The underscore character is permitted as part of a word.
$word = JFactory::getApplication->input->cookie->getWord( 'search-word' );
will retrieve the value of the 'search-word' argument from the request. The default is an empty string.
$word = JFactory::getApplication->input->cookie->getWord( 'keyword', '' );
will retrieve the value of the 'keyword' variable from a cookie, with the default being an empty string.
Command[edit]
A command is like a word but a wider range of characters is permitted. Allowed characters are: all alphanumeric characters, dot, dash (hyphen) and underscore.
$command = JFactory::getApplication->input->getCmd( 'option' );
will retrieve the value of the "option" argument from the request. The default value is an empty string.
$command = JFactory::getApplication->input->post->getCmd( 'controller', 'view' );
will retrieve the value of the "controller" argument from a POST request (but not a GET), with a default value of 'view'.
String[edit]
The string type allows a much wider range of input characters. It also takes an optional fourth argument specifying some additional mask options. See #Filter options for information on the available masks.
$string = JFactory::getApplication->input->getString( 'description' );
will retrieve the value of the "description" argument from the request. The default value is an empty string. The input will have whitespace removed from the left and right ends and any HTML tags will be removed.
$string = JFactory::getApplication->input->getString( 'text', '' );
will retrieve the value of the "text" argument from the request. The default value is an empty string.
$string = JFactory::getApplication->input->getString( 'template', '<html />' );
will retrieve the value of the "template" argument from the request. The default value is '<html></html>'.
JSON String[edit]
$json = JFactory::getApplication->input->json->get( 'var_name' );
Generic and other data types[edit]
If the above methods do not meet your needs, there is a small number of additional filter types which you can use by calling the JInput->get method directly. The syntax is:
JFactory::getApplication->input->get( <name>, <default>, <type> );
where:
<name> | the name of the variable to be retrieved (for example, the name of an argument in a URL). |
<default> | the default value. There is no default value that will be returned if no default is specified in the call the JInput->get. If no default is specified and the argument is not present in the request variable then it will return undefined. |
<type> | specifies the data type expected (see below). |
The first three arguments are the same as for the more specific methods described earlier. Only the first argument is mandatory.
Allowed values of the <type>, which is case-insensitive, are as follows:
INT, INTEGER | Equivalent to JInput->getInt. |
UINT | Get an unsigned integer. Equivalent to JInput->getUint. |
FLOAT, DOUBLE | Equivalent to JInput->getFloat. |
BOOL, BOOLEAN | Equivalent to JInput->getBool. |
WORD | Equivalent to JInput->getWord. |
ALNUM | Allow only alphanumeric characters (a-z, A-Z, 0-9). Equivalent to JInput->getAlnum. |
CMD | Equivalent to JInput->getCmd. |
BASE64 | Allow only those characters that could be present in a base64-encoded string (ie. a-z, A-Z, 0-9, /, + and =). Equivalent to JInput->getBase64. |
STRING | Equivalent to JInput->getString. |
ARRAY | Source is not filtered but is cast to array type. When using this type you should use JFilterInput directly to filter the values in your data array. |
HTML | A sanitised string. Equivalent to JInput->getHtml. |
PATH | Valid pathname regex that filters out common attacks. For example, any path beginning with a "/" will return an empty string. Simliarly, any path containing "/./" or "/../" will return an empty string. Dots within filenames are okay though. Equivalent to JInput->getPath. |
USERNAME | Removes control characters (0x00 - 0x1F), 0x7F, <, >, ", ', % and &. Equivalent to JInput->getUsername. |
Filter options[edit]
All input values can be filtered using JFilterInput->clean.
Filter an array of values.
$data = JFactory::getApplication()->input->post->get('data', array(), 'array');
$filter = JFilterInput::getInstance();
foreach ($data as $value)
{
$array[] = $filter->clean($value, 'string');
}
For more filter types see JFilterInput source.
Options listed bellow available only in deprecated JRequest library. Allowed values of <options> are as follows (none of these are applied by default):
JREQUEST_NOTRIM | Does not remove whitespace from the start and ends of strings. |
JREQUEST_ALLOWRAW | Does not do any filtering at all. Use with extreme caution. |
JREQUEST_ALLOWHTML | Does not remove HTML from string inputs. |
Masks can be combined by logically OR'ing them. If no filter options are specified, then by default, whitespace is trimmed and HTML is removed.
File uploads[edit]
Web servers already have a good deal of security around handling file uploads, but it is still necessary to take additional steps to ensure that file names and paths cannot be abused. A simplified form which requests a file to be uploaded looks like this:
<form action="index.php?option=com_mycomponent/form_handler.php" method="post" enctype="multipart/form-data">
<input type="file" name="Filedata" />
<input type="submit" />
</form>
On clicking the submit button, the browser will upload the file in a POST request, passing control to Joomla which will call "components/com_mycomponent/form_handler.php". This will include code like the following. The variable $somepath must be set to some path where the web server has permission to create files.
// Check to ensure this file is included in Joomla!
defined('_JEXEC') or die( 'Restricted access' );
// Get the file data array from the request.
$file = JFactory::getApplication->input->get( 'Filedata', '', 'files', 'array' );
// Make the file name safe.
jimport('joomla.filesystem.file');
$file['name'] = JFile::makeSafe($file['name']);
// Move the uploaded file into a permanent location.
if (isset( $file['name'] )) {
// Make sure that the full file path is safe.
$filepath = JPath::clean( $somepath.'/'.strtolower( $file['name'] ) );
// Move the uploaded file.
JFile::upload( $file['tmp_name'], $filepath );
}
Saving a request variable into user state[edit]
Because setting a user state variable from a variable in the request is such a common operation, there is an API method to make the task easier. This is generally safe to use because it calls JInput->get to obtain the input from the request, but remember that none of the input filtering calls will protect against SQL injection attempts.
$app = JFactory::getApplication();
$app->getUserStateFromRequest( <key>, <name>, <default>, <type> );
where
<key> | the name of the variable in the user state. |
<name> | the name of the request variable (same as the first argument of a JInput->get call). |
<default> | the default value to be assigned to the user state variable if the request variable is absent. The default is null. |
<type> | the type of variable expected. |
For example, getting an integer variable called id from the request with a default value of 0, then saving it into a session variable called myid can be done like this:
$app = JFactory::getApplication();
$app->getUserStateFromRequest( 'myid', 'id, 0, 'int' );
instead of something like this:
$app = JFactory::getApplication();
$app->setUserState( 'myid', $app->input->getInt( 'id', 0 ) );
Constructing SQL Queries[edit]
One of the most common forms of attack on web applications is SQL injection, where the aim of the attacker is to change a database query by exploiting a poorly filtered input variable. Injecting modified SQL statements into the database can damage data or reveal private information. It is important to ensure that when SQL statements are constructed, they are correctly escaped and quoted so that bad input data cannot result in a bad SQL statement. You cannot rely on the JInput methods to do this as they are not SQL-aware.
Secure integers and the rest of numeric values[edit]
With the MySQL database, numeric fields should not be quoted, so it is important that they be typecast instead. Failure to do this will leave your code vulnerable to an attacker inserting a string containing SQL data.
Depending on the type, numeric types are cast like this:
// For SQL data types: INT, INTEGER, TINYINT, SMALLINT, MEDIUMINT, BIGINT, YEAR
$query = 'SELECT * FROM #__table WHERE `id`=' . (int) $id;
// For SQL data types: FLOAT, DOUBLE
$query = 'SELECT * FROM #__table WHERE `id`=' . (float) $id;
It's a good idea to get into the habit of always typecasting integers like this even if the variable was previously obtained using JInput->getInt. Further information on SQL injection attacks can be found at the PHP.NET SQL Injection page and here: Retrieving_request_data_using_JInput#Getting_Values.
Secure Strings[edit]
In the examples that follow it is assumed that $db is an instance of a Joomla database object. This can always be obtained from JFactory using
$db = JFactory::getDBO();
Strings should always be escaped before being used in an SQL statement. This is actually very simple as the JDatabase->quote method escapes everything for you. You can also use the JDatabase->escape method directly. The following statements are equivalent:
$query = 'SELECT * FROM #__table WHERE `field` = ' . $db->quote( $db->escape( $field ), false );
$query = 'SELECT * FROM #__table WHERE `field` = ' . $db->quote( $field );
Secure on search[edit]
Special attention should be paid to LIKE clauses which contain the % wildcard character as these require special escaping in order to avoid possible denial of service attacks. LIKE clauses can be handled like this:
// Construct the search term by escaping the user-supplied string and, if required, adding the % wildcard characters manually.
$search = '%' . $db->escape( $search, true ) . '%';
// Construct the SQL query, being careful to suppress the default behaviour of Quote so as to prevent double-escaping.
$query = 'SELECT * FROM #__table WHERE `field` LIKE ' . $db->quote( $search, false );
Secure dates[edit]
If data is to be entered into a datetime column then you can use the Joomla API to ensure a valid date format:
$date = JFactory::getDate( $mydate );
$query = 'UPDATE #__table SET `date` = ' . $db->quote( $date->toMySQL(), false );
Note that it is necessary to suppress database escaping as legitimate dates may contain characters that should not be escaped.
Secure field names[edit]
In the comparatively rare case where a field name is a variable, that should also be quoted using an API call:
$query = 'SELECT * FROM #__table WHERE ' . $db->quoteName( $field->name ) . '=' . $db->quote( $field->value );
Secure arrays of integers[edit]
When you have an array of ids, typically used for IN() queries, you have to sanitise it also with JArrayHelper::toInteger($cid); before imploding:
...
$catId = JArrayHelper::toInteger($catId);
$query->where($db->quoteName('x.category_id') . ' IN (' . implode(',', $catId) . ')');
Short Aliases of Quote and QuoteName[edit]
Shorter alternatives to the quote methods may also be used. The following statements are equivalent:
$query = 'SELECT * FROM #__table WHERE ' . $db->quoteName( $field->name ) . '=' . $db->quote( $field->value );
$query = 'SELECT * FROM #__table WHERE ' . $db->qn( $field->name ) . '=' . $db->q( $field->value );
Securing forms[edit]
Apart from cleaning input variables as described above, you can also implement a simple technique which makes it more difficult for a cross-site request forgery attack (CSRF) to succeed. This involves adding a randomly-generated unique token to the form which is checked against a copy of the token held in the user's session. By checking that the submitted token matches the one contained in the stored session, it is possible to tie a rendered form to the request variables presented.
In POST forms you should add a hidden token field using:
echo JHTML::_( 'form.token' );
This outputs the token as a hidden form field looking like this:
<input type="hidden" name="8cb24ae69ffd7828ccecbcf06056e6fc" value="1" />
and places a copy of the token into the user's session, for later checking.
If you need to add the token to a URL rather than a form then you can use something like this:
echo JRoute::_( 'index.php?option=com_mycomponent&' . JSession::getFormToken() . '=1' );
In the most common scenario, you will want to check the token following a POST to the form handler. This can be done by adding this line of code to form handler:
JSession::checkToken() or die( JText::_( 'Invalid Token' ) );
If you need to pass the token in a GET request then you can check it like this:
JSession::checkToken( 'get' ) or die( JText::_( 'Invalid Token' ) );
In both cases the code will die if the token is omitted from the request, or the submitted token does not match the session token. If the token is correct but has expired, then JSession::checkToken will automatically redirect to the site front page.
Cleaning Filesystem Paths[edit]
If there is any possibility that a filesystem path might be constructed using data that originated from user input, then the path must be cleaned and checked before being used. This can be done quite simply like this:
JPath::check( $path );
This will raise an error and terminate Joomla if the path contains a ".." or leads to a location outside the Joomla root directory. If you want to deal with the error yourself without terminating the application, then you can use code like this:
$path = JPath::clean( $path );
if (strpos( $path, JPath::clean( JPATH_ROOT ) ) !== 0) {
// Handle the error here.
}
The JPath:clean method can be used in your own code too. It merely removes leading and trailing whitespace and replace double slashes and backslashes with the standard directory separator.
Cleaning Filesystem File Names[edit]
As with filesystem paths, if there is any possibility that a file name might be constructed using user-originated data, then the file name must be cleaned and checked before use. This can be done like this:
jimport('joomla.filesystem.file');
$clean = JFile::makeSafe( $unclean );
This method removes sequences of two or more "." characters and any character that is not alphabetic, numeric or a dot, dash or underscore character. If there is a leading dot then that is removed too.