Monday, February 23, 2009

PHP Tutorial : getcwd get full path on host

How many scripts which you have/had to install requires/required you to enter somewhere in a config php file to enter and setup the full path of your account in the web host? There are few I guess. The full path on the ftp consists in a linux like full path in the most of the cases.

You can get it by using the getcwd() php function which means I think Get Current Working Directory or something like that.

<?php
$fullpath = getcwd();

print
"My full path is $fullpath";
?>

Tuesday, February 17, 2009

rejas tutorials-01

This tutorial aims to outline the process of creating a visitor map you sometimes see on MySpace profiles. It does not aim to be a definitive guide on creating an incredibly precise map, but rather shows an example of the process involved and how different APIs and data can work together. Due to the nature of the external services used, it is not going to be entirely accurate, however it generally bodes quite well and is accurate to country almost all the time. Eventually, this is the kind of output we'll get:

Step 1 diagram

We will be using three external services: hostip.info, maps.yahoo.com and maps.google.com. The visitor's IP address is gathered, and this is then fed to the hostip.info API. If its location can be determined, then it is passed to the Yahoo Maps API to gather its geographic coordinates. Note: I am aware that the Yahoo API can be bypassed as the hostip.info API provides geographic coordinates, but at the time of writing no such feature was available. However, by the end of the tutorial you should have learned enough to add this functionality in yourself. These will then be plotted using the Google Maps API to create the actual map display. If the location can not be determined, the user is not plotted on the map, so it heavily depends on the APIs involved. A pseudo-flowchart may look something as follows:

Step 1 diagram

What it is advised you should know before using this tutorial:

1) At least a basic understanding of PHP, especially functions and data types (array, boolean etc.)

2) An understanding of databases, namely MySQL and its integration with PHP scripts

3) Hopefully knowledge of what the Document Object Model and XML are, though these are not a necessity

Step 2

The first thing to do is sign up for a Google Maps API key. You can do this here; be sure to read the restrictions and the terms of use.

Step 3

Create a database, naming it something like 'visitormap', and assign it a user. We will have five fields; id, ip, location, longitude and latitude; all hopefully self-explanatory. So, execute the following SQL:

CREATE TABLE `visitor_map` (


`id` INTEGER auto_increment,

`ip` VARCHAR(15) NOT NULL,

`location` VARCHAR(32) NOT NULL,

`longitude` FLOAT NOT NULL,

`latitude` FLOAT NOT NULL,

PRIMARY KEY (`id`)

);

For ease of use, we'll now create a configuration file. Create a file called config.php and insert the following, changing the constant values to suit your database setup and Google Maps API key.






define('DB_HOST', 'localhost'); // Database host

define('DB_NAME', 'visitormap'); // Database being used

define('DB_USER', 'david2012'); // Database user

define('DB_PASS', 'password56'); // Database user's password



/* Your Google Maps API key */

define('API_KEY', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');



?>

Step 4

We want to create a function in which we can house the code to give us the coordinates and other information we need to create the map display. If you are not very strong on functions, view the Zymic tutorial on functions or visit the PHP manual page. Create a file named visitormap.php with the following (explained afterwards):






function IPtoCoords($ip)

{

$dom = new DOMDocument();



$ipcheck = ip2long($ip);

if($ipcheck == -1 || $ipcheck === false)

trigger_error('Invalid IP, what are you doing? :|', E_USER_ERROR);

else

$uri = 'http://api.hostip.info/?ip=' . $ip;



$dom->load($uri);

$location = (strpos($dom->getElementsByTagName('name')->item(1)->nodeValue, 'Unknown') === false)

? $dom->getElementsByTagName('name')->item(1)->nodeValue

: $dom->getElementsByTagName('countryAbbrev')->item(0)->nodeValue;



if($location == 'XX')

return false;

else

{

$dom->load('http://local.yahooapis.com/MapsService/V1/geocode?appid=YahooDemo&location=' . $location);

$longitude = $dom->getElementsByTagName('Longitude')->item(0)->nodeValue;

$latitude = $dom->getElementsByTagName('Latitude')->item(0)->nodeValue;

return array('location' => $location, 'longitude' => $longitude, 'latitude' => $latitude);

}

}



?>

Step 5






function IPtoCoords($ip)

{

$dom = new DOMDocument();

}



?>

Here we create a function called 'IPtoCoords'. Eventually, it will return us an array of the following values: location (be it city or country--however accurate the hostip.info API is), longitude and latitude. Inside the function, PHP's DOMDocument object is instantiated (an instance created of; in this case assigned to the $dom variable) using the 'new' keyword, and this will provide us with methods to grab data from XML outputted from both the hostip.info and Yahoo Maps APIs.

Step 6






function IPtoCoords($ip)

{

/*$dom = new DOMDocument();*/



$ipcheck = ip2long($ip);

if($ipcheck == -1 || $ipcheck === false)

trigger_error('Invalid IP, what are you doing? :|', E_USER_ERROR);

else

$uri = 'http://api.hostip.info/?ip=' . $ip;



}



?>

We want to make sure the IP is valid, just in case the user is trying something a bit funny. So, we use the PHP function ip2long to check this - it works by converting the IP to a proper address, and then if this either returns -1 or a boolean value of false, we can assume that the given address is invalid. If the IP is invalid, we trigger an error message, however if the IP is indeed correct, we form a URI out of the IP in format http://api.hostip.info/?ip=IP and assign it to a variable for later use - this is the first step in using hostip.info's API.

Step 7






function IPtoCoords($ip)

{

/*$dom = new DOMDocument();



$ipcheck = ip2long($ip);

if($ipcheck == -1 || $ipcheck === false)

trigger_error('Invalid IP, what are you doing? :|', E_USER_ERROR);

else

$uri = 'http://api.hostip.info/?ip=' . $ip;*/




$dom->load($uri);

$location = (strpos($dom->getElementsByTagName('name')->item(1)->nodeValue, 'Unknown') === false)

? $dom->getElementsByTagName('name')->item(1)->nodeValue

: $dom->getElementsByTagName('countryAbbrev')->item(0)->nodeValue;

}



?>

First we call the DOMDocument's 'load' method, which loads an external XML file. In this case, it's the $uri variable we created in the previous step. The next section is a bit more complex and uses a ternary operator (short-hand if/else block) Basically, we have the $location variable at the beginning, and will assign it values based on the if/else block. We use the strpos function to test if the second 'name' tag in the XML file contains the word 'Unknown' as returned by the API, using the DOM's getElementsByTagName method. If it does, we use the ISO 3166 country code returned in the 'countryAbbrev' tag, but if not, we use the second 'name' tag as it contains a known location.

Step 8






function IPtoCoords($ip)

{

/*$dom = new DOMDocument();



$ipcheck = ip2long($ip);

if($ipcheck == -1 || $ipcheck === false)

trigger_error('Invalid IP, what are you doing? :|', E_USER_ERROR);

else

$uri = 'http://api.hostip.info/?ip=' . $ip;



$dom->load($uri);

$location = (strpos($dom->getElementsByTagName('name')->item(1)->nodeValue, 'Unknown') === false)

? $dom->getElementsByTagName('name')->item(1)->nodeValue

: $dom->getElementsByTagName('countryAbbrev')->item(0)->nodeValue;*/




if($location == 'XX')

return false;

else

{

$dom->load('http://local.yahooapis.com/MapsService/V1/geocode?appid=YahooDemo&location=' . $location);

$longitude = $dom->getElementsByTagName('Longitude')->item(0)->nodeValue;

$latitude = $dom->getElementsByTagName('Latitude')->item(0)->nodeValue;

return array('location' => $location, 'longitude' => $longitude, 'latitude' => $latitude);

}

}



?>

The first two lines are performing a check on the location. If our $location variable was given the contents of the 'countryAbbrev' tag, it may have resolved to 'XX' as provided by the hostip.info API - meaning the country could not be determined. If this is the case, we return a boolean value of false. If not, we begin utilising the Yahoo Maps API, using a similar convention to the hostip.info one.

Again, we load a URI into the DOMDocument's 'load' method, this time loading a Yahoo URI with a 'location' GET value of what is held by our $location variable. The API is clever and the GET value can be a wide range of things, including street, city, state, zip, country (including ISO 3166 code) and various combinations. In our case it will generally either be a city, or if this could not be resolved by the hostip.info API then a country code. With this new URI loaded, we again use the getElementsByTagName method to grab the contents of the first 'Longitude' and 'Latitude' tags with the nodeValue property (which grabs the text content contained within the tag.)

Finally, we set the return values of the function. It is an associative array in the format location: city or country, longitude: location longitude, latitude: location latitude. This will then be used for entering into our database and eventually displaying on a map.

Step 9

Now that we can successfully gain the visitor's location, we can use this to build our finished visitor map. Create a new file (for instance viewmap.php). At the top of your file, put the following:






// Include the configuration and function files we created

require 'config.php';

require 'visitormap.php';



// Establish a MySQL connection and select our database using values contained in config.php.

mysql_connect(DB_HOST, DB_USER, DB_PASS);

mysql_select_db(DB_NAME);



// Assign the user's IP to a variable and plot it into our function, whose return value is assigned to $userinfo

// Remember, the user's IP is not to be trusted 100%

$ip = $_SERVER['REMOTE_ADDR'];

$userinfo = IPtoCoords($ip);



// We select the location column from the database

$user = mysql_query('SELECT `location` FROM `visitor_map` WHERE `location` = \'' . $userinfo['location'] . '\'');

// If it does NOT return a value, and the user's IP could indeed be matched...

// (This makes sure that we have no duplicate rows (which would be a waste) and can determine the visitor's location, respectively)

// ...We insert the values returned by our function into the database, escaping any possible dangerous input

if(!mysql_fetch_row($user) && $userinfo)

mysql_query('INSERT INTO `visitor_map` (`ip`, `location`, `longitude`, `latitude`) VALUES (\'' . mysql_real_escape_string($ip) . '\', \'' . $userinfo['location'] . '\', ' . $userinfo['longitude'] . ', ' . $userinfo['latitude'] . ')') or die(mysql_error());



?>

This is quite simple so explanation is contained within the file itself.

Step 10

Now that we've inserted the user's data into our database, we select all of this information and plot it on a Google Maps object. In the file you just created, you can now create the HTML of the page, including whatever content you wish. The Google Maps API requires the following, however:

1) Include the JavaScript file required for Google Maps, using your API key. Insert the following into the of your HTML page:

2) Certain functions must be triggered on page load and unload. You can use JavaScript event handlers if you want to separate structure from behaviour, but the easiest method is to put the information into the tag:

 onload="load();" onunload="GUnload();">

Step 11

Now we'll be using the actual Google Maps API to plot the data, which is done using JavaScript. In the head of your HTML document, put the following:

We start off defining a 'load' function, which is triggered on page load, shown in the previous step. Then, an if statement is used to see if the user's browser supports Google Maps. The next line assigns the map canvas to an HTML element, in this case with an ID of 'map', using the DOM's getElementById method. The following two lines add a large zoom/pan control to the map object and set the default centre of the map when loading to 0 (the centre of the world), respectively.

Finally, we use some PHP to draw the longitude and latitude coordinates from our database, then loop the results. On each successive loop, we create a marker (the red pointer showing the visitor locations) using the addOverlay method provided by the Google Maps API. The following JavaScript is echoed, albeit with the values contained in our database:

map.addOverlay(new GMarker(new GLatLng(LATITUDE, LONGITUDE)));

All of this is covered in the Google Maps API documentation.

Step 12

Now that we have our JavaScript function which plots the data set up, all we have to do is define a canvas for the map to be displayed. In the 'load' function, we specified an HTML element of ID 'map', so make sure this reflects the ID of the element you'll be using to display the map:

id="map" style="width: 500px; height: 300px">

Basic inline CSS styles are applied to give a width and a height to the canvas, but it's not a necessity. There we have it! Your visitormap should work. If not, download the provided example and compare the code with your own.

Step 13

Conclusion:

1) Our script has some limitations. Primarily, the accuracy is not going to be 100% and can be as vague as a country or city. Secondly, if the user's IP can't be pinpointed, they are not plotted, rendering the visitor map unreliable regarding visitor data (hits etc.).

2) Performance is a big issue. The script uses three external APIs, and on this note can be quite slow (though luckily we are storing the data returned from 2 of the APIs in a database, meaning in general only 1 API is used for the display). This tutorial was meant to demonstrate some coding examples, and realistically if you have millions of visitors then your database is going to get very large, the page will end up huge and of course the map will get crowded.

3) The idea is useful though. It could be extended very easily into a unique guestbook type of script by changing the database structure (accommodating for name, email address, message etc.), and using a message pointer provided by the Google Maps API to show the message left by the visitor when their location marker is clicked. It opens up the gates for some interesting concepts.

You can download the source files here.

Step 14 diagram

rejas tutorials-02

Step 1

What Do Sanitisation and Validation Mean? Sanitisation and Validation are important terms to understand when writing PHP applications. Both in the context of this tutorial are about processes performed on user input. Sanitisation is cleaning user input to make it safe to process, and Validation is checking the data to see if it is: in the correct format; of the correct type etc. It is important to sanitise and validate data coming in from users of your PHP applications, because if it is left unchecked, the input may be used to facilitate an exploit. Some of the most common exploits involving user input are: code injection, sql injection and header injection. And we will have a look at some of these during the tutorial.

Step 2

Validation is a vital topic when handling user input. It helps to improve security, improve usability and reduce the amount of bugs in your program. To validate something, we first work out a criteria which our user input has to conform to. For example, we might want the user input to be a number between 10 and 99, we then test the user input against these rules, and if the input fails the check(s) we will not use the data and inform the user that they have input something incorrect. Ok, but what does that mean in terms of code? Well here's an example of the code you might use to test a number to see if it is between 10 and 99.


// check the input
if($_POST['number'] >= 10 && $_POST['number'] <= 99)
{
// the number is fine, continue
echo $_POST['number'];
}
else
{
// the number provided is not within range
die('The number provided is not valid. Please provide a number between 10 and 99.');
}
?>

Step 3

Validation is especially useful because once we are certain of what format the user input is in we might not have to sanitise it. For example, in the previous code snippet we no longer need to sanitise $_POST['number'] because to have passed the validation it would have to be a number, and is therefore harmless. A practical example of this might be in an email form, where we are taking user input and then placing it in an email header. For example, this script is vulnerable to header injection:


// the email to send to
$myemail = 'ted@platypus.org.uk';

// from header
$from = 'From: ' . $_POST['name'] . ' <' . $_POST['email'] . '>';

// send the email
mail($myemail,$_POST['subject'],$_POST['message'],$from);
?>

This script is not validating any of the input it is given, so a user could send an email with a line break within it. This would then allow them to add extra headers to the email, which is not desired. More fundamentally, it just makes no sense not to validate the input. If someone has sent an email like "not_a_valid_email" the email should just not be sent. To combat this we can validate the input provided by the user, to see if it makes sense to allow it. This could be done with string functions, but it is much easier to introduce Regular Expressions (see link to tutorial about regexps). We can use RegExps in the previous example to make the script much more sensible:


// the email to send to
$myemail = 'ted@platypus.org.uk';

if(!preg_match('/^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$/',$_POST['email']))
die('Invalid email proved, the email must be in valid email format (such as name@domain.tld).');
if(!preg_match('/^[-_ 0-9a-z]$/i',$_POST['name']))
die('Invalid name proved, the name may only contain a-z, A-Z, 0-9, "-", "_" and spaces.');

// from header
$from = 'From: ' . $_POST['name'] . ' <' . $_POST['email'] . '>';

// send the email
mail($myemail,$_POST['subject'],$_POST['message'],$from);
?>

The script is now both safer and more suitable.

Step 4

Sanitisation is as we said above, cleaning user input to make it safe to process further, but what does that actually mean? Well, below we have a vulnerable PHP/MySQL login form. First, we'll show it in its vulnerable state, then improve on it and show why it is now safer than it was previously.


// connection to MySQL server
mysql_connect('localhost','username','password');
mysql_select_db('database');

// User input
$username = $_POST['username'];
$password = md5($_POST['password']);

// Construct and run query.
$sql = 'SELECT id FROM users WHERE username="'.$username.'" AND password="'.$password.'"';
$result = mysql_query($sql);

// If there is a user, log them in.
if(mysql_num_rows($result) > 0)
{
$_SESSION['login'] = true;
// Redirect to admincp
header('Location: http://somesite.com/admincp/');
}
else
die('Incorrect username or password.');
?>

Now, on the face of things that may look safe, it's checking the username and password in the database, and only logging the user in if a user is found. However, if someone were to enter a username of '" OR password LIKE "%" -- ' then the query becomes:

SELECT id FROM users WHERE username="" OR password LIKE "%" -- " AND password="9cdfb439c7876e703e307864c9167a15"

That query fetches the id of all users in the users table (since LIKE "%" matches all rows and -- comments the rest of the line) meaning it would log them in regardless of the actual values in the database. To prevent things like this, we can use sanitisation functions like mysql_real_escape_string(). Applying this function to the user input means that characters like " which can be used to inject SQL are escaped to with a backslash (e.g. \"). So with the following code:


// connection to MySQL server
mysql_connect('localhost','username','password');
mysql_select_db('database');

// User input
$username = mysql_real_escape_string($_POST['username']); // sanitised input
$password = md5($_POST['password']); // already safe due to md5()

// Construct and run query.
$sql = 'SELECT id FROM users WHERE username="'.$username.'" AND password="'.$password.'"';
$result = mysql_query($sql);

// etc...
?>

The same input is sanitised, and the query becomes this:

SELECT id FROM users WHERE username="\" OR password LIKE \"%\" -- " AND password="9cdfb439c7876e703e307864c9167a15"

The code is no longer vulnerable to that SQL injection exploit. mysql_real_escape_string() is only applied to $username because $password is hashed, and hashing also sanitises data. Anything passed through a hashing function like md5() or sha1() is returned in hexadecimal. Meaning that only 0-9 and a-f characters can be returned by the function. This means any threatening characters like quotes and slashes are sanitised and we can use the resultant hash in a query without fear of injection.

Step 5

There are also other ways of sanitising input, and a very useful one is typecasting. Taking another SQL example, say we were allowing the users to specify an offset to display data. In a query something like this:


// code...
$sql = 'SELECT id,title FROM news LIMIT '.$_GET['offset'].',10';
$result = mysql_query($sql);
// more code...
?>

We could use the same function as before to sanitise this $_GET variable, but it is more appropriate to use typecasting to force it to be an integer. We can do this using intval(). Intval takes a variable, and returns its value as an integer. So, a string "14" will become the number 14, and any input that is not numeric will become 0, making the input safe to work with.


// code...
$sql = 'SELECT id,title FROM news LIMIT '.intval($_GET['offset']).',10'; // sanitised input
$result = mysql_query($sql);
// more code...
?>

An important thing to remember about sanitisation, is that it is not just required only when inputing data into something like a database! Outputting an unsanitised variable can be just as dangerous as taking it as input for another purpose. For example, say we had a simple script that took a $_GET variable called "name", then output "Hello, [name]!". If you do not sanitise the user input then a user can craft a malicious URL to your script that will send cookies associated with your domain to them. How could they do that? By placing HTML code in the URL which executes some Javascript when the page is loaded.


// Dangerous! $_GET['name'] has not been sanitised.
echo 'Hello, ',$_GET['name'],'!';
?>

Now we know what the vulnerability, how can we stop it? Luckily PHP provides a very useful function for just this purpose, called htmlspecialchars(). This function replaces possibly dangerous characters like <>


// This is now safe because the user input has been sanitised.
echo 'Hello, ',htmlspecialchars($_GET['name'], ENT_QUOTES),'!';
?>

Step 6

Conclusion: It is evident that both validation and sanitisation are very important considerations in any PHP application. If possible, you should validate over sanitising, but if you are in doubt as to what you want to recieve, or you want to allow possibly dangerous characters then you should definately sanitise it. Sanitising where you shouldn't is much less trouble than not sanitising where you should! Sanitisation and validation should be a part of your planning stages, you might want to consider jotting down all the input you are taking from the user, and noting down exactly what you expect and make a note of whether the input might require sanitisation. Do this and you will be making much more secure, and more useful PHP applications with fewer bugs and which do not allow as much spam input from undesirable users.

Step 7

Closing Notes:

  • Be careful of validation by type when dealing with $_GET and $_POST variables. If someone inputs a number, is_numeric will not be true, because all information passed as GET and POST vars are strings. So to find out if a valid number has been supplied, you can either cast and then see if it is greater than 0. Or alternatively, check the string to see if it is only made of 0-9 characters.
  • Try to use application specific sanitisation functions where possible. For example mysql_real_escape_string is more likely to return a safe string to use in a mysql query than addslashes, because the latter does not check the character set used in the database. This can lead to inconsistencies that let injection through.
  • All PHP examples are assumed to be run on a system with magic_quotes_gpc OFF.
  • For more information consult the

Download Free Php ebooks

Practical PHP Programming (Paul Hudson)

Practical PHP Programming (Paul Hudson Prev Version)

PHP Manual (Mehdi Achour, et al)

A Programmers Introduction to PHP 4.0 (W.J. Gilmore)

PHP Documentation HOWTO (S. Bakken, et al)

PHP Documentation HOWTO (S. Bakken, et al) Prev Version

Intrusion Detection Systems (IDS) with Snort Advanced IDS with Snort, Apache, MySQL, PHP, and ACID

Building Database Driven Web Sites Using PHP MySQL

PHP GTK Manual (J. Moore, S. Fox, A. Zmievski et al)

PHP Tutorial

PHP Tutorial Another

PHP from the Ground Up

PHP Freaks Tutorials

Web Programming Books (include PHP)

PHP MySQL Tutorial

PHP amp; MySQL Tutorial Another

.

Share_this