Buca Bay - Always nice

Dua tiko noqu toa loaloa, na yacana ko… laga mai…

Joomla PHP Pages Component Editor with Syntax Highlighting

December17

The PHP Pages Component is a Joomla component that allows you to create PHP pages inside Joomla. It is quite a simple component but very powerful. I use it whenever I need to put together a quick PHP page in Joomla, and do not want to have to package a full blown component with MVC skeleton and all. The PHP pages created in the component has full access to the Joomla API and thus can do anything a Joomla component can.

A few days ago a Joomla developer posted on our forum and mentioned some issues and suggestions with the PHP Pages Component. I use the component a lot and have come to ignore those issues altogether. However, being the lazy developer I am, always calculating the best way to do less work, I had to spend some time on the component’s development in order to gain some time later one when using it.

The new Joomla PHP Pages Component now comes with HTML/PHP/JavaScript code syntax highlighting (provided by the awesome CodeMirror project). Other User Interface fixes were made to make developing  PHP Pages faster and simpler.

The code is currently very alpha, but works pretty well as far as I can see. Go download it if you haven’t already. Trust me, if you use Joomla as much as I do, it will make your life a lot easier.

posted under joomla | No Comments »

Send SMS (TXT Messages) with PHP for free

November18

You can send SMS (TXT Messages) for free to most Phone carriers by sending an email to their Email to SMS gateway.

A list of carriers email to SMS gateways is here:
http://en.wikipedia.org/wiki/List_of_SMS_gateways

I wrote a class based on this a while ago but never got around to finishing it:


< ?php
/**
 * Send SMS via Email to SMS gateways
 *
 * Please note that the list of carreirs is UTF-8 encoded. ie: Mexico != México
 *
 * @author gabe@fijiwebdesign.com
 * @link http://www.fijiwebdesign.com/
 */
class email2sms {
	/**
	 * A list of the major Phone carriers and their Email 2 SMS gateway syntax
	 * Compiled from: http://en.wikipedia.org/wiki/List_of_carriers_providing_SMS_transit
	 */
	protected static $carriers = array(
		array("Airtel", "Andhra Pradesh, India", "#@airtelap.com"),
		array("Airtel", "Karnataka, India", "#@airtelkkk.com"),
		array("Alaska Communications Systems", "USA", "#@msg.acsalaska.net"),
		array("Alltel Wireless Verizon Wireless", "USA", "#@text.wireless.alltel.com,#@mms.alltel.net"),
		array("aql", "UK", "#@text.aql.com"),
		array("AT&T Wireless", "USA", "#@txt.att.net,#@mms.att.net"),
		array("AT&T", "USA", "#@mmode.com"),
		array("AT&T Mobility", "USA", "#@mms.att.net,#@txt.att.net,#@cingularme.com"),
		array("AT&T", "USA", "#@page.att.net"),
		array("Bell", "Canada", "#@txt.bell.ca,#@txt.bellmobility.ca"),
		array("Boost Mobile", "USA", "#@boostmobile.com"),
		array("Bouygues Télécom (company)", "France", "#@mms.bouyguestelecom.fr"),
		array("[[Loop_Mobile_India|Loop] (BPL Mobile)", "Mumbai, India", "#@bplmobile.com"),
		array("Cellular One", "USA", "#@mobile.celloneusa.com"),
		array("Cingular", "USA", "#@cingular.com"),
		array("Centennial Wireless", "United States, Puerto Rico, U.S. Virgin Islands", "#@cwemail.com"),
		array("Cincinnati Bell", "Cincinnati, Ohio, USA", "#@gocbw.com,#@mms.gocbw.com"),
		array("Claro", "Brasil", "#@clarotorpedo.com.br"),
		array("Claro", "Nicaragua", "#@ideasclaro-ca.com"),
		array("Comcel", "Colombia", "#@comcel.com.co"),
		array("Cricket", "", "#@mms.mycricket.com,#@sms.mycricket.com"),
		array("CTI Móvil Claro", "Argentina", "#@sms.ctimovil.com.ar"),
		array("Emtel", "Mauritius", "#@emtelworld.net"),
		array("Fido", "Canada", "#@fido.ca"),
		array("General Communications Inc.", "Alaska", "#@msg.gci.net"),
		array("Globalstar satellite", "", "#@msg.globalstarusa.com"),
		array("Helio", "", "#@myhelio.com"),
		array("Iridium", "", "#@msg.iridium.com"),
		array("i-wireless (Sprint PCS)", "", "#@iwirelesshometext.com"),
		array("Mero Mobile", "Nepal", "#@sms.spicenepal.com"),
		array("MetroPCS", "", "#@mymetropcs.com"),
		array("Movicom", "", "#@movimensaje.com.ar"),
		array("Mobitel", "Sri Lanka", "#@sms.mobitel.lk"),
		array("Movistar", "Colombia", "#@movistar.com.co"),
		array("MTN", "South Africa", "#@sms.co.za"),
		array("MTS Mobility", "Canada", "#@text.mtsmobility.com"),
		array("Nextel", "United States", "#@messaging.nextel.com"),
		array("Nextel", "México", "#@msgnextel.com.mx"),
		array("Nextel", "Argentina", "#@nextel.net.ar"),
		array("Orange Polska", "Poland", "#@orange.pl"),
		array("Personal", "Argentina", "#@alertas.personal.com.ar"),
		array("Plus", "Poland", "#@text.plusgsm.pl"),
		array("PC Telecom", "Canada", "#@mobiletxt.ca"),
		array("Qwest Wireless", "USA", "#@qwestmp.com"),
		array("Rogers Wireless", "Canada", "#@pcs.rogers.com"),
		array("SaskTel", "Canada", "#@sms.sasktel.com"),
		array("Setar", "Aruba", "#@mas.aw"),
		array("Sprint (PCS)", "USA", "#@messaging.sprintpcs.com,#@pm.sprint.com"),
		array("Sprint (Nextel)", "USA", "#@page.nextel.com,#@messaging.nextel.com"),
		array("Suncom", "", "#@tms.suncom.com"),
		array("Sunrise Communications", "Switzerland", "#@gsm.sunrise.ch"),
		array("Syringa Wireless", "USA", "#@rinasms.com"),
		array("T-Mobile", "USA", "#@tmomail.net"),
		array("T-Mobile", "Austria", "#@sms.t-mobile.at"),
		array("T-Mobile", "Croatia", "#@sms.t-mobile.hr"),
		array("Telus Mobility", "Canada", "#@msg.telus.com"),
		array("Tigo", "Colombia", "#@sms.tigo.com.co"),
		array("Tracfone", "", "#@mmst5.tracfone.com,#@txt.att.net,#@tmomail.net,#@vtext.com,#@email.uscc.net,#@message.alltel.com"),
		array("Unicel", "USA", "#@utext.com"),
		array("US Cellular", "USA", "#@email.uscc.net,#@mms.uscc.net"),
		array("Verizon", "USA", "#@vtext.com,#@vzwpix.com"),
		array("Viaero", "USA", "#@viaerosms.com,#@mmsviaero.com"),
		array("Vivo", "Brasil", "#@torpedoemail.com.br"),
		array("Virgin Mobile", "Canada", "#@vmobile.ca"),
		array("Virgin Mobile", "USA", "#@bills.com,#@vmobl.com,#@vmpix.com"),
		array("Vodacom", "South Africa", "#@voda.co.za")
	);

	/**
	 * Send an SMS to a Phone Number
	 * @return Bool
	 * @param $msg String SMS/TXT message
	 * @param $to Int Receivers Phone number
	 * @param $from String From Email address or phone number
	 * @param $carrier String Carrier Name (index of self::$carriers)
	 *
	 * @todo 	at the moment we only send to the first listed email
	 * 			In the future it would be good to check the existence of mailbox at each domain
	 *
	 *
	 */
	public function send($msg, $to, $carrier, $from) {
		$carriers = self::$carriers;
		if (!isset($carriers[$carrier])) {
			throw new Exception('The requested carrier was not found');
			return false;
		}
		$addresses = explode(',', $carriers[$carrier][2]);
		$address = str_replace('#', $to, $addresses[0]);
		if (!mail($address, $msg, $msg, "from:$from\n")) {
			throw new Exception('The Email could not be sent to SMS gateway');
			return false;
		}
		return true;
	}

	/**
	 * Return a Carrier by phone number
	 * @return Array
	 * @param $name String
	 */
	public function getCarrierByNumber($number) {
		// todo
		// here we would query MTA at each domain for mailbox existence
	}

	/**
	 * Return a Carrier by name
	 * @return Array
	 * @param $name String
	 */
	public function getCarrierByName($name) {
		return isset(self::$carriers[$name]) ? self::$carriers[$name] : null;
	}

	/**
	 * Retrieve a list of Carriers by region
	 * @return Array
	 * @param $region String[optional] Return only gateways in this region. eg: "USA" or "India"
	 */
	public function getCarriersByRegion($region = false) {
		$carriers = self::$carriers;
		if ($region) {
			$_carriers = array();
			$region = strtolower($region);
			foreach($carriers as $carrier) {
				if (in_array($region, explode(', ', strtolower($carrier[1])))) {
					$_carriers[] = $carrier;
				}
			}
			return $_carriers;
		}
		return $carriers;
	}

	/**
	 * Retrieve the regions in carrier list
	 * @return Array
	 */
	public function getRegions() {
		$regions = array();
		foreach(self::$carriers as $carrier) {
			$regions[$carrier->region] = 1;
		}
		return array_keys($regions);
	}

}

?>
posted under general | 2 Comments »

PHP File Session Reader

November18

Here is a PHP class that provides an interface for reading the PHP session data.

PHP alone only gives you access to the current users session. This class allows you to read all session files, and thus allow you to read session data across different users.

This may be useful if you want to copy, backups session data, or transfer to a different format/handler etc..

At the moment it only reads file based session handling, with PHP doing the serializing and handling (the default setting). It does not support other session handlers or serializers.

You can extend the base abstract class for different handlers and serializations.

/**
 * Class for retrieving session data when save handler is files
 *
 * Features:
 * Retrieving of all session data saved by PHP, not just the current user
 * Encoding and decoding in PHP session format (not serialize|unserialize)
 *
 * @author gabe@fijiwebdesign.com
 * @link http://www.fijiwebdesign.com/
 *
 */
class FileSessionData extends AbstractSessionData
{

  /**
   * @var array Sesssion Files
   */
  static $sess_files;
  /**
   * @var array Session Ids
   */
  static $sess_ids;

  /**
   * Do not call constructor, use FileSessionData::singleton();
   */
  protected function __construct()
  {
    // configs
    $this->handler = ini_get('session.save_handler');
    $this->save_path = ini_get('session.save_path');

    // make sure session is using files
    if ( $this->handler !== 'files')
    {
      throw new Exception('This class (' . __CLASS__ .') only works for file based session handling.');
    }

    // start session (it may already be started so suppress errors)
    @session_start();
  }

  /**
   * Retrieve a single instance of this class
   *
   * @usage
   *
   * $SessionData = FileSessionData::singleton();
   *
   */
  public static function singleton(array $args = array())
  {
    return parent::singleton(__CLASS__, $args);
  }

  /**
   * Get the session Object for a session id
   *
   * @param string Session ID
   *
   * @return array|bool
   */
  public function get($sess_id)
  {
    $data = $this->read($sess_id);
    if ($data)
    {
      return self::decode($data);
    }
    return false;
  }

  /**
   * Save the session Object for a session id
   *
   * @param string Session ID
   * @param array Session Object
   *
   * @return Bool
   */
  public function set($sess_id, array $sess)
  {
    $data = self::encode($sess);
    return $this->write($sess_id, $data);
  }

  /**
   * Unset a session
   *
   * @param string Session ID
   *
   * @return Bool
   */
  public function un_set($sess_id)
  {
    return unlink($this->getPath($sess_id));
  }

  /**
   * Returns if a session exists given it's ID
   *
   * @param string Session ID
   *
   * @return Bool
   */
  public function is_set($sess_id)
  {
    return file_exists($this->getPath($sess_id));
  }

  /**
   * Write raw session data to a session
   *
   * @param string Session Id
   * @param string Session encoded data
   *
   * @return Bool Write result
   */
  public function write($sess_id, $data)
  {
    return file_put_contents($this->getPath($sess_id), $data);
  }

  /**
   * Read raw session data from a session
   *
   * @param string Session Id
   *
   * @return string Session Data
   */
  public function read($sess_id)
  {
    return file_get_contents($this->getPath($sess_id));
  }

  /**
   * Retrieve all session Ids
   *
   * @return array
   */
  public function getIds()
  {

    if (!self::$sess_ids)
    {

      $files = $this->getFiles();

      self::$sess_ids = array();
      foreach($files as $file) {
        // @todo use substr ?
        $parts = explode('_', $file);
        self::$sess_ids[] = array_pop($parts);
      }
    }

    return self::$sess_ids;

  }

  /**
   * Retrieve the file to the session given its id
   *
   * @param string Session ID
   *
   * @return string Session Path (Path may not exist)
   */
  private function getPath($sess_id)
  {
    return $this->save_path . '/sess_' . $sess_id;
  }

  /**
   * Retrieve the list of session files
   *
   * @return array
   */
  private function getFiles()
  {
    if (!self::$sess_files)
    {
      self::$sess_files = glob($this->save_path . '/sess_*');
    }

    return self::$sess_files;
  }

   /**
   * Decode/Unserialize encoded session data string
   *
   * @param string Session encoded data
   *
   * @return string Session Object
   */
  public static function decode($data)
  {
    return parent::decode($data);

  }

  /**
   * Encode/Serialize Object in session format
   *
   * @param array Session Object
   *
   * @return string Session encoded data
   */
  public static function encode(array $sess)
  {
    return parent::encode($sess);
  }

  /**
   * Maps isset() and unset()
   */
  public function __call($method, $args)
  {
    if ($method == 'isset')
    {
      return call_user_func_array(array($this, 'is_set'), $args);
    }
    else if ($method == 'unset')
    {
      return call_user_func_array(array($this, 'un_set'), $args);
    }
    else
      {
        throw new Exception('Call to non-existant method ' . htmlentities($method) . ' of class ' . htmlentities(__CLASS__));
      }
  }

}

/**
 * Class for retrieving session data
 *
 * Features:
 * Retrieving of all session data saved by PHP, not just the current user
 * Encoding and decoding in PHP session format (not serialize|unserialize)
 *
 * @author gabe@fijiwebdesign.com
 * @link http://www.fijiwebdesign.com/
 *
 * This class must be implemented for each Session Handler as retrieving session data will be different for each
 *
 */
abstract class AbstractSessionData
{

  /**
   * Retrieve a single instance of this class
   *
   * @usage
   *
   * $SessionData = AbstractSessionData::singleton('FileSessionData');
   *
   */
  public static function singleton($classname, array $args)
  {
    static $instance = array();
    if (!isset($instance[$classname]))
    {
      $instance[$classname] = new $classname;
    }

    return $instance[$classname];
  }

  /**
   * Get the session Object for a session id
   *
   * @param string Session ID
   *
   * @return array|Bool
   */
  abstract public function get($sess_id);

  /**
   * Save the session Object for a session id
   *
   * @param string Session ID
   * @param array Session Object
   *
   * @return Bool
   */
  abstract public function set($sess_id, array $sess);

  /**
   * Unset a session
   *
   * @param string Session ID
   *
   * @return Bool
   */
  abstract public function un_set($sess_id);

  /**
   * Returns if a session exists given it's ID
   *
   * @param string Session ID
   *
   * @return Bool
   */
  abstract public function is_set($sess_id);
  /**
   * Write raw session data to a session
   *
   * @param string Session Id
   * @param string Session encoded data
   *
   * @return Bool Write result
   */
  abstract public function write($sess_id, $data);

  /**
   * Read raw session data from a session
   *
   * @param string Session Id
   *
   * @return string Session Data
   */
  abstract public function read($sess_id);

  /**
   * Retrieve all session Ids
   *
   * @return array
   */
  abstract public function getIds();

  /**
   * Decode/Unserialize encoded session data string
   *
   * @param string Session encoded data
   *
   * @return string Session Object
   */
  public static function decode($data)
  {
    // save current session var and empty it
    $sess_orig = $_SESSION;
    $_SESSION = array();

    // decode session data to $_SESSION
    session_decode($data);

    // restore original session
    $sess = $_SESSION;
    $_SESSION = $sess_orig; 

    return $sess;

  }

  /**
   * Encode/Serialize Object in session format
   *
   * @param array Session Object
   *
   * @return string Session encoded data
   */
  public static function encode(array $sess)
  {
    // save current session var
    $sess_orig = $_SESSION;
    $_SESSION = $sess;

    // decode session data in $_SESSION
    $data = session_encode();

    // restore original session
    $_SESSION = $sess_orig; 

    return $data;

  }

}

Example Usage of the PHP File Session Reader Class


// include the classes
require 'FileSessionData.php';

// instantiate the file session reader
$FileSession = FileSessionData::singleton();

// get all saved session ids
$sess_ids = $FileSession->getIds();

// loop through each session id on the server and show session data
foreach($sess_ids as $id)
{
  // get the session data
  if ($data = $FileSession->get($id))
  {
    // dump session array
    dumper($data);
    // dump serialized session
    dumper(FileSessionData::encode($data));

    exit; // lets limit to just one for testing
  }
}

/**
 * Dump utility
 * @param Object
 */
function dumper($data)
{
  echo '' . print_r($data, true) . '';
}
posted under php | No Comments »

Base conversion in PHP, Radix 255

September29

Allows you to convert to any base between 2 and 255, effectively using all the ASCII characters.

In order to convert very large numbers with arbitrary precision you’ll need the BCMath lib. Without BCMath the large numbers will not be converted correctly due to PHP not being able to do the arithmetic.

If you need to convert between bases 2-36, you can use the base_convert() function. However, converting to higher bases such as 255 has some benefits, such as “compressing” the characters.

You can successfully compress SHA1 from a 40 byte hex to a 20 byte string.

echo base255(base_convert(sha1('test'), 16, 10)));

Since there is no loss of data, it can be used as a lossless compression. Normal compression such as zlib won’t work on a SHA1 since there are no repeating patterns.

posted under php | 2 Comments »

PHP Object Cache

June25

The last two days I’ve been writing an Object cache in PHP as part of a larger project. I released it today as open source so it will help those doing something similar and get some that helpful feedback open source offers.

PHP Object Cache is a Memory Object cache, implemented with PHP Sockets. It runs on PHP4 or PHP5+ and requires your PHP build to have sockets enabled.

The project is a web (browser) based chat system called Joomla Ajax Chat. The initial development of the chat was 3 and a half years ago, so many of what worked then (HTTP Polling and AJAX) is becoming old school now and not as efficient as what is possible with browsers today.

One of the new features is implementing comet like HTTP, which essentially means keeping the HTTP connection open for as long as you can. XMPP defined a specification for this called BOSH for their Instant Messaging Protocol to work over HTTP. With a good implementation, Comet can be very efficient.

Our problem is that we have to implement Comet on regular ol’ Apache, Lighty, Nginx, IIS servers running different versions and builds of PHP. The code will have to run on the average shared hosting, dedicated servers to cloud based solutions. The other problem is it has to run on top of Joomla, and other CMSs. A call to a minimal Joomla page alone, is around 5-6 database reads and one or two writes and about 5-6Mb of ram. It wouldn’t take much to crash a shared hosting account trying to implement comet on top of that.

So the solution would have to be an object cache of some sort, even if it means a file based cache (last resort). Thus, PHP Object Cache, which hopefully will work for the percentage of shared server that allow sockets. The idea it to take the most intensive IO processes, such as session management, chat events and put them into the Object cache. When everyone online has viewed what they need from the cache, flush that bit to the database or some other persistent storage if needed.

Since the browser is able to do more as time goes on, hopefully php on old shared servers can keep up.

Incoming mail with PHP Mime Mail Parser

May13

I’ve just added the ability to parse mime mail from standard input into PHP Mime Mail Parser. This allows you to receive and parse email in PHP efficiently and effortlessly. 

To pipe email to PHP, follow one of these articles:

Evolt - Incoming Mail and PHP
DevPapers - Incoming Mail and PHP
DevArticles - Incoming Mail and PHP

Then for your PHP code, get the PHP Mime Mail Parser, and use:

<?php
// include the mime-mail-parser class
require_once('MimeMailParser.class.php');

// instantiate
$Parser = new MimeMailParser();
// read the email from stdin
$Parser->setStream(STDIN);

// get the email parts
$to = $Parser->getHeader('to');
$delivered_to = $Parser->getHeader('delivered-to');
$from = $Parser->getHeader('from');
$subject = $Parser->getHeader('subject');
$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
$attachments = $Parser->getAttachments();

?>

A PHP Mime Mail parser using MailParse Extension

April21

Ever tried parsing Mime Mail? Not for the faint hearted, I assure you. The great thing is that PHP has an extension for parsing Mime Messages called MailParse. The bad news is that using this extension is probably just as hard as writing your own Mime Parser. 

Fortunately with a bit of help I’ve put together a Mime Mail Parser Class that wraps the MailParse extension functions making it simple, efficient and fast to parse mime mail in PHP. 

Why another Mime Mail parser? Well for two main reasons. 

1) Pure PHP implementations are slow and inefficient compared to MailParse.

2) MailParse is too hard to use, and is not OO. 

Therefore welcome to MimeMailParser.

Here is a an example that shows how easy it is to parse raw mime mail using MimeMailParser:

<?php

require_once('MimeMailParser.class.php');

$path = 'path/to/mail.txt';
$Parser = new MimeMailParser();
$Parser->setPath($path);

$to = $Parser->getHeader('to');
$from = $Parser->getHeader('from');
$subject = $Parser->getHeader('subject');
$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
//$attachments = $Parser->getAttachments();
$attachments = $Parser->getAttachmentsAsStreams();

?>

You can find the source code here for your enjoyment.

Secure PHP Programming for Web Developers

February12

Security in PHP is the same as any server side programming language, they are all vulnerable to the same attacks.

PHP has a history of being vulnerable mainly because of its popularity.

1) More non-security aware developers use PHP then any other language, so there code has flaws almost 100% of the time.
2) There are more sites written in PHP then any other server side language, thus more chances to find security holes amongst so many.

PHP has had a rep of having security problems in the PHP core itself, but this has improved greatly. Much of which can be attributed to the Hardened-PHP project.
http://www.hardened-php.net/suhosin/

If you are using a shared host, ask if they have PHP4 with the suhosin patch or PHP5 or higher. I’m not sure if PHP5 still needs Suhosin but it seems many large sites aren’t using the two together so I believe PHP5 has a better security then PHP4 natively.

As a developer I think there are just about 4 or so main security vulnerabilities to keep in mind when coding.

1) XSS (Cross Site Scripting)

This is the most common vulnerability in any website. It is estimated that around 70% of websites have an XSS vulenerability.
http://en.wikipedia.org/wiki/Cross-site_scripting

A simple example in PHP:

<?php

echo $_GET['username'];

?>

What happens is the PHP echo’s a variable passed in from HTTP (in this case a GET parameter). If a user typed in the browser URL:

site.com/example.php?username=<script>alert('document.cookie')</script>

They would see the cookies saved for their session. An attacker can make a user click a link that will also retrieve these cookies from JavaScript, and send it to them - without the user knowing.

To prevent it:

<?php

echo htmlentities($_GET['username'], ENT_QUOTES, 'UTF-8');

?>[

This will turn any HTML into HTML entities. You also have to specify the encoding you used for the page (in this case UTF-8). The reason is so PHP codes not mangle the character encoding, which can also result in XSS.

I must say here, that you should never use your own PHP filtering functions for HTML, or any other "cleansing" of user input. Most likely you will miss something that an attacker will use.

2) XSRF - Cross Site Request Forgery

This is similar to XSS and just as common or maybe even more common. It is when a website fails to protect it's users from being used by 3rd parties without their knowledge.
http://en.wikipedia.org/wiki/Cross-site_request_forgery

And example of this in PHP is a simple comment form.

<form action="submit.php">

<textarea name="comment">
<input type="submit" value="Post Comment" />

</form>

Imagine the comment form is only available for logged in users. Now an attacker can just send an already logged in user the URL:

site.com/submit.php?comment=I hacked you&submit=Post Comment

So when the logged in user clicks on that link, they have posted the comment without knowing. This can even be done in a hidden frame, so the user never see's it.

So the attacker is using the user's already authenticated session (privileges) to do his/her bidding.

Preventing XSRF:

<form action="submit.php">

<textarea name="comment">
<input type="submit" value="Post Comment" />
<input type="key" value="some_random_value" />

</form>

Notice the new <input /> called "key". It will contain a random value remembered by PHP. This random value should be saved, and be unique for every form that is sensitive.

This way, the attacker would not be able to make the user post something on their behalf, since they don't know the value of "key".

(This only works if you don't have an XSS vulnerability of the page itself, as that can lead to the attacker knowing what the value of "key" is)

3) SQL injection

SQL injection is when an attacker manages to manipulate any SQL database queries in your website in a way you didn't intend.
http://en.wikipedia.org/wiki/SQL_injection

Example:

<?php

$query = "SELECT * FROM users where password = '".$_GET['password']."'";
$result = mysql_query($query);

?>

Because the $_GET['password'] can be anything the attacker wishes to put in the URL, they could craft a URL like:


site.com/login.php?username=joe&password=nothing' or 1

Notice the ‘ in the value for the parameter “password”

This will make your sql query:

SELECT * FROM users where password = 'nothing' or 1

This would make it return the first user instead of the user with password = “nothing” since “or 1″ is always true.

Preventing SQL injection:

< ?php

$query = "SELECT * FROM users where password = '".mysql_real_escape_string($_GET['password'])."'";
$result = mysql_query($query);

?>

the function mysql_real_escape_string() will prevent any SQL injection by escaping any character that would otherwise terminate the string.

4) Remote File inclusion

Remote file inclusion is when an attacker can include remote file into your PHP code. This is the most dangerous attack, as it allows the attacker to execute arbitrary code on your PHP server.
http://en.wikipedia.org/wiki/Remote_File_Inclusion

eg:

<?php

include('/pages/'.$_GET['page'].'.php');

?>

With this code the developer is hoping to have a URL such as:

site.com/pages.php?page=home

And this would include the file:

/pages/home.php

However, any attacker can now place a URL such as:


site.com/pages.php?page=../../passwords.txt

And it would reveal the contents of the file passwords.txt

Or they could use it to include a remove file from their server, if the URL wrappers are enabled for file includes (which is common).

It is best not to have any user input in the files to include. However, if you think it is beneficial to your PHP website, then make sure you have a predefined list of files that can be included.

For example:

<?php

if (in_array($_GET['page'], array('home', 'contact', 'links', 'about')) {
    include('/pages/'.$_GET['page'].'.php');
}

?>

With these in mind, you should be able to keep your PHP site secure from most attacks. Now in one paragraph:

When ever you write a PHP page, make sure all input from users (HTTP) is escaped according to the the following rules, HTML just be entity encoded with htmlentities(), XML with htmlspecialchars(), SQL must be escaped by mysql_real_escape_string(). User input should not be used for file inclusions at all, but if you do, then make sure it is from a predefined list of possible files. All forms must contain a secure, random, key that is used to authenticate it only once so that there can be no subsequent posts of the same form, or posts from other sources.

That actually covered all the points above. Can you see how simple it is? It is just keeping those in mind at all times while coding that is the hard part.

Google AJAX Language API with PHP

January20

I had noticed some time ago that Google had released an API for their language translation service. A recent forum discussion made me revisit the API, and since I had a wee bit of time on my hands, I wrote a very rudimentary PHP class implementation of the API.

Google seems to like flaunting “AJAX”, in their APIs at least. So the API is called “Google AJAX Language API” and the main implementation is .. take a guess, AJAX. However, in addition to their pure JavaScript API, they also have a REST interface (of course JavaScript would need such an interface anyway).

The REST interface is just a HTTP endpoint (URL) that returns JSON. You just need to formulate a HTTP GET passing the parameters described in the API documentation, and Google will send you a nicely formated JSON response with the translated text and some other details.

Here is an example request, to translate “Hello World” to Italian.

http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=hello%20world&langpair=en|it

The parameters are q=hello world and langpair=en|it

The JSON response looks like:

{"responseData": {"translatedText":"ciao mondo"}, "responseDetails": null, "responseStatus": 200}

So a simple implementation in PHP would be to use file_get_contents() to download the JSON text from the URL over HTTP and here it is in a PHP class.

http://code.google.com/p/php-language-api/source/browse/trunk/google.translator.php

Example Usage:

// example usage
$text = 'Welcome "to my " website.';
$trans_text = Google_Translate_API::translate($text, '', 'it');
if ($trans_text !== false) {
	echo $trans_text;
}

The class uses file_get_contents() which assumes your PHP has allow_url_fopen directive enabled in the PHP configuration (PHP.ini). This is usually the case, however, it can be disabled for security reasons (since it allows include() to use the URL wrapper and thus include remote files - a favourite exploit for attackers it to inject remote files into include() functions)

The class doesn’t use a JSON parser, I think its a bit of an overhead including the JSON libraries in PHP4. Intead it just uses regular expressions. The PCRE regular expression functions are pretty fast. PHP5 has native support for JSON however, so the class could be modified to use the PHP5 native JSON functions if you use PHP5 specifically.

Something I ran into was that Google returns not only UTF-8, but UTF-8 escape sequences. That is, they have characters outside the basic ASCII range escaped with the UTF-8 escape sequence which is \u followed by the character’s hex value. For example, the & symbol becomes:

\u0026

Cools aye. Sucks, because PHP does not understand this. JavaScript, which is the main method of invoking the Google Language API, understands this natively. PHP doesn’t even understand UTF-8 in PHP4. First I resorted to this ugly function to unescape the UTF-8 escape sequences (convert those UTF-8 sequence to actual UTF-8 byte sequences).

/**
         * Convert UTF-8 Escape sequences in a string to UTF-8 Bytes. Old version.
         * @return UTF-8 String
         * @param $str String
         */
        function __unescapeUTF8EscapeSeq($str) {
                return preg_replace_callback("/\\\u([0-9a-f]{4})/i", create_function('$matches', 'return html_entity_decode(\'\'.$matches[1].\';\', ENT_NOQUOTES, \'UTF-8\');'), $str);
        }

The function is ugly because it uses html_entity_decode() to do the transformation for us. We just convert the UTF-8 escape sequence to a HTML escape sequence (HTML entities), then use html_entity_decode() which PHP handles well. I decided on a compatible function that uses bitwise operations instead. Both are included in the source however for reference.

The code is very early development and will be buggy. You can check out the latest sources via SVN:

svn checkout http://php-language-api.googlecode.com/svn/trunk/ php-language-api-read-only

Feel free to let me know on the Google project page if you find any bugs.

http://code.google.com/p/php-language-api/issues/list

Tag Cloud