Headers already sent by..

Sunday, June 17, 2007

Familiar with that ?

For some PHP newbies that warning is quite irritating. This an example of script that generate that warning.

<?php
echo "some output here";
header("location: index.php");
?>
Means, there should be on output before the tag header("location: index.php") or the warning will show up. The warning will tell you something like "Cannot modify header information - headers already sent by ....". You have to make sure that no output is made before the header tag. In this case, you have to remove the echo "some output" to make the script works and redirected user to index.php. The other solution if using ob_start() and ob_end_flush(), ob_start() buffers your whole page before load it, so the output will not be load until WHOLE page is buffered. Thus, your script will work and user will redirected to index.php.
<?php
ob_start();
echo "some useless output here";
header("location: index.php");
ob_end_flush();
?>
Or, you could use meta instead of header. Meta is not part of PHP you could use it on plain HTML. Here's how
<?php
echo "some output here";
?>
<meta http-equiv="refresh" content="0;url=index.php">
Using meta, u can make a kind ot imer by modifying the number on content, the above example using content="0... " to diretly redirected user to index.php. You can however, change the value to something like 10, 5, to redirect the user AFTER that many second(s). Cheers.

Register Globals

Friday, June 15, 2007

If your PHP scripts stil rely on register_globals, then I must say.. where had you been? I mean, since PHP 4.20 register_globals has been set off by default, thus your script must not rely on register_globals for security reason. What kind of security reason? Register_globals made it easy for PHP programmers to code. Every variables, whether it's from POST, GET, SESSION, COOKIES, FILES or any other else can be called only by referencing to their variable name. So if have a form that POST $name to let's say file process.php then ini file process.php I can easily call the variable simply using $name. The problem is, it's also easy for attacker or any other people to manipulate the $name variable, the easiest example is making the same named variable but making it not by method POST, e. g

http://somesite.com/process.php?name=somenamehere

which is making a name variable via GET method with value 'somenamehere'. That happens because register_globals threat every variable whether it's POST, GET, or any other that I have metioned before as the same. The above example is about manipulating a POST method using GET. What will happen if the same way used to manipulate SESSION? I believe you get the point. What if ALL scripts I wrote before rely on register_global? First thing, you must change the way you code from now on. Then, fix your scripts so they're compatible with register_globals = Off. The fix is simple, you'll just have to change your variables from $something to $_GET["something"], or $_POST["something"] depending on what method the variables were made from. Ah, you had lots of files that almost impossible to change all the variable, copy paste this script, save it on a file, then include the file to every scripts that rely on register_globals. Here's the scripts:
foreach($_POST AS $key => $value) { ${$key} = $value; } foreach($_GET AS $key => $value) { ${$key} = $value; } foreach($_SESSION AS $key => $value) { ${$key} = $value; } //add your methods (if any) here like the above example
I think the scripts is well self-explained. Other resources about register_globals can be found here and here, and many others using google.

Login System with PHP and MySQL

Saturday, June 9, 2007

This example will show you how to make a password protected page using PHP and MySQL. It's a very simple example. We'll be using session() in PHP. You'll need a login form for user to input their username and password, a PHP script to check the username and password inputted by the user, and a table contains the value of valid username and password, you can input valid username and password to this table using phpmyadmin or make a separate script for that. Here's the flow, first, user input their username and password from a login form. Then, a php script do a crosscheck to the database looking for that username and password, if found, the script then start a session to save that username and password as key variables. If not found, the user is redirected back to the login form page. After a session is created, we can just simply put a line contains session checking in every page that we need to protect. The codes below will explain it better. First, a table contains valid username and password. A simple query like this will do:

CREATE TABLE users (user_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(20), password VARCHAR(50))
and insert a dummy user password for testing
INSERT INTO users (username, password) VALUES ("admin", password("mypassword"));
And now, creating the login form, let's just name it login.php
<form action="login_handler.php" method="post"> Username:<input name="username" maxlength="40" type="text"> Password:<input name="pass" maxlength="50" type="password"> <input name="submit" value="Login" type="submit"> </form>
And the handler to check if the username an password inputted is valid or not, name it login_handler.php
<?php session_start(); if(isset($username)&&isset($password)){ //enter your database configuration here $host=""; $dbuser=""; $dbpassword=""; $dbname=""; $dbh=mysql_connect($host, $dbuser, $dbpassword) or die(mysql_error()); mysql_select_db($dbname, $dbh) ; //end of database configuration $query="select * from users where username='$username' and password=PASSWORD('$password') "; $res=mysql_query($query, $dbh); if(!mysql_num_rows($res)){ echo "Access denied !"; exit; }//close if mysql_num_rows else{ $authLogData["login"]=$username; $authLogData["password"]=$password; session_register("authLogData"); @mysql_close(); header("location: member.php"); exit; //just in case ^_^ }//close else }//close if sbLog ?>
The member.php in the end is the page that needs authorization. The file would look like this
<?php session_start(); if(!session_is_registered("authLogData")||$authLogData["login"]==""){ header("location: login.php"); } //member content goes here //...... ?>
The conditional element (that is if(){....})simply redirect user to login form if there's no session created. That way, user can't access this file directly and have to login first. For every page that needs authorization you'll just have to add that conditional element ( if(){....} ). And voila, you're done. And I forgot to mention back then, you'll need a logout script too!. This is the codes:
<?php session_start(); session_unregister('authLogData'); session_destroy(); mysql_close(); header("Location: login.php"); ?>
As before, I haven't tested this scripts so I would appreciate if anyone has tested it. Any feedback would be very very appreciated.

Storing Images Into MySQL Database Table

Friday, June 8, 2007

First of all, I don't recommend it. Storing images data into MySQL database server consumes lot of server resources, especially when the size is large. Storing thumbails might be an exception, but IMHO uploading images physically is stil a better idea. The other problem with storing images data into MySQL database is exporting the data. You may find it's difficult with phpmyadmin to export the data and inserting it to a new MySQL table. I know I did. I'll give you a little illustration, try open a small image in your computer, but don't open it with your favorite image viewer software use notepad or other text-viewer instead. The unreadable chars appeared there is exactly the ones we're going to insert into MySQL database table when we store the images into database. Get my point? But if you can't resist creating an image database in MySQL, here's how. I assume you already had a working apache(or other webserver)-PHP-MySQL environment. This is just a quick-easy example, so maybe it's not well made in a programming manner. You will need 2 files, and a table in your database to do this operation. First, the file(and the form) to upload the image-data into database I'll name it image_upload.php. Second, file to extract the image-data from database and print the output to the browser, I'll name it image_view.php. And the last, a table to store the image information. Here's a simple query to create it.
CREATE TABLE images ( image_id smallint(4) NOT NULL auto_increment, image_binary blob NOT NULL, image_alt varchar(20) default NULL, image_type varchar(15) default NULL, PRIMARY KEY (image_id) ) TYPE=MyISAM;
And these are the codes for the php files image_upload.php
<?php echo "<form method=\"post\" action=\"\" enctype=\"multipart/form-data\">"; echo "<input name=\"image\" type=\"file\">"; echo "<input name=\"MAX_FILE_SIZE\" value=\"1000000\" type=\"hidden\">"; echo "<input name=\"submit\" value=\"submit\" type=\"submit\">"; echo "</form>"; if($submit){ //insert your database connection here $host=""; $user=""; $password=""; $dbname=""; $dbh=mysql_connect($host, $user, $password) or die(mysql_error()); mysql_select_db($dbname, $dbh) ; //end of database configuration $image_binary = addslashes(fread(fopen($image, "r"), filesize($image))); $query="INSERT INTO `images` (image_binary, image_alt, image_type) VALUES ( $image_binary, $image_name, $image_type)"; $res=mysql_query($query, $dbh) or die(mysql_error()); } @mysql_close(); ?>
image_view.php
<?php if($id){ //insert your database connection here $host=""; $user=""; $password=""; $dbname=""; $dbh=mysql_connect($host, $user, $password) or die(mysql_error()); mysql_select_db($dbname, $dbh) ; //end of database configuration $data=mysql_result($res, 0, "image_binary"); $type=mysql_result($res, 0, "image_type"); Header ("Content-type: $type"); echo $data; }; ?>
The 'id' in file image_view.php is the image_id on images table. So if you want the extract the image to a HTML page you can use <img src="http://www.blogger.com/image_view.php?id=2" /> instead of <img src="http://www.blogger.com/somefile.png" />. I didn't cross-check the script though, and I'm open for any corrections, suggestions, or anything.