How to make an online PHP contact / phone book with simple PHP (no database)?
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I've used pipe-delimited plain text data, since pipe (|) is rarely needed in normal text. It is trivial to parse in PHP. CSV is a bit more complex to handle, but not too bad either. For pipe-delimited plain text data, you could have each line describe one person: last name, first name, e-mail, link, and description, for example:
Code:
LastName | FirstName | firstname.lastname@example.com | http://www.example.com/~firstname.lastname/ | CTO of Example Co. |
If you put your contact list in above format into file path/to/list.txt then you'd only need one PHP page, for example something like
PHP Code:
<?PHP header('Content-Type', 'text/html; charset=UTF-8'); ?><html><head><title> List </title><style type="text/css">
The above PHP is much more complex than absolutely necessary, and I deliberately made it too ugly to use as-is in real life. I only wanted to show some useful techniques. This script is very tolerant of its input: you don't need to care which newline convention is used, or escape any special characters in the data (even < or >), as the script will do those for you. With this field order it will sort the names in ascending order based on last name.
The search box takes one or more keywords to search for. You can put a + in front of each keyword, or a - to invert the selection (meaning any entries that have that keyword will be dropped from the list). Note that the keyword matching is done when parsing, simply because this way you can check each entry (line) with a single function call per keyword.
It is a good idea to separate parsing and display. Note how easily you can extend or modify the input format: you just modify the parsing loop. Named fields (in the PHP array) make the output loop easier to read, too.
The display foreach loop may look a bit complex, but the if clauses are there only to help make the names links to the e-mail address, and the description a link to the possible home page.
If there is interest for some open collaboration here, and somebody writes an example layout in standards-compliant CSS and HTML, perhaps with ideas on other useful search or display features, I might be willing to rewrite the above PHP with lots of comments and easier to read code. If so, do start a new thread in the Programming section. (And drop me a private message if I don't notice it.)
I once started out on a project of a similar scope and complexity as your address-book project. It was a web interface to a data-set which I would occasionally update, but mostly browse. I was using Perl, not PHP, but I think that is irrelevant. Not long into the project, I realized that the use of an SQL database actually simplified the project enormously. The amount of ready-made code that suddenly becomes at your disposal is a huge productivity boost, IMHO. I was able to use offline general-purpose tools to manage the data and observe that my data was being correctly updated, as well as repair mistakes in the database in parallel with the development process. In addition, I was able to use the database to extract info that I had not previously envisioned, and use the data in ways that were outside the original scope of the project (maybe not so important for an address book).
If the SQL database is available to you, I would suggest that you at least consider it for use in your project.
--- rod.
Do you want to maintain locally and read from the web? My first thought would be csv. Might do the trick if you don't have 'one-to-many' relationships. You probably can export your contacts to csv and ftp them to the web. php has support for csv (fgetcsv (read) from version 4, fputcsv (write) from version 5.1.0).
Just be cautious: make sure that the whole world isn't able to access your address book. I for one will not be very amused if 'my' phone number or email address can be pulled from your addressbook and is going to be used for spam. '.htaccess' can help to limit access and I strongly suggest https so info can't be sniffed (but OK, I'm paranoid).
Really https? I didnt know. man... I am so worried now. - You are twice actually very right. I think cvs is highly great and https, I have no idea how to do that. HTACCESS I use it already but not https
Quote:
I could do something like this:
<?php
if(isset($_POST['submit']))
{
$name = $_POST['Fname'];
$lastname = $_POST['Lname'];
$emailsubmit = $_POST['Lemail'];
echo "User Has submitted the form and entered this name : <b> $name $lastname $emailsubmit </b>";
echo "<br>You can use the following form again to enter a new name.";
I've used pipe-delimited plain text data, since pipe (|) is rarely needed in normal text. It is trivial to parse in PHP. CSV is a bit more complex to handle, but not too bad either. For pipe-delimited plain text data, you could have each line describe one person: last name, first name, e-mail, link, and description, for example:
Code:
LastName | FirstName | firstname.lastname@example.com | http://www.example.com/~firstname.lastname/ | CTO of Example Co. |
If you put your contact list in above format into file path/to/list.txt then you'd only need one PHP page, for example something like
PHP Code:
<?PHP header('Content-Type', 'text/html; charset=UTF-8'); ?><html><head><title> List </title><style type="text/css">
The above PHP is much more complex than absolutely necessary, and I deliberately made it too ugly to use as-is in real life. I only wanted to show some useful techniques. This script is very tolerant of its input: you don't need to care which newline convention is used, or escape any special characters in the data (even < or >), as the script will do those for you. With this field order it will sort the names in ascending order based on last name.
The search box takes one or more keywords to search for. You can put a + in front of each keyword, or a - to invert the selection (meaning any entries that have that keyword will be dropped from the list). Note that the keyword matching is done when parsing, simply because this way you can check each entry (line) with a single function call per keyword.
It is a good idea to separate parsing and display. Note how easily you can extend or modify the input format: you just modify the parsing loop. Named fields (in the PHP array) make the output loop easier to read, too.
The display foreach loop may look a bit complex, but the if clauses are there only to help make the names links to the e-mail address, and the description a link to the possible home page.
If there is interest for some open collaboration here, and somebody writes an example layout in standards-compliant CSS and HTML, perhaps with ideas on other useful search or display features, I might be willing to rewrite the above PHP with lots of comments and easier to read code. If so, do start a new thread in the Programming section. (And drop me a private message if I don't notice it.)
Wow. What a technology this PHP, nice !
I put that php into contact.php, then chmod and got an error
Code:
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
we could do that as in the JPG for the contact book as in the file in attachement...
Then we cross the contact adn overwrite the all thing...
Here is my own made code:
Quote:
<?php
function getRealIpAddr() {
if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip=$_SERVER['HTTP_CLIENT_IP']; // share internet
} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip=$_SERVER['HTTP_X_FORWARDED_FOR']; // pass from proxy
} else {
$ip=$_SERVER['REMOTE_ADDR'];
}
return $ip;
}
if(isset($_POST['submit']))
{
$ipreal = getRealIpAddr(); // Get the visitor's IP
$ip = $_SERVER['REMOTE_ADDR'];
$pagina = $_SERVER['REQUEST_URI'];
$varb = $_SERVER['REMOTE_ADDR'];
$varc = $_SERVER['HTTP_USER_AGENT'];
$hostname = gethostbyaddr($_SERVER['REMOTE_ADDR']);
$datum = date("d-m-y / H:i:s");
$name = $_POST['Fname'];
$lastname = $_POST['Lname'];
$emailsubmit = $_POST['Lemail'];
echo "User Has submitted the form and entered this name : <b> $name $lastname $emailsubmit </b>";
echo "<br>You can use the following form again to enter a new name.";
OK, I have coded something that works, but very simply. It works, it can be used already for saving
"Editing" : I have no idea how to make it. To check the record Number, and remove it... - well, maybe someone would know...
Best regards
Please find hte code.
For the package contactbook_csv.tar.gz. Just simply rename the log to tar.gz extension. Then you can unpack. Copy to your website, and please use the right permissions.
OK, I have coded something that works, but very simply. It works, it can be used already for saving
"Editing" : I have no idea how to make it. To check the record Number, and remove it... - well, maybe someone would know...
You've already reached the point where SQL starts to pay off.
Code:
update addressbook set email='joe@some.isp' where firstname='Joe' and lastname='Smith';
The classic actions of a web database application: Create, Read, Update, Delete (CRUD). The create and read parts are easy; after that it takes more work. I don't know how big your database is, but if is less than a megabyte, I would simply read it in as an in-memory data structure, modify or output according to the HTTP request, and write it back to disk when done. File locking, somehow.
You've already reached the point where SQL starts to pay off.
I agree.
I would also like to point out that you (Xeratul) really should think about authentication and access controls at this point, too. You basically have four possibilities:
Let Apache handle both authentication and access control
Basic authentication (HTTP Basic authentication) is easy to implement, but logout is problematic: usually the credentials persist until you close the browser. (I know of one way to do logout in a way that works with all browsers, but it will pop up a small window asking the user for another username and password; the user needs to click either OK or Cancel. Very confusing to the user.)
Do authentication in PHP, but access control within Apache
Usually this means you use a cryptographically secure authentication cookie tied to the user, session, and IP address. Your own PHP script will construct the cookie when the user logs in, and an Apache module grants access only to users with a valid such cookie. I've used mod_auth_tkt, but there are a number of viable Apache modules you could use.
Do authentication and access control in PHP
This is the do-it-yourself option.
Security is difficult to develop from scratch. It is not easy to make a secure authentication and access control mechanism. Even the simplest error can render the mechanism ineffective. I personally find it extremely repellent when people and organisations use sloppy, it's-good-enough approach to maintaining my privacy; that is why I always recommend using the strongest security measures that are feasible for you, even if the "feel" overblown or paranoid.
Usually it translates to using known reliable code, and not rolling your own.
Cookie-based authentication happens to be not too difficult to implement in PHP safely, mainly because it has SHA1 built-in (and much stronger cryptographic hash functions in the hash module (built in for PHP-5.1.2 and later), and encryption/decryption routines in the Mcrypt module). I'll detail that further below.
User password checking is sensitive. Assuming you are sane, you will store user passwords as salted hashes, but I'll detail that too further below. At login, the user password is transmitted in cleartext; this means you either need to use HTTPS (a secure, encrypted connection, which should be nearly impossible to eavesdrop on), or a small login app (Javascript for example) to hash the user password prior to transmission.
Because hashing the password at the client end means you need to use a layered approach to salting and hashing, it gets even more complex.. so normally you just use HTTPS instead.
Note that if you switch to HTTP after login, an attacker might be able to steal the rest of the session (pretend to be you, logged on); using HTTPS that is nearly impossible.
Do authentication in PHP, but access control in an auto-prepended PHP file
You can use the auto_prepend_file directive in php.ini to auto-include a library file, which verifies the authentication cookie, and sets some PHP variables (username etc.) if successful. If there is no cookie, or the cookie has expired, the auto-prepended PHP file can produce or redirect to a login page.
This is a bit more complex to set up, but very easy to use afterwards.
The only thing you need in every PHP page is something like if(strlen(@$User)<1)exit(1); at the very beginning, to make sure the page errors out if the autoincluded PHP file was not executed for some reason.
Hash functions are functions which take some data (called a plaintext), and process it into a single large fixed-size number (the hash). Cryptographic hash functions are those that do the process in such a way that you cannot use the result (hash) to find what the original data (the plaintext) was. You can only make guesses, compute their hashes, and compare that to the one you're searching for. Since a 128-bit hash has 340282366920938463463374607431768211456 and a 256-bit hash 115792089237316195423570985008687907853269984665640564039457584007913129639936 possible values, it is going to take a very long time to find a match, even if you can check billions of hashes each second.
Since a bad guy can create a library for a large number of typical passwords, hash salting is used. Instead of just hashing the password, you sprinkle it with salt before hashing -- salt being some random number or string, which is saved along with the hash value. Usually you also apply the hash function a number of times, just to make life difficult for any attackers. Here is an example of how to generate a 72-bit salt and 160-bit SHA-1 hash pair in PHP:
PHP Code:
$Password = '[I]This is the secret password the user has supplied[/I]'; $Salt = sprintf("%06x%06x%06x", mt_rand(0, 16777215), mt_rand(0, 16777215), mt_rand(0, 16777215)); $Hash = sha1($Salt . $Password, TRUE); for ($i = 0; $i < 998; $i++) $Hash = sha1($Hash, TRUE); $Hash = sha1($Hash, FALSE);
To check a password, you look up the $Salt and $Hash for that user first:
PHP Code:
$Salt = '[I]the known salt value[/I]'; $Hash = '[I]the known hash result[/I]'; $Password = '[I]This is the secret password the user has supplied[/I]'; $Test = sha1($Salt . $Password, TRUE); for ($i = 0; $i < 998; $i++) $Test = sha1($Test, TRUE); $Test = sha1($Test, FALSE); if (strcmp($Hash, $Test) === 0) { /* $Password was correct */ } else { /* $Password was incorrect */ }
If you wanted to do login over insecure HTTP, you could use a Javascript implementation of SHA-1 to hash the user password in the client browser. In this case you'd normally use a multi-part salt, and two or more layers of hashing, so that an eavesdropper gets no information about the password or its properly salted hash. The client end can compute e.g. SHA1(SHA1(salt1+password+salt2)+cips+time+salt3)) where salt1, salt2 and salt3 are random strings provided by the server (salt1 and salt2 split to avoid both prefix and postfix attacks on SHA1), time is the UNIX time on the server, and cips is the client IP address as seen by the server (and optionally verified by the browser; it may not match if NAT is used). The login request contains the user name, hash value, salt1, salt2, salt3, time, and cips.
On the server end, you need to store salt1, salt2 and SHA1(salt1+password+salt2) for each user; the triplet should be unique for each user. The time should not be too old, to avoid a replay attack (by a later user on the same machine); perhaps a couple of minutes. salt3 should be a large random number.
If your salted hash user database is leaked, attackers gain access to your service only. The user passwords remain secure. (It would make a dictionary attack easier, but only against each user individually.) An eavesdropper can see all communications, but cannot use anything it sees to gain access (unless they can fake their IP address too). A man-in-the-middle attack (where the attacker sits between you and the server you're accessing) is still possible, but will yield only that session. However, a normal user has no way of determining with HTTP if the page they see in their browser is the genuine one and not a look-alike copy, so a man-in-the-middle can steal the user password using a decoy or "phishing" page.
Use HTTPS to avoid that risk.
Of course, it would be practically insane to do the above for each and every request. Instead, you check the password only at login, and if correct, give the user an authentication cookie. You obviously should make that cookie cryptographically secure, and tied to both the user and the IP address they're using; preferably the time and the session too (so that if they close their browser or stay inactive long enough they automatically log out too).
If you have a secure fast storage mechanism, say an SQL database, it is best to use just a very large random number (say, 256 bits) as the cookie, and just look each cookie value up in the storage. The cookie value is then a key to the user session data, which contains at least the user name, IP address, and time. For each request, you simply look up the triplet keyed by the cookie value, and verify that the IP address matches the client with the cookie, and that the time has not expired. (You'll probably want to update the time field now and then.)
You can do pretty secure standalone authentication cookies using encryption (Mcrypt module in PHP). I recommend using AES-128 (MCRYPT_RIJNDAEL_128) or AES-256 (MCRYPT_RIJNDAEL_256) in CBC mode. I'd use something like sha1(time+IP-address+secret+salt) as the encryption key, with time and salt stored in the cookie unencrypted, and secret being a secret string known only to the PHP scripts. An attacker can only use the cookie if they can use the same IP address as the victim user, or if they find out the secret.
You do not need to encrypt the authentication cookies, if the user credentials (all except the password) can be stored in plaintext in the cookie. I have used key:user:options:salt:hash as the cookie, with hash=sha1(IP-address:key:user:options:salt:secret) and key=UNIX time and secret a fixed secret value only known to the PHP scripts. The cookie is renewed with a new key every now and then; I recommend whenever a quarter of the expiry interval has passed. (It does add 25% uncertainty to the expiry interval.) The cookie can be stolen by someone having the same IP address (for example, same NAT with a bit of luck), or if they find out the secret.
To summarize: you need to consider how you will store not only your data, but access credentials too; and you need to consider where and how you will do authentication and access control. Using an SQL database is definitely simpler and easier for something like a contact list web app, but it is definitely doable with just (two) plain files. With SQL, the main problem is how to store the password for database access securely. With plain files, display is easy, but modification requires extra care; most of the examples shown on the web (flock() in PHP) are unreliable, and will only work in specific conditions.
FWIW, the addressbook web application is like the "Hello World" of web application development toolkits in the realm of Ruby-On-Rails and Django. The standard CRUD application will come with authentication/authorization/session-management built in, as well as potentially an administrator backdoor and other goodies. These normally require a SQL backend (sqlite is pretty simple to use for such an application) and developing your application will be trivial following a tutorial recipe.
--- rod.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.