Keeping Extensions Secure with Joomla! 1.5: Part 1

Exclusive offer: get 50% off this eBook here
Joomla! 1.5 Development Cookbook

Joomla! 1.5 Development Cookbook — Save 50%

Solve real world Joomla! 1.5 development problems with over 130 simple but incredibly useful recipes

$26.99    $13.50
by James Kennard | September 2009 | Cookbooks Joomla! MySQL Content Management Open Source PHP

This article demonstrates how we can secure extensions and explains some of the ramifications if we fail to do this.

This two-part article by James Kennard contains the following recipes:

  • Writing SQL safe queries
  • Writing SQL-safe LIKE string comparison queries
  • Using the token
  • Making a filename safe
  • Making a directory path safe
  • Making a path safe
  • Safely retrieving request data
  • Getting a value from an array

Introduction

There's no such thing as a completely secure system. No matter how many precautions we take and how many times we verify our design and implementation, we will never be able to guarantee that we have created a truly secure Joomla! extension. Why not? This is because it is not possible to be prepared for every potential vulnerability.

Common Weakness Enumeration (CWE) is a project dedicated to generating a formal categorization and identification system of security vulnerabilities. CWE published a list of the top 25 security weaknesses, which were selected on the basis of their frequency and consequences. This list includes some of the most publicized security weaknesses, such as code injection (CWE-94) and XSS (CWE-79). When considering the security of our extensions, this list can prove useful. For information about common programming mistakes that lead to security vulnerabilities, refer to http://cwe.mitre.org/top25/.

This article includes references to the CWE weaknesses. These references are in the form of CWE IDs, that is, CWE-n. For information about a weakness, simply Search By ID on the CWE web site. These references are intended to help us better understand the weaknesses, the risks associated with the weaknesses, how the risks can be reduced using the Joomla! framework, and the suggested CWE mitigations.

Something we should consider is the ramifications of security flaws. Whichever way we look at this, the answer always involves financial loss. This is true even of non-profit organizations. If a web site is attacked and the attacker managed to completely obliterate all the data held on that web site, it will cost the owner's time to restore a backup or replace the data. OK, it may not seem like a financial loss because it's non profit. It is a wastage of time if the web site owner spends two hours to restore his or her data, as those two hours could have been used elsewhere.

For commercial web sites, the potential for financial loss is far more obvious. If we use a bank as an example, a security flaw could enable an attacker to transfer money from the bank to his or her own (probably untraceable) account. In 2004, the Internet bank Cahoot suffered a security flaw enabling any existing customer access to other customers' accounts. Cahoot did not suffer any obvious financial loss from the security flaw and they claimed there was no risk of financial loss. However, the customers' confidence in Cahoot was inevitably lost. This loss of confidence and bad press will certainly have affected Cahoot in some way. For example, some potential customers may have decided to open an account with a rival bank because of concerns over how secure their savings would, or would not, be with Cahoot. For more information, refer to http://news.bbc.co.uk/1/hi/business/3984845.stm.

From the perspective of an extension developer, we should reflect on our moral duty and our liability. Disclaimers, especially for commercial software, do not relinquish us of legal responsibility. We should always try to avoid any form of litigation, and I'm not suggesting that we run to Mexico or our closest safe haven. We should take a holistic approach to security. We need a complete view of how the system works and of the various elements that need to be secure. Security should be built into our extension from the requirements gathering stage through to the ongoing system maintenance.

How we do that depends on how we are managing our project and what the security implications of our extension are. For example, a shopping cart component with credit card processing facilities will require far greater attention to security than a content plugin that converts the occurrences of :) to smiley face images. Irrespective of the way we choose to manage the risks of weaknesses, we should always document how we are circumventing security threats. Doing so will make it easier to maintain our extension without introducing vulnerabilities. Documentation also provides us with proof of prudent risk management, which can be useful should we ever be accused of failing to adequately manage the security risks associated with our software.

This is all starting to sound like a lot of work! This brings us back to the ramifications of vulnerabilities. If on the one hand, it costs us one extra month of development time to produce a piece of near-secure software. And on the other hand, it costs us two months to patch a non-secure piece of software and an incalculable amount of damage to our reputation. It is clear which route we should favor!

Packt Publishing offers a book that deals specifically with Joomla! security. For more information, refer to http://www.packtpub.com/joomla-web-security-guide/.

Writing SQL safe queries

SQL injection is probably the most high profile of all malicious web attacks. The effects of an SQL injection attack can be devastating and wide ranging. Whereas some of the more strategic attacks may simply be aimed at gaining access, others may intend on bringing about total disruption and even destruction. Some of the most prestigious organizations in the world have found themselves dealing with the effects of SQL injection attacks. For example, in August 2007 the United Nations web site was defaced as a result of an SQL injection vulnerability. More information can be found at http://news.bbc.co.uk/1/hi/technology/6943385.stm.

Dealing with the effects of an SQL injection attack is one thing, but preventing them is quite another. This recipe explains how we can ensure that our queries are safe from attack by utilizing the Joomla! framework. For more information about SQL injection, refer to CWE-89.

Getting ready

The first thing we need is the database handler. There is nothing special here, just the usual Joomla! code as follows:

$db =& JFactory::getDBO();

How to do it...

There are two aspects of a query that require special attention:

  • Identifiers and names
  • Literal values

The JDatabase::nameQuote() method is used to safely represent identifiers and names. We will start with an easy example, a name that consists of a single identifier.

$name = $db->nameQuote('columnIdentifier');

We must take care when dealing with multiple-part names (that is, names that include more than one identifier separated by a period). If we attempt to do the same thing with the name tableIdentifier.columnIdentifier, we won't get the expected result! Instead, we would have to do the following:

// prepare identifiers
$tableIdentifier = $db->nameQuote('tableIdentifier');
$columnIdentifier = $db->nameQuote('columnIdentifier');
// create name
$name = "$tableIdentifier.$columnIdentifier";

Avoid hardcoding encapsulation

Instead of using the JDatabase::nameQuote() method, it can be tempting to do this: $sql = 'SELECT * FROM `#__foobar_groups` AS `group`'. This is OK as it works. But the query is now tightly coupled with the database system, making it difficult to employ an alternative database system.

Now we will take a look at how to deal with literal values. Let's start with strings. In MySQL, strings are encapsulated in double or single quotes. This makes the process of dealing with strings seem extremely simple. Unfortunately, this would be an oversight. Strings can contain any character, including the type of quotes we use to encapsulate them. Therefore, it is also necessary to escape strings. We do all of this using the JDatabase::Quote() method as follows:

$tableIdentifier = $db->nameQuote('tableIdentifier');
$columnIdentifier = $db->nameQuote('columnIdentifier');
$sql = "SELECT * FROM $tableIdentifier "
. "WHERE $columnIdentifier "
. ' = ' . $db->Quote("How's the recipebook going?");

The JDatabase::Quote() method essentially does the following. The exact output will depend on the database handler. However, most databases escape and encapsulate strings in pretty much the same way.

 

Original

Quoted

How's the recipebook going?

'How's the recipebook going?'

Joomla! 1.5 Development Cookbook Solve real world Joomla! 1.5 development problems with over 130 simple but incredibly useful recipes
Published: September 2009
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Dealing with the LIKE clauses requires slightly different string handling. For more information, refer to the next recipe, Writing SQL safe LIKE string comparison queries.

The other type of literal value we often use in the queries is numbers. In MySQL, there are two types of literal numbers—integers (whole numbers) and floats (decimal numbers). The following examples show how we can cast unsafe values or use the PHP *val() functions to make these values safe for use in a query:

// integer
$safeNumber = (int)$unsafeValue;
$safeNumber = intval($unsafeValue);
// floating-point
$safeNumber = (float)$unsafeValue;
$safeNumber = floatval($unsafeValue);

In most instances, $unsafeValue will have been extracted from the request data; for example, index.php?option=com_foobar&int=unsafeInt&flt=unsafeFlt. In these instances, we can use JRequest to do the work for us as follows:

// integer
$safeInt = JRequest::getInt('int');
$safeInt = JRequest::getVar('int', 0, 'DEFAULT', 'INT');
$safeInt = JRequest::getVar('int', 0, 'DEFAULT', 'INTEGER');
// floating-point
$safeFlt = JRequest::getFloat('float');
$safeFlt = JRequest::getVar('float', 0, 'DEFAULT', 'FLOAT');
$safeFlt = JRequest::getVar('float', 0, 'DEFAULT', 'DOUBLE');

For more information about using JRequest and the various methods shown above, refer to the Safely retrieving request data recipe, later in the article.

The final and the most complex option is to use JFilterInput. This class allows us to use the same sort of principles as with JRequest as shown here:

// get filter instance
$filter = JFilterInput::getInstance();
// integer
$safeInt = $filter->clean($unsafeValue, 'INT');
$safeInt = $filter->clean($unsafeValue, 'INTEGER');
// floating-point
$safeFlt = $filter->clean($unsafeValue, 'FLOAT');
$safeFlt = $filter->clean($unsafeValue, 'DOUBLE');

Quoting numbers

As an extra line of defense, we can also treat a number as a string. For example, we could use $db->Quote((int)$unsafeValue). Although numbers do not require encapsulation, it is acceptable to quote a number.

How it works...

An identifier identifies a database, table, or a column. A literal value is an expression that cannot be broken down any further. Therefore, they are literally equal to themselves, for example 1 == 1. To make identifiers and literal values safe, we encapsulate them in special characters defined by the server. For example, when we are dealing with MySQL identifiers, we encapsulate the identifiers in grave accents such as `identifier`. Of course, we don't need to know this because JDatabase deals with this for us!

A name consists of one or more identifiers separated by a period. Names that contain more than one identifier are known as multiple-part names. Multiple-part names provide the ability to drill down, for example myTable.myColumn. The JDatabase::nameQuote() method cannot handle multiple-part names. Hence, each identifier in a name must be handled separately.

In most database systems, encapsulating identifiers is not technically required, and MySQL is no exception. However, there are occasions when failure to do so will prevent a query from working. SQL has reserved keywords. If any of our identifiers are also reserved words, we must encapsulate them. This tends to be especially noticeable when using aliases. For example, SELECT * FROM #__foobar_groups AS group will fail because group is a keyword. We can easily overcome this in Joomla! as follows:

$sql = 'SELECT * FROM ' . $db->nameQuote('#__foobar_groups')
. ' AS ' . $db->nameQuote('group');

There's more...

In MySQL, there are six different types of literal values. Absent from this list are date and time. This is because the date and time values are expressed as strings. For example, November 2nd, 1815 would be expressed as the literal value '1815-11-02'. The six types of literal values are as follows:

  • String
  • Number
  • NULL
  • Hexadecimal
  • Boolean
  • Bit field

The type of the value makes a difference as to how we should handle it. We have already addressed strings and numbers. The following subsections describe how to safely handle the remaining literal types.

NULL

A NULL value represents the absence of data. This is not the same as an empty value. For example, a string with no characters is not a NULL value. NULL values should always be written as NULL or N. We should never use raw input when we express a NULL value in a query.

$safeValue = ($unsafeValue == 'NULL') ? 'NULL' : 'NOT NULL';

Hexadecimal

It is unusual to use hexadecimal literal values in Joomla! extensions. It is no surprise that there are no special tricks for dealing with hexadecimal values in the Joomla! framework. An example of when we might want to use hexadecimal is recording colors such as red, FF0000. The following example shows how we can sanitize some hexadecimal data. There are three normal ways of representing hexadecimal data—X'value', x'value', and 0xvalue. The following example uses the standard SQL notation, x'value':

$matches = array();
$pattern = "~^([Xx]'|0x)([0-9A-F]+)'?$~";
$safeHex = $defaultSafeHexValue;
if (preg_match($pattern, $unsafeValue, $matches)) {
$safeHex = "x'" . $matches[2] . "'";
}

Boolean

Boolean values are very straightforward. They are represented as the raw strings TRUE and FALSE. We should always use a PHP expression to determine a Boolean value.

$boolean = ($unsafeValue) ? 'TRUE' : 'FALSE' ;

Bit field

Binary values are also unusual in Joomla! extensions. Values of this type can be useful for storing bit patterns (essentially flags). Again, there are no special tricks for dealing with these types of literal values. To overcome this we can improvise, as shown in the following example. There are two ways of representing binary values—b'value' and 0bvalue:

$matches = array();
$pattern = "~^(b'|0b)([01]+)'?$~";
$safeBin = $defaultSafeBinValue;
if (preg_match($pattern, $unsafeValue, $matches)) {
$safeBin = "b'" . $matches[2] . "'";
}

Binary representation is only available in MySQL from version 5.0.3 onwards.

Writing SQL-safe LIKE string comparison queries

Performing searches on a database using strings takes a bit more thought than normal. String-based searches use special characters to enable special searching capabilities that are not present when using basic operators, such as =. Therefore, we must treat comparison strings slightly differently to normal strings (described in the previous recipe).

Failure to properly manage the risks associated with constructing an SQL query with a LIKE clause can lead to an SQL injection weakness. For more information about SQL injection, refer to CWE-89.

Getting ready

The first thing we need is the database handler. Nothing special here, just the usual Joomla! code as follows:

$db =& JFactory::getDBO();

How to do it...

Searching in Joomla! is commonly achieved using the string comparison function, LIKE. This function compares two strings character by character. When we use the function, we can include the special characters % and _. These represent zero-to-many characters and one character respectively. Of course, including special characters means that we have to pay special attention if we want to use the special characters in their unadulterated form. As with special characters in a normal string, we escape the characters with backslashes.

It is standard practice in Joomla! to simply surround a search string with % characters. This will attempt to find an exact match to the string somewhere within the string being searched.

There are two steps to this process, escaping and encapsulating. We can escape the comparison string by using the JDatabase::getEscaped() method, and we can encapsulate the escaped string using the JDatabase::Quote() method.

// prepare search
$escaped = $db->getEscaped($searchFor, true);
$quoted = $db->Quote('%' . $escaped . '%', false);
// write the SQL
$like = $db->nameQuote('columnName') . ' LIKE ' . $quoted;

How it works...

Normally when we use the JDatabase::Quote() method, the string we pass in is automatically escaped. However, that escaping does not allow for the extra characters % and _. To overcome this, we take care of the escaping of characters ourselves. This means when we use the JDatabase::Quote() method, we must ensure that we do not escape the string a second time! When the optional second Boolean parameter is specified as false, it is used to prevent escaping, as shown in the example.

Luckily, Joomla! provides us with the JDatabase::getEscaped() method specifically for escaping strings. This method also accepts a second optional Boolean parameter. This parameter is used to determine if the extra characters, % and _, should also be escaped.

After we have escaped the string and before we quote it, we encapsulate the string in % characters. It is vital that we add these at the correct point in time. If we add these too early, they will be escaped; and if we add them too late, they will cause the query to fail. We can easily summarize this process by inspecting the variables:

 

Variable

Value

$searchFor

We are 25% through the_chapter

$escaped

We are 25% through the_chapter

$quoted

'%We are 25% through the_chapter%'

$like

'columnName' LIKE '%We are 25% through the_chapter%'

It is possible to make the search a little more flexible by replacing spaces with % characters. The following code sample shows how we can achieve this:

// split the search into an array of words
$words = preg_split('~[s,.]+~', $searchFor);
// iterate over the words
for ($i = 0; $i < count($words); $i ++) {
$words[$i] = $db->getEscaped($words[$i], true);
}
// implode and quote the words
$quotedSearch = implode('%', $words);
$quotedSearch = $db->Quote('%' . $quotedSearch . '%', false);
// write the SQL
$like = $db->nameQuote('columnName') . ' LIKE ' . $quoted;

This time we end up with the following values:

 

 

Variable

Value

$searchFor

We are 25% through the_chapter

$words

array('We', 'are', '25%', 'through', 'the_chapter')

$words

array('We', 'are', '25%', 'through', 'the_chapter')

$quotedSearch

We%are%%%through%the_chapter

$quotedSearch

'%We%are%%%through%the_chapter%'

$like

'columnName' LIKE '%We%are%%%through%the_chapter%'

 

Joomla! 1.5 Development Cookbook Solve real world Joomla! 1.5 development problems with over 130 simple but incredibly useful recipes
Published: September 2009
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Trailing spaces

The LIKE function can also be useful if we want to compare two strings and one or more of those strings contains trailing spaces. The = operator always ignores trailing spaces, for example 'J!' is considered equivalent to 'J! '. The LIKE function, on the other hand, would consider the strings to be different.

See also

For information about safely dealing with other values in queries, refer to the previous recipe, Writing SQL safe queries.

Using the token

Tokens provide an additional layer of security, typically for use with logged-in users. It is generally quite easy to assume that once a user is logged in, all the requests he or she makes are legitimate. And assuming the login itself was legitimate, why wouldn't all of the requests also be legitimate? Once a user has logged into a system, it is not technically the user who gains access to the system; it is the client machine itself! Therefore, if the client machine has been compromised in any way, there may be some unexpected skullduggery occurring without the user's knowledge. For web applications such as Joomla!, this threat is primarily from Cross-Site Request Forgery (CSRF) CWE-352.

Consider the following scenario. A user has a browser with tabbed windows. In one tab, they have logged into a Joomla! web site and in another tab they have unwittingly browsed to a compromised web site. The compromised web site contains JavaScript, or another client-side language, which is able to take advantage of the fact that privileges have been granted to the browser.

It is unsurprising to discover that financial institutions are the most commonly targeted because a successful attack could bring the attacker significant financial gain. We must not become careless in instances where we are creating extensions that would provide limited or no tangible gain for an attacker. In 2006, a CSRF vulnerability was discovered in Google that enabled an attacker to change a user's language preferences. You can read more about this at http://isc.sans.org/diary.html?date=2006-10-01.

So, where do tokens come in? A token is a value, normally an alphanumeric string, which can be used to verify the authenticity of a request. The server generates a token, and that token is sent to the client. When the client makes any subsequent requests, the same token must be returned. If it is not, it indicates that the origin of the request is not quite what it seems.

Getting ready

Joomla! has a built-in token system that we can use to easily add an extra layer of security. This makes it very easy to protect ourselves against CSRF attacks. To use the built-in token system, we use the static classes JUtility, JRequest, and JHTML.

How to do it...

In Joomla!, we pass a token using the token value as the name. The value of this is then set to 1, for example index.php?tokenValue=1. There is a good reason for using the value of the token as the name of the query value. It is more secure. Systems that use a name such as token and simply set the value of this to the token value, that is index.php?token=tokenValue, are less secure because it is very easy to extract the value of the token. With this in mind, the following example shows how to include the token in a URL:

// get the value of the token
$token = JUtility::getToken();
// build the URL with the token embedded
$url = 'index.php?option=com_foobar&controller=foo&task=bar'
.'&'.$token.'=1';
$url = JRoute::_($url);

It is unusual to use the token in this way. Generally, we only use the token when dealing with forms because forms are used to make the most important requests. As a general rule of thumb, we should use the token for requests that will result in changes to data or in execution of secure transactions (such as a bank transfer). The following example shows a shortcut we can use to add the token to a form:

// import JHTML
jimport('joomla.html.html');
// insert a hidden token to the form field
echo JHTML::_('form.token');

We're almost there. There's just one thing left to do—validate the token once a request is received. Joomla! provides us with a quick and easy way to do this. The JRequest::checkToken() method compares the token value against the request and provides a Boolean response. For this reason, it is up to us to act on that response.

// check the token (POST request)
JRequest::checkToken() or jexit('Invalid Token');

The jexit() function is equivalent to the exit() and die() PHP functions. We should always use jexit() in preference to exit() and die() because it gives Joomla! the opportunity to tidy up any resources.

By default, the JRequest::checkToken() method looks specifically at the POST request data. We can override this by providing the name of the request hash we want to check, for example GET or REQUEST.

// check the token (whatever the request method)
JRequest::checkToken('REQUEST') or jexit('Invalid Token');

In the previous examples, we take the hard-line approach of invoking jexit() in case the comparison fails. This is generally the best policy. However, there are other options such as using JError to return an HTTP error.

// check the token (POST request)
if (!JRequest::checkToken('REQUEST')) {
// return 403 error
JError::raiseError(403, JText::_('ALERTNOAUTH'));
// belt and braces approach to guarantee the script stops
jexit('Invalid Token');
}

There's more...

Joomla! uses the same token throughout the life of a session. Although this is not a flaw. it does not conform to the suggested solution indicated by the CWE. The CWE suggests that creating a new token for each and every form will be more secure. Admittedly, the CWE appears to favor the double-submitted cookie solution as opposed to the state-recorded token solution, as implemented in Joomla!. It can still be useful to change the token, especially for systems that require very high levels of security.

There is one problem with this. If a user has multiple pages from the site open in different windows or tabs, frequently changing the tokens in these pages may render them outdated. Therefore, we should carefully consider whether or not we need to reset the token before doing so.

The token is reset as follows:

// the old token
$oldToken = JUtility::getToken();
// the new token
$newToken = JUtility::getToken(true);

Easy! However, there is one more issue to consider. If we are going to reset the token, we should do so only if forms that don't use the token have already been generated in the same page. It is best to generate a new token only when creating components, because components are always rendered first.

There is an additional option we can incorporate in an attempt to prevent the CSRF attacks. However, this solution is not foolproof and can be circumvented. It can also potentially cause problems for legitimate clients.

HTTP request headers include a referrer, which is the URL from which the requested URL was obtained. Unfortunately, this header is not required by the HTTP specification. Some browsers don't send this information, some proxies and gateways strip this information, and it is also possible to spoof this information. Put simply, this is not a reliable solution and should, therefore, be used only as a supplement alongside a token.

The following example shows how we could safely include referrer validation. This example will not block any requests that do not include the referrer in the headers of the HTTP request.

// check the token (POST request)
JRequest::checkToken() or jexit('Invalid Token');
// get referrer
$referer = JRequest::getString('HTTP_REFERER', null, 'SERVER');
// check referrer was included in the request
if ($referer != null && $referer != '') {
// referrer is present in request, validate referrer!
if (JURI::isInternal($referer)) {
// invalid referrer
jexit('Invalid Referrer');
}
}

For more information about the HTTP protocol, refer to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36.

Making a filename safe

Joomla! extensions can contain many files and interacting with them is a typical process. In instances where a filename is generated dynamically, for example from user input, we need to be especially attentive. Unsafe filenames can lead to security vulnerabilities such as code injection. Joomla! provides us with an easy way to ensure that filenames are safe.

For more information about external control of filenames, refer to CWE-73.

How to do it...

The static JFile class, which is a part of the joomla.filesystem library, provides us with all sorts of useful methods for working with files. To use the class, we must import it as follows:

jimport('joomla.filesystem.file');

This bit is nice and easy. We use the JFile::makeSafe() method and pass the name of the file we want to sanitize. This method returns a string that can be used to interact safely with a file.

// make the filename safe
$safeFilename = JFile::makeSafe($unsafeFilename);

How it works...

So, what exactly does the JFile::makeSafe() method do to guarantee a safe filename? It strips out any characters or character sequences that are seen as posing potential security risks. The following list describes the rules that are used by the JFile::makeSafe() method. Any characters that do not meet the criteria are stripped from the string.

  • Only consists of alphanumeric characters, periods, underscores, dashes, and spaces
  • Periods do not occur together (is not attempting filesystem traversal, CWE-22)
  • Does not start with a period (is not a hidden *nix file)

The following table shows some examples of input and output strings from the JFile::makeSafe() method:

 

 

Original

 

Safe

 

Stripped

 

.htaccess

 

htaccess

 

Leading period

 

some file.html

 

some20file.html

 

% character

 

../../traversed.file

 

traversed.file

 

/ character and consecutive periods

 

spaced out.file

 

spaced out.file

 

 

dotty...to..the.dot

 

dottytothe.dot

 

Consecutive periods

 

There's more...

Depending on how we are using the filename, we may also want to check the file extension. For this, we use the JFile::getExt() method as shown here:

switch (JFile::getExt($filename)) {
case 'jpeg':
case 'jpg':
echo 'File is a JPEG';
break;
case 'gif':
echo 'File is a GIF';
break;
default:
echo 'File is not a JPEG or a GIF';
}

Verify file type by content

A file extension cannot be used as a reliable mechanism to determine a file type. We can attempt to determine the MIME (Multipurpose Internet Mail Extensions) type of a file using the deprecated PHP function mime_content_type(), or the PECL (PHP Extensions Community Library) File Information module. For more information, refer to http://php.net/manual/function.mime-content-type.php and http://php.net/manual/book.fileinfo.php.

Along with determining the extension of a filename, we can also strip the extension.

$filenameWithoutExtension = JFile::stripExt($filename);

This can be useful if we want to specify the extension ourselves. For example, if we are dealing with JPEGs, the extension can be .jpeg or .jpg.

$filename = JFile::stripExt($filename) . '.jpeg';

Depending on what we intend to do with the filename, it can also be useful to check that the file exists before continuing. We are probably used to working with the PHP functions file_exists() and is_file(). But when we use Joomla!, we should use the JFile::exists() method instead.

if (JFile::exists($filename)) {
echo "<img src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="$filename" alt="image">";
} else {
echo JText::_("No Image File");
}

See also

The following two recipes, Making a directory path safe and Making a path safe, investigate how to safely deal with directories and paths.

[ 1 | 2 ]

If you have read this article you may be interested to view :

About the Author :


James Kennard

James Kennard is a computer programmer. He has worked with various PHP and MySQL applications, since 2002. He quickly discovered Mambo/Joomla! because of its flexible extension manager. James currently maintains one open-source Joomla! component, which has been translated into over fifteen languages. Moreover, he has plans to build two more open-source components. Examples of his work can be found on his personal website www.webamoeba.co.uk.

Books From Packt

Drupal 6 Site Blueprints
Drupal 6 Site Blueprints

Joomla! 1.5 Template Design
Joomla! 1.5 Template Design

Papervision3D Essentials
Papervision3D Essentials

Drupal 6 Search Engine Optimization
Drupal 6 Search Engine Optimization

Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs
Joomla! 1.5x Customization: Make Your Site Adapt to Your Needs

PHP Team Development
PHP Team Development

Unity Game Development Essentials [RAW]
Unity Game Development Essentials [RAW]

WordPress 2.7 Cookbook
WordPress 2.7 Cookbook

 

 

 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software