PHPSecInfo: Alternatif PHPINFO()

Seperti saya tulis sebelumnya, PHPINFO() adalah tool ampuh buat programmer seperti kita yang menggunakan jasa shared hosting. Hanya saja kita perlu lebih hati-hati untuk tidak membiarkannya terbuka begitu saja untuk dibaca orang lain.

Tool serupa yang bisa kita gunakan, khususnya pengguna jasa shared hosting adalah PHPSecInfo(). Tampilannya mirip dengan PHPINFO(), hanya saja informasi yang ditampilkan bukan konfigurasi teknis dari PHP engine anda, melainkan aspek security yang mesti kita perhatikan. Atau lebih tepatnya, yang mesti diperhatikan penyedia jasa hosting website anda.

PHPSecInfo bisa dikatakan seperti security auditor untuk kita. Karena yang ditampilkan bukan hanya warning atau notice terhadap setting PHP yang dianggap membahayakan, namun juga pass untuk setting yang dianggap sudah benar atau aman.

Contoh-contoh tampilannya adalah seperti berikut:

phpsecinfo warning

phpsecinfo notice

phpsecinfo pass

Karena informasi yang ditampilkan adalah aspek security dari setting PHP anda maka anda harus lebih hati-hati dalam menggunakannya. Jangan sampai informasi ini terlihat oleh orang lain.

Source

Kalau anda telah download source-nya, anda bisa melihat bahwa PHPSecInfo merupakan kumpulan dari class-class library. Ini artinya anda bisa saja menggunakan script tersebut untuk membuat report sesuai yang anda butuhkan dalam format yang anda inginkan.

Credit

PHPSecInfo dirilis oleh PHP Security Consortium dan ditulis oleh Ed Finkler.

Ngintip PHPINFO()

Seperti saya tulis dalam posting sebelumnya, beberapa hari lalu tim pengembang PHP telah merilis PHP versi 5.2 yang merupakan versi paling stabil dari seri 5.x. Banyak perbaikan dan penyempurnaan yang dilakukan oleh mereka, termasuk dan terutama dari sisi security. Oleh karena itu sangat dianjurkan bagi pemakai PHP, khususnya penyedia jasa hosting, untuk mengupgrade PHP mereka dengan versi 5.2 ini.

Bicara tentang hosting, sepertinya sudah menjadi kebiasaan programmer untuk memastikan apakah PHP yang diinstal penyedia jasa hosting mereka persis seperti yang diinginkan. Dan cara paling mudah untuk itu adalah menggunakan PHPINFO() seperti berikut:

<?php
  PHPINFO();
?>

Namun seringkali mereka lupa untuk menghapus file tersebut (atau sengaja?). Padahal search engine seperti Google atau Yahoo! punya bot yang bisa merekam apa saja, termasuk skrip PHPINFO() anda.

Memang skripnya sendiri tidak berbahaya, tapi seperti yang ditulis ilia, informasi yang ditampilkan akan menjadi berharga bagi hacker nakal yang ingin menyerang web site anda. Terutama anda yang menggunakan PHP versi lama yang masih rentan terhadap serangan XSS atau CSRF.

Coba saja query di Google atau Yahoo!. Atau tambahkan dengan site:.id untuk mengkhususkan pencarian pada domain .id seperti ini. Dari percobaan secara random bisa dikatakan hampir semuanya valid (bukan sekedar cache yang sudah tidak ada lagi).

ClientLogin Authentication for Zend GData

As i mentioned in my previous post, i was going to add AuthSub authentication for Ngeblog when people at Google Code team announced that Zend framework now has GData support.

After played around with it for hours, i finally got into a decision to use Zend Gdata for abstracting Ngeblog connection to Blogger. Unfortunately, Zend GData class library only supports AuthSub authentication, while Ngeblog already uses ClientLogin authentication and works fine so far.

So, to make it available for both type of authentication, i finally sat down and wrote some codes myself to add ClientLogin support for Zend GData. You can download the bundle in .zip here or in .tgz here which contains both original Zend_Gdata bundle and my Zend_Gdata_ClientLogin class for ClientLogin authentication.

How it works

To understand how this class works, you must first understand how Google account authentication works. Please read the manual for that. But i try to explain it anyway.

Authentication is required to access any of Google Services such as Google Calendar, Google Base or Blogger. To do that, first you must provide username and password to log into your Google account. And then once your login is authorized, Google will give you a token to identify yourself for accessing the desired Google Service.

Currently there are two kind of authentication that Google uses. AuthSub authentication and ClientLogin authentication. As the manual said, AuthSub is used for web application that offers a service to access Google Service. While ClientLogin is used for installed application, such as desktop or handheld application.

But that doesn’t mean you can’t use ClientLogin for web application. It’s just that with ClientLogin authentication you must handle the authentication programmatically yourself to get the token. While with AuthSub you only need to redirect your web users to log into Google Account web site and grab the token as the result once they authorized.

Now, let’s get to the business. To use this class, you must first include Zend.php and load Zend_Gdata_ClientLogin class, like this:


  require_once 'Zend.php';
  Zend::loadClass('Zend_Gdata_ClientLogin');

Then use getClientLoginAuth method to get the token (authorization code), like this:

  $username     = 'yourusername';
  $password     = 'yourpassword';
  $service      = 'blogger';
  $source       = 'Ngoprekweb-Zend_Gdata-0.1.1'; // companyName-applicationName-versionID

  try {
    $resp = Zend_Gdata_ClientLogin::getClientLoginAuth($username,$password,$service,$source);
    print_r($resp);
  } catch ( Exception $e )  {
    echo $e->getMessage();
  }

As you can see, there are four parameters required for this method: username, password, service and source. You can read about these parameters here.

The results of this method will be in three possibilities:

First

, if the authentication is success (authorized by Google), the output will be something like this,

Array
(
    [response] => authorized
    [auth] => DQAAAGgA...dk3fA5N
)

Second, if the authentication is failed for some reasons, it throws exception. About the reason of this failure, the manual said:

Please note that ClientLogin does not differentiate between a failure due to an incorrect password or one due to an unrecognized user name (for example, if the user has not yet signed up for an account).

Third

, if Google requires you to solve CAPTCHA challenge, the output will be something like this,

Array
(
    [response] => captcha
    [captchatoken] => DQAAAGgA...dkI1LK9
    [captchaurl] => http://www.google.com/login/captchaALD$ALSJ4.png
)

About CAPTCHA challenge, the manual said,

ClientLogin uses standard security measures to protect user account information. To block bots and other entities from breaking user passwords, Google Accounts may add a visual CAPTCHA� to the authentication process when the server suspects an illegal intrusion, such as after too many incorrect login attempts. A CAPTCHA ensures that a real person is attempting login, and not a computer trying random strings (a dictionary attack).

Handling CAPTCHA Challenge

As i mentioned above, when Google requires you to answer CAPTCHA challenge (when she suspects you as an intruder :)), you’ll get both captchatoken for identifying which CAPTCHA image you received, and captchaurl that shows you the location of the image you have to answer (to tell miss Google you are human, not bot).

To answer that challenge, you use the same getClientLoginAuth method, only now with two additional parameters: captchatoken and captchaanswer.

  $username     = 'yourusername';
  $password     = 'yourpassword';
  $service      = 'blogger';
  $source       = 'Ngoprekweb-Zend_Gdata-0.1.1'; // companyName-applicationName-versionID
  $captchatoken = 'DQAAAGgA...dkI1LK9';
  $captchaanswer= 'brinmar';

  try {
    $resp = Zend_Gdata_ClientLogin::getClientLoginAuth($username,$password,$service,$source,$captchatoken,$captchaanswer);
    print_r($resp);
  } catch ( Exception $e )  {
    echo $e->getMessage();
  }

If you ARE really human, then most likely you’ll get something like this as the result,

Array
(
    [response] => authorized
    [auth] => DQAAAGgA...dk3fA5N
)

which means you’re now authorized to use Google Service you requested, in this case is Blogger. Use $resp['auth'] as your token (authorization code) to do the rest of operation (query, add, edit or delete posts in your Blogger).

In Action: Reading Blogger

This far, some of you might said, “what in Google Earth is this guy talking about?!”. 🙂

Alright kids, grab your emacs or UltraEdit, here comes the example. What we’re going to do here is to get the entries of your Blogger, for comparing purpose with what Zend does using AuthSub (see my previous post here).

<?php
/**
 * Testing Zend_Gdata_ClientLogin for getting list of Blogger entries
 *
 * written by: Eris Ristemena (http://www.ngoprekweb.com/tags/php)
 *
 */

  set_include_path(dirname(__FILE__) . '/Zend_Gdata');
  require_once 'Zend.php';
  Zend::loadClass('Zend_Gdata_ClientLogin');
  Zend::loadClass('Zend_Gdata');
  Zend::loadClass('Zend_Feed');

  $username     = 'yourusername';
  $password     = 'yourpassword';
  $service      = 'blogger';
  $source       = 'Ngoprekweb-Zend_Gdata-0.1.1'; // companyName-applicationName-versionID
  $logintoken   = $_GET['captchatoken'];
  $logincaptcha = $_GET['captchaanswer'];

  try {
    $resp = Zend_Gdata_ClientLogin::getClientLoginAuth($username,$password,$service,$source,$logintoken,$logincaptcha);

    if ( $resp['response']=='authorized' )
    {
      $client = Zend_Gdata_ClientLogin::getHttpClient($resp['auth']);
      $gdata = new Zend_Gdata($client);
      $feed = $gdata->getFeed("http://www.blogger.com/feeds/default/blogs");

      foreach ($feed as $item) {
        echo '<h3><a href="'.$item->link("alternate").'">' . $item->title() . '</a></h3>';
        $_id = explode("/",(string) $item->id());
        $blogid = $_id[count($_id)-1];
        $feed1 = $gdata->getFeed("http://www.blogger.com/feeds/$blogid/posts/summary");

        echo "<ul>";
        foreach ($feed1 as $item1) {
          echo "<li>";
          echo "<a href=\"{$item1->link('alternate')}\">{$item1->title()}</a><br />";
          echo "{$item1->summary()}<br />";
          echo "</li>";
        }
        echo "</ul>";
      }
    }
    elseif ( $resp['response']=='captcha' )
    {
      echo 'Google requires you to solve this CAPTCHA image <br />';
      echo '<img src="'.$resp['captchaurl'].'" /><br />';
      echo '<form action="'.$_SERVER['PHP_SELF'].'" method="GET">';
      echo 'Answer : <input type="text" name="captchaanswer" size="10" />';
      echo '<input type="hidden" name="captchatoken" value="'.$resp['captchatoken'].'" />';
      echo '<input type="submit" />';
      echo '</form>';
      exit;
    }
    else
    {
      // there is no way you can go here, some exceptions must have been thrown
    }

  } catch ( Exception $e )  {
    echo $e->getMessage();
  }

?>

Have fun. And please be kind to drop some comments below or report any bugs you find to my email (eristemena at ngoprekweb dot you know what).

Moving to PHP 5.2

The PHP development team has announced the immediate release of PHP 5.2.0. This release is a major improvement in the 5.X series, which includes a large number of new features, bug fixes and security enhancements.

Further details about this release can be found in the release announcement 5.2.0, and the full list of changes is available in the ChangeLog PHP 5.

To me, one of the most important thing that come with this new release is the addition of input filtering extension which enabled by default. If you have no idea about this extension then you should take a look at this tutorial from Zend developer zone.

For many years, unlike any other languages, PHP did not have any standards functions to filter out data from external world (like cgi or perl). You’d likely have to write additional codes yourself to do this. So this input filter

extension which for some time sit in PECL package surely makes our lives easier.

As an illustration, this is what you’d do to make sure that only integer is passed by query string,

if (isset($_GET['mode'])) {
    if (!is_numeric($_GET['mode'])) {
        echo "The 'mode' argument must be a valid integer.";
        exit();
    }
    $mode = (int)$_GET['mode'];
} else {
    echo "The 'mode' argument is missing.";
    exit();
}

This is what you can do using filter extension,

$mode = filter_input(INPUT_GET, 'mode', FILTER_VALIDATE_INT);

if (is_null($mode)) {
    echo "The 'mode' argument is missing.";
    exit();
} elseif ($mode === false) {
    echo "The 'mode' argument must be a valid integer.";
    exit();
} else {
    echo "mode is: $mode.";
}

Of course there are other filter you can use, like filtering URL, Email or IP. All are described in its manual.

PHP Community Playing Cards

286002715 ba111d6051

286002973 2d56c4a673

286003017 bc1a79cde6286003045 7483dfbc98

Thanks to Cal Evans, this playing cards is really useful especially when you need to go to the conference without any idea who are the folks behind you.

100 Million Web Sites was Reached by October 2006

dsc00516

Netcraft has announced that web sites passed significant milestone during last month: 100 million web sites!

100 million? That’s few. Surely there can’t be only 100 million web sites out there!

Hold on a minute, let’s take a closer look at what Netcraft considers a web site:

“There are now 100 million Web sites with domain names and content on them,” said Netcraft’s Rich Miller.

Yeah, that we all can agree. What it means that any domain name without content in it doesn’t count as a web site. That includes registrar parking pages, Google ad farms, and the like.

Also any blogger which hosted on blogspot.com or wordpress.com are counted as one, although there are actually many of them.

What interesting to me is the raising curve of new web sites during the past three years,

There were just 18,000 Web sites when Netcraft, based in Bath, England, began keeping track in August of 1995. It took until May of 2004 to reach the 50 million milestone; then only 30 more months to hit 100 million, late in the month of October 2006.

That could only mean one thing, it’s much easier to create a Web site nowadays. And blog is one of the web app which responsible for that.

Anyway, i wonder if Netcraft counted this blog right, since i have two other domains directed here (blog.ngoprek.web.id and ngoprekweb.ngikutin.com). 🙂

Zend Google data API

I was going to implement AuthSub authentication for Ngeblog, when i found that Zend has done it all for me. Zend framework now has GData supported.

As usual, i grabbed it and tasted it a bit. And as you’ve might suspected, sweet!

Here you go, try it out yourself.

<?php
/**
 * Testing Zend_Gdata for reading Blogger
 * Written by Eris Ristemena (http://www.ngoprekweb.com)
 *
 */

set_include_path(dirname(__FILE__) . '/Zend_Gdata');
require_once 'Zend.php';
Zend::loadClass('Zend_Gdata_AuthSub');
Zend::loadClass('Zend_Gdata');
Zend::loadClass('Zend_Feed');

session_start();

if (!isset($_SESSION['blogger_token'])) {
        if (isset($_GET['token'])){
                //convert the single-use token to a session token
                try {
                $session_token =  Zend_Gdata_AuthSub::getAuthSubSessionToken($_GET['token']);
                $_SESSION['blogger_token'] = $session_token;
        } catch ( Exception $e )  {
      echo $e->getMessage();
      exit;
    }
        } else {
                //display link to generate single-use token
                echo 'Click <a href="'. Zend_Gdata_AuthSub::getAuthSubTokenUri('http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'],"http://beta.blogger.com/feeds",0,1).'">here</a> to authorize this application.';
                exit();
        }
}

if (isset($_GET['logout'])) {
        Zend_Gdata_AuthSub::AuthSubRevokeToken($_SESSION['blogger_token']);
        unset($_SESSION['blogger_token']);
        header('Location: '.$_SERVER['PHP_SELF']);
        exit;
}

echo '<a href=
  "'.$_SERVER['PHP_SELF'].'?logout">Logout</a>'; echo '<hr size="1" />';
try {
  $client = Zend_Gdata_AuthSub::getHttpClient($_SESSION['blogger_token']);
  $gdata = new Zend_Gdata($client);
  $feed = $gdata->getFeed("http://www.blogger.com/feeds/default/blogs");

  foreach ($feed as $item) {
    echo '<h3><a href="'.$item->link("alternate").'">' . $item->title() . '</a></h3>';
    $_id = explode("/",(string) $item->id());
    $blogid = $_id[count($_id)-1];
    $feed1 = $gdata->getFeed("http://www.blogger.com/feeds/$blogid/posts/summary");

    echo "<ul>";
    foreach ($feed1 as $item1) {
      echo "<li>";
      echo "<a href=\"{$item1->link('alternate')}\">{$item1->title()}</a><br />";
      echo "{$item1->summary()}<br />";
      echo "</li>";
    }
    echo "</ul>";
  }
} catch ( Exception $e )  {
  echo $e->getMessage();
}

?>

PHP Programming Innovation Award

logo

I received an email from PHPClasses.org this evening,

This is a notification message to let you know that your package Ngeblog was nominated to the PHP Programming Innovation Award among all the classes approved for publishing in the PHP Classes site during the month of October of 2006.

The class was nominated by Manuel Lemos that submitted the following comment:

“Blogger is a very popular blog Web site owned by Google. Nowadays it provides a Web services API named GData API. It makes possible to manipulate articles posted in Blogger blog from an external site. This class makes use of the GData API to manipulate Blogger posts from any PHP Web site.”

Well, when i started this project about two weeks ago, i never planned it to be nominated to any award. Like any other open source projects, it all just started with “i wanna do this, and i wanna do it now” motivation.

But then i looked at the prize and i thought why not. 🙂

So please be kind to vote it for me.