Showing posts with label PHP. Show all posts
Showing posts with label PHP. Show all posts

Tuesday, December 6, 2011

PHP Command Line Telnet Client

A while ago I wrote a single line PHP Command line client.

while (1) {  fputs(STDOUT, "\n\-PHP$ "); eval(trim(fgets(STDIN))); }

Recently I needed to test an XMPP server and found out that Windows7 does not have telnet enabled by default. Usually I'd just use putty as it supports telnet also but wondered if I could just do this from the command line via PHP. Well here it is, a PHP command line telnet client.

echo "PHP Telnet Client. (c) 2011 Fiji Web Design, http://www.fijiwebdesign.com.\n";

$opts = getopt("h:p:") or die("Invalid options. Please supply -h [host] -p [port]");

$host = $opts['h'];
$port = $opts['p'];

$fp = fsockopen($host, $port) or die("Could not connect to host ($host) on port ($port)");
echo "Connected to server...\n";

stream_set_blocking($fp, 0);

while (1) {

  $input = (fgets(STDIN));
  fwrite($fp, $input) or die('Could not write to server');
  sleep(1); // let server respond

  $out = '';
  while($buf = fread($fp, 2028)) {
    $out .= $buf;
  }

  if ($out != '') echo $out;

}


Now that isn't one line like the PHP command line client. Also, if you are on windows and a version of PHP lower then 5.3, you will not have the getopt() function. To solve this here is a substitute for getopt().

if (!function_exists('getopt')) {

  function getopt($opts) {
      $argv = $_SERVER["argv"];
      $result = false;
      $opts_array = explode(':', $opts);
      foreach($opts_array as $opt) {
          $key = array_search('-' . $opt, $argv);
          if($key && !in_array($argv[$key+1], $opts_array)) {
              $result[$opt] = trim($argv[$key+1]);
          } elseif($key) {
              $result[$opt] = '';
          }
      }
      return $result;
  }

}

Save the PHP code to a file, I call it telnet.php. Then open the shell and navigate to the directory which has telnet.php and type in:

php telnet.php -h [hostname] -p [port]

For example:

php telnet.php -h google.com -p 80

This will open a connection to google.com on the http port. Then you can type in your HTTP headers:

GET / HTTP/1.1
HOST: google.com

Then press enter twice, because HTTP requires that you send a newline to terminate the HTTP headers. Google.com should respond with the headers and HTML of the Google website.

You can telnet into any listening TCP port so for instance you can test XMPP servers.

php telnet.php -h talk.google.com -p 5222

Then send your XMPP stanzas.

Or even telnet into an email server and send or retrieve emails.

Notes


The socket connection to the server your are telneting to is currently non-blocking while the read from STDIN is currently blocking. This causes the response from the server to not show until you hit enter (send \n) a few times. It would be better design to have both streams non-blocking and do a socket_select() call but the current works for now and does not require socket_select() support which I believe requires php built with sockets support. The current implementation should work without that support but does use up a lot of CPU with the while(1) loop.

Sending email from PHP on Windows

I'm assuming you've installed Xampp or WAMP on your local windows machine. If not please visit the XAMPP website. WAMP and XAMPP will install the full windows equivalent of the LAMP (Linux Apache MySQL PHP/Perl/Python) stack on your Windows Machine, and offer a GUI to manage it. Here I'll talk about sending email from windows specifically on XAMPP, but you can follow this for WAMP or your own PHP setup.

I'm also assuming you're trying to send email through the PHP function "mail()".

Unlike Linux, Windows is not distributed with a default mail transfer agent (MTA). Linux flavors usually come with sendmail, which acts as a local MTA.

In order to have PHP send email, your windows machine should be able to send email, and have PHP configured to send email through it. So first you need to get your machine to send email.

You can either install an MTA locally (not recommended since sending emails is a complicated by spam detection mechanisms built into the protocol).
http://www.google.com/Top/Computers/Software/Internet/Servers/Mail/
http://en.wikipedia.org/wiki/List_of_mail_servers

Or use a free MTA such as hotmail.com or gmail.com. You need an account.

To use a free MTA, you need to specify the MTA host, port, user and password. Luckily there are also sendmail like programs for windows and one is included with Xampp that makes this easy.

You will just need to configure PHP to use the sendmail binary provided by Xampp, to send emails. To do this edit the PHP configuration file, php.ini. You can find this in xampp by opening the control panel, clicking "explore" and going to the folder "php". The full path should be something like:

c:/xampp/php/php.ini

Look for:
;sendmail_path = "\"C:\xampp\sendmail\sendmail.exe\" -t"

and remove the ";" from the beginning of that line, to enable that configuration directive.

Now your PHP is configured to use the sendmail program that comes with Xampp for win.

You now need to configure your sendmail program to send email to your SMTP server.

So open up the sendmail.ini in the "sendmail" folder.
c:\xampp\sendmail\sendmail.ini

Create a new account configuration. Example for gmail:

# Gmail example
account Gmail
tls on
tls_certcheck off
host smtp.gmail.com
from [email protected]
auth on
user [email protected]
password mypassword

Substitute myuser and mypassword for your details.

Now you need to make this account the default by editing the last line in the file to:

# Set a default account
account default : Gmail

You will then need to restart the apache service. You can do this from the Xampp control panel. (stop/start). This reloads the configuration for PHP.

Now you should be able to send email.

Tuesday, August 26, 2008

Quoting Strings in SQLite with PHP

Unlike MySQL, SQLite follows the quoting standards in SQL strictly and does not understand the backslash \ as an escape character. SQLite only understands escaping a single quote with another single quote.

For example, if you receive the input data 'cheeky ' string' and use the PHP function addslahes() to escape literal characters in the string then you will get 'cheeky \' string' which according to SQLite is not escaped properly. You need to escape the string so that it looks like 'cheeky '' string'.

If you have magic_quotes turned on then you are in even more trouble. This PHP setting escapes all HTTP variables received by PHP with an equivalent of addslshes(). So the correct way to escape strings in SQLite would be:

function sqlite_quote_string($str) {
 if (get_magic_quotes_gpc()) {
  $str = stripslashes($str);
 }
 return sqlite_escape_string($str);
}
This will remove the escape characters added by the magic_quotes setting, and escape strings with SQLites sqlite_escape_string() function which correctly escapes the string with '.

Creating a custom SQLite Function in PHP

SQLite is available in PHP5 either by compiling PHP5 with SQLite support or enabling the SQLite extension dynamically from the PHP configuration (PHP.ini). A distinct feature of SQLite is that it is an embedded database, and thus offers some features a Server/Hosted database such as the popular MySQL database doesn't.

Creating Custom Functions in SQLite

One of the really cool features of SQLite in PHP is that you can create custom PHP functions, that will be called by SQLite in your queries. Thus you can extend the SQLite functions using PHP.

A custom Regexp function for SQLite in PHP

// create a regex match function for sqlite
sqlite_create_function($db, 'REGEX_MATCH', 'sqlite_regex_match', 2);
function sqlite_regex_match($str, $regex) {
 if (preg_match($regex, $str, $matches)) {
  return $matches[0];
 }
 return false;
}
The above PHP code will create a custom function called REGEX_MATCH for the SQLite connection referenced by $db. The REGEX_MATCH SQLite function is implemented by the sqlite_regex_match user function we define in PHP.

Here is an example query that makes use of the custom function we created. Notice that in the SQLite query, we call our custom function REGEX_MATCH:

$query = 'SELECT REGEX_MATCH(link, \'|http://[^/]+/|i\') AS domain, link, COUNT(link) AS total'
 .' FROM links WHERE domain != 0'
 .' GROUP BY domain'
 .' LIMIT 10';
$result = sqlite_query($db, $query);
This will make SQLite call the PHP function sqlite_regex_match for each database table row that is goes over when performing the select query, sending it the link field value as the first parameter, and the regular expression string as the second parameter. PHP will then process the function and return its results to SQLite, which continues to the next table row.

Custom Functions in SQLite compared to MySQL

In comparison with MySQL, you cannot create a custom function in PHP that mysql will use. MySQL allows creation of custom functions, but they have to be written in MySQL. Thus you cannot extend MySQL's query functionality with PHP.

I believe the reason for this is simply because having a callback function called on the client, by the database, over a Client-Server model for each row that has to be processed would be just inefficient. Imaging processing 100,000 rows in a MySQL database and having MySQL make a callback to PHP over a TCP connection, the overhead of sending the data back and forth for the callback would be way too much.
With an embedded database like SQLite, this isn't the case since making the actual communication between the language and the embedded database does not pose such a high overhead.

Thursday, August 21, 2008

XSS (Cross Site Scripting) and stealing passwords

XSS (Cross Site Scripting) would be viewed by most web developers as the stealing of users session cookies by injecting JavaScript into a web page through URL. You do not associate it with stealing passwords, but worse then stealing session cookies, it can steal a users username and password directly from the browser.

Many users choose to have the browser remember their login credentials. So when ever they visit a login form, their username and password fields are pre-populated by the browser. Now if there is an XSS vulnerability on that login page, then a remote attacker can successfully retrieve the users username and password.

Hello World in XSS

You have a page that has an XSS vulnerability. Let say a website has a PHP page, mypage.php with the code:

<?php

// the variable is returned raw to the browser
echo $_GET['name'];

?>
Because the variable $_GET['name'] is not encoded into HTML entities, or stripped of HTML, it has an XSS vulnerability. Now all an attacker has to do is create a URL that a victim will click, that exploits the vulnerability.
mypage.php?name=%3Cscript%3Ealert(document.cookie);%3C/script%3E
This basically will make PHP write <script>alert(document.cookie);</script> onto the page, which displays a modal dialog with the value of the saved cookies for that domain.

How Does stealing passwords with XSS work?

The example above displays the cookies on the domain the webpage is on. Now imagine the same page has a login form, and the user chose to have their passwords remembered by the browser. Lets say the PHP page looks like this:

<?php

// the variable is returned raw to the browser
echo $_GET['name'];

?>

<form action="login.php">
<input type="text" name="username" />
<input type="password" name="password" />
<input type="submit" value="Login" />
</form>

Now an attacker just needs to craft a URL that retrieves the username and password. Here is an example that retrieves the password:
mypage.php?name=%3Cscript%3Ewindow.onload=function(){alert(document.forms[0].password);}%3C/script%3E

As you can see, it is just a normal XSS exploit, except it is applied to the username and password populated by the browser after the window.onload event.

Password stealing XSS vs Session Cookie stealing XSS

Well, they are both suck from a developers perspective. According to Wikipedia, 70% or so of websites are vulnerable to XSS attacks.

As a developer, I've always thought of XSS as an exploit on a users session, just as CSRF/XSRF (Cross Site Request Forgery), which requires an active session. Now, as you can see, XSS of the type described does NOT require an active session. The user does not have to be logged into the site. They could have logged out 10 years ago, but as long as the browser remembers their login credentials, the XSS exploit can steal those login credentials.

Due to its ability to be executed without having the user logged into a website, this exploit should be regarded worse then session based XSS.

Proof of Concept

Fill in the form below with dummy values and click the "Login" button.

Login Form
Username:
Password:

Now return to the same page, to simulate logging out. Now click the Exploit. This will simulate an XSS exploit on this page, and alert the saved password.

I've set up a proof of concept based on an actual XSS exploit here: http://xss-password.appjet.net/.

Preventing Stealing Passwords via XSS

The only way I can think of right now is to give your username and password fields unique names so that the browser does not remember their values. In PHP you can do this with the time() function. eg:

<input type="password" name="pass[<?php echo sha1(time().rand().'secret'); ?>]" />
The unique names prevents the browser from remembering the password field. This should work universally in all browsers.

Thursday, June 5, 2008

Synchronizing Date and Time in different Timezones with PHP, MySQL and JavaScript

How do you show the correct date and time (timestamp) to users in differnet time zones? In theory it should be simple:

Save your your date and time with the correct timezone

The date and time with timezone is a timestamp. Though implementations differ, timestamps basically contain the same information (date/time and timezone).The timezone can be explicitly recorded in the timestamp (eg: 2005-10-30 T 10:45 UTC) or implicitly taken from the context in which the timestamp was generated or recorded (eg: unix timestamp is dependent on timezone of server generating the timestamp).

Something as simple as saving a timestamp in mysql with PHP can be not so simple due to the difference in the timestamp representation in the two languages.

The PHP timestamp is defined as the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) while the mysql timestamp is a representation of the present date and time in the format YYYY-MM-DD HH:MM:SS.

If you save the timestamp as a mysql timestamp field, then the timestamp is saved as UTC, however, when you access the timestamp it is converted to the timezone set in the mysql server, so basically you don't get to see the stored UTC version of the timestamp. If you save it as a PHP timestamp in a varchar or unsigned int field then it would be subject to the the PHP servers timezone. So in essence both the MySQL and PHP timestamp a dependent on the timezone of their respective servers.

Whichever format you save it in, just remember that both the PHP and MySQL timestamps reference the timezone on the server they are saved on, PHP during generation of the timestamp, and mysql during retrieval.

Retrieve the timezone of the user to whom you will display the date and time to

The easy way to do this is ask the user what their timezone is. You see this used in many registration forms on websites as well as many open source forums, CMSs, blog software etc. However, not every user will even bother giving you the correct timezone.

You can use JavaScript if it is available on the browser:

var tz = (new Date().getTimezoneOffset()/60)*(-1);
This depends on the users computer's clock, so if it is set wrong, then you will get a wrong result. Time is relative however, so if a user wants to be a few hours behind, let them be.

You can use the users IP address to assume their geographic location and thus their timezone. This can be done server side and thus is not dependent on the users browser having JavaScript. Using the IP to determine timezone is dependent on the accuracy of your IP geocoding service you use. Here is an example using the hostip.info API for geocoding and earthtools.org for lat/long conversion to timezone.

<?php
/**
* Retrieves the Timezone by the IP address
* @param String $IP (optional) IP address or remote client IP address is used
*/
function getTimeZoneByIP($IP = false) {

 // timezone
 $timezone = false;

 // users IP
 $IP = $IP ? $IP : $_SERVER['REMOTE_ADDR'];

 // retrieve geocoded data from http://hostip.info/ API in plain text
 if ($geodata = file('http://api.hostip.info/get_html.php?ip='.$IP.'&position=true')) {
  // create an associative array from the data
  $geoarr = array();
  foreach($geodata as $line) {
   list($name, $value) = explode(': ', $line);
   $geoarr[$name] = $value;
  }
  
  // retrieve lat and lon values
  $lat = trim($geoarr['Latitude']);
  $lon = trim($geoarr['Longitude']);
  
  if (strlen($lat) > 0 && strlen($lon) > 0) {
   // pass this lat and long to http://www.earthtools.org/ API to get Timezone Offset in xml
   $tz_xml = file_get_contents('http://www.earthtools.org/timezone-1.1/'.$lat.'/'.$lon);
   // lets parse out the timezone offset from the xml using regex
   if (preg_match("/<offset>([^<]+)<\/offset>/i", $tz_xml, $match)) {
    $timezone = $match[1];
   }
  }

 }
 return $timezone;
}
?>

You can also use a combination of the three in order to correlate the data and get a better guess of the timezone.

Calculate the difference in hours between the saved date and time and the users date and time

Now that we have the timestamp and the users timezone, we just need to adjust the timestamp to their timezone. First we need to calculate the difference between the timezone the timestamp is saved in, as the users timezone.

$user_tz_offset = $tz_user - $tz_timestamp; 
where $user_tz_offset is how far ahead or behind in hours the user timezone is from the timestamps timezone.

Add the difference in hours to the saved date and time and display

Now we have all we need to show the correct time to the user based on their timezone. Example in pseudo code:

$user_tz_offset = $tz_user - $tz_timestamp; 
$users_timestamp = $timestamp + $user_tz_offset;

Sunday, January 20, 2008

Cleaning xHTML markup with PHP Tidy

Everyone makes mistakes. Even the best xHTML coders will sometimes write invalid xHTML. Not to worry, PHP can automatically clean up xHTML before display using the PHP Tidy Extension.

PHP Tidy uses the Tidy Parser. Tidy, is ported to many programming languages, and allows the language to clean up XML documents. It works well for xHTML.

In PHP5, the tidy extension is a default extension, however, in PHP4 you will need to download the Tidy PHP4 extension and compile the PHP executable with Tidy support.

How to use Tidy in PHP is documented here. Here is some examples of what Tidy can do.

Example use of Tidy in PHP

For code portability/distribution its necessary to first check if the tidy extension is available on your PHP version. You can do this by querying the existence of the tidy functions or classes (among other methods). So first you check if Tidy support is availalbe:

if (function_exists('tidy_parse_string')) {
// do your tidy stuff
}
Then comes the tidying. For simplicity, I'll use the single PHP Tidy function, 'tidy_repair_string'.

// Specify configuration
$config = array(
 'indent'         => true,
 'output-xhtml'   => true,
 'wrap'           => 200);
// Specify encoding
$encoding = 'utf8';
// repair HTML
$html = tidy_repair_string($html, $config, $encoding);

This works for both PHP4 and PHP5. PHP5 also supports an OO syntax.

Example Implementation: PHP Tidy Plugin for Joomla

Here is how I implemented the PHP Tidy Plugin into Joomla.

Joomla is a Content Management System, thus you cannot directly control the xHTML that will go into your articles. Some of your users may not be very xHTML savvy. The main reason I implemented Tidy is to clean content inserted automatically from feeds - which you have absolutely no control over.

A Joomla Plugin implements a basic Observer Pattern into Joomla. Functions are registered as observers, which are triggered during certain events. One such event is the preparation of content for display. The tidy plugin thus registers as a handler of content preparation. It then passes all content through the tidy parser, and returns the clean xHTML to Joomla.

The Joomla Tidy Plugin Code


/**
* @copyright Copyright (C) 2007 Fiji Web Design. All rights reserved.
* @license http://www.gnu.org/copyleft/gpl.html GNU/GPL
* @author [email protected]
*/

// no direct access
defined( '_VALID_MOS' ) or die( 'Restricted access' );

// register content event handlers
$_MAMBOTS->registerFunction( 'onPrepareContent', 'bot_tidy' );

/**
*  Tidy up the xHTML of your content
*/
function bot_tidy( $published, &$row, &$params, $page=0 ) {
 
 if ($published) {
  // get the plugin parameters
  //$botParams = bot_tidy_getParams('bot_tidy');

  if (isset($row->text) && $row->text) {
   $row->text = bot_tidy_parse($row->text);
  }

 }
 return true;
}

/**
* Parses a string with tidy taking into consideration the Joomla encoding
* @param String xHTML
*/
function bot_tidy_parse($html) {
 if (function_exists('tidy_parse_string')) {
  
  // Specify configuration
  $config = array(
       'indent'         => true,
       'output-xhtml'   => true,
       'wrap'           => 200);
  // get Joomla content encoding
  $iso = split( '=', _ISO );
  $encoding = '';
  $jos_enc = str_replace('-', '', $iso[1]);
  if (in_array($jos_enc, array('ascii', 'latin0', 'latin1', 'raw', 'utf8', 'iso2022', 'mac', 'win1252', 'ibm858', 'utf16', 'utf16le', 'utf16be', 'big5', 'shiftjis'))) {
   $encoding = $jos_enc;
  }
  
  // Tidy
  $html = tidy_repair_string($html, $config, $encoding);
  
  return $html
  ."\r\n"
  ;
 } else {
  return $html
  ."\r\n"
  ;
 }
}

Here is the tidy plugin for Joomla.

Tidy is great for Content Management Systems where content is contributed by users with differing levels of xHTML knowledge. It is also necessary if you want content from RSS feeds to pass W3C validation (if they contain xHTML like the Google News Feeds). I've noticed however, that PHP Tidy does not always create valid xHTML content. It does however create valid XML every time. This is yet to be explored further as I have just released Joomla Tidy Plugin for Alpha testing.