Security Levels and PHP
Also see:
- Security levels from javascript and php input filtering
- Security levels from captchas and php input filtering
- Exiting from a PHP Form Validator
Also see Address Book Security. With this tutorial, you will learn how to password protect directories that you keep online. Apply this info not just to address books but any online folder you want to keep safe. cPanel X uses htaccess to put a wall between your folder and the rest of the world. No tickee, no takee. In other words, without the correct password and username, outsiders will remain just that . . . OUTSIDE!
Security
There is no such thing as absolute security. The closest thing to it can be illustrated by the movie War Games in which it was decided that the only way to win the war game is NOT TO PLAY. If you do not have a website, it cannot get hacked.
Let's look at some ways to increase security. You won't get perfection, but you can take plenty of steps to raise your level of security. Let's look at some:
- The good news is that hackers cannot View Source and see what's going on in your PHP files unless your server's PHP is temporarily down during maintenance—at which time your PHP files will be readable—UNLESS. Unless what? Unless you take preventative security steps. It helps to store sensitive files outside the web root, but unless you run your own server, your host may not allow this. But, not to worry. Here are a couple of simple steps you can take so hackers won't access sensitive stuff, especially config.php in your includes folder. To prevent direct access to any PHP files, check a defined PHP constant in your include files, and use htaccess to prevent hackers from viewing files or indexing folders from their browsers. But keep in mind that hackers cannot read the PHP sections of your PHP files normally anyway, regardless of these steps. But just in case your PHP is down, take the steps. We tested the check a defined PHP constant trick, and all browser using snoopers see is "Direct access not allowed." That was before we added the htaccess protection. With that in place, they just saw FORBIDDEN. Double protection feels good.
- You should use sha256 or sha512 instead of sha1 or md5 password hashing in your security algorithms. [You need to use the PHP hash() function for most hashing algorithms. Example: hash('sha256', 'a string to hash');. But you can do md5('a string to hash'); and sha1('a string to hash'); without hash().] The sha1 or md5 hash algorithms already have rainbow tables that crackers use during brute force attacks, so they have both been compromised, and md5 suffers from the collision curse as well, to a degree. (Hash collisions exploitation is hackers taking advantage of the probability that two or more hashes have the same password, and the solution to avoiding this is to have longer passwords to start with.) Terms: A hash is a set-length one-way code, calculated based on the contents of a password. One cannot use it to find the original password, unless one is a cracker and uses rainbow tables, which only work if the hashing algorithms have had tables created for them—like md5. Rainbow tables are tables of all the hashes of passwords up to certain lengths, along with the passwords that created them. If you find a match to the hash code, you find the password—it's cracked. In practice, hashes of passwords are stored in a database (usually MySQL), rather than the passwords themselves, when the users enters new passwords. Then when the user logs in later, his input is hashed again with the same hashing algorithm and compared with the hash in the database. A match means the right password was typed. Summary, avoid weak hashing algorithms like sh1 and md5, and during sign-ups hash and store entered passwords as hashes only. If forgetful users ask you what their password is, tell them to enter a new one (since you have no idea what it is!), but make sure they give you their user name and email address first to verify they can be trusted.
- NEVER ENCRYPT! Encryption may seem like a good idea but the process is reversible. Anyone with access to your code would have no trouble transforming the passwords back to their originals. Encryption is good for obscuring data before storing it in a db and then decrypting it when called up, but not good for pw storing! Encrption is 2-way, leaving an opening for crackers that you really don't want to give. Hashing is good, for passwords. Encrypting is bad.
- Do not give users clues about why login attempts fail. Hackers will use this info against you. You can add "LIMIT 1" to the end of your SQL statements. That will limit the number of results to just 1. If someone successfully hijacks your site, and is able to run an SQL statement that returns data, putting "LIMIT 1" at the end of SQL command strings will help limit the amount of data they are able to view or mess up. Example:
SELECT * FROM user_account WHERE Login='$login' AND Password='$hash' LIMIT 1
Also: use the "maxlength" option in your HTML form elements so hackers cannot POST evil commands to your db. And trim logins to longest user name size allowed. Finally, use the "at" sign, @, which will stop from showing PHP function call failures in the browser window—give hackers less info that way. Example:
$login = @substr($login, 0, 8);.
- Avoid GET where possible, and use $_POST, not $_REQUEST, so hacker cannot use GET attack via URL string.
- Clean, limit, filter, and distrust ALL user inputs. Always limit user access, and db permissions, according to the role
each user plays.
- SSL Encryption (https). To better ensure the privacy of the data being sent across the internet, purchase an SSL certificate to encrypt the login page, and any others where data is put on the Net. Secure Sockets Layer, SSL, is the standard security technology for creating an encrypted link between web server and browser. This link ensures that all data passed between the web server and browser remain private. SSL is used by millions of sites for transaction protection. To generate an SSL link, a web server must have an SSL Certificate. Make sure you get one that costs money from a reputable company, as the free ones cause scary error messages and false security in your customers, so you'll be likely to lose them quickly!
- Always use the PHP checkdnsrr() function to check if domains entered in email addresses exist. Check domain name server records corresponding to a given Internet host name, but filter out bad characters first, and look out for second level domain anomalies, and if a user-supplied email address fails a validity check, don't tell the user the email address they entered is invalid and force them to enter something different. Instead, gently inform them of the apparent problem (this function is not perfect).
- Use wise hashing routines. This mix() function hashes the password, one character at a time, with md5(), then does a sha512() hash on the result before it gets trimmed down to 65 characters.
function mix(){
global $password, $c;
$p = str_split($password);
foreach ($p as $h){$m .= md5($h);}
$c = hash('sha512',$m);
$c = substr($c, 0, 65);}
- Use SALTs with hashes. Above is a good hash routine. Now follow the salt instructions that follow, using it as a security multiplier for logins, etc. Before generating a hash, create a random string of characters of a predetermined length, and append or prepend this string to your plain text password. If the string ("salt") is long and random enough, the resulting hash will nearly always be different each time you run the hashing function. Don't forget to store this salt in the database along with the hash, for each user, by widening the password field by the number of characters in the salt. When you validate a user's login data, follow the same process, using the salt from the database (using PHP's substr() function) instead of generating a new random salt. Add the user supplied password to it by concatenation, run your hashing algorithm, then compare this result with the hash stored in the user's profile.
- To prevent direct access to files, and only allow them as include files, check a defined PHP constant in the include file. Use htaccess stuff on each of these includes, too. Here is the constant checking script:
includefileonly.php is below:
<?php
defined('_NODIRECTACCESS') or die('Direct access not allowed.');
/* more */
?>
script.php is below:
<?php
define('_NODIRECTACCESS', TRUE);
include('includefileonly.php');
/* more */
?> - Use security conscious htaccess files in includes folders on your server's public files. The config.php file and the includefileonly.php file (from above) won't be allowed to be viewed (hackers will see "Not Authorized"), and your site cannot be indexed by others, using the code below. Note that in the htaccess file below, the includefileonly.php file, as well as your config.php file that gives your precious db connection data, are both set up for access denial.
<Files "config.php">
order deny,allow
deny from all
</Files>
<Files "includefileonly.php">
order deny,allow
deny from all
</Files>
Options -Indexes - The easiest way to slow hackers is with delays. This will delay each login attempt by a second (don't go over 2 seconds), whether it is successful or not, giving no clue to a hacker whose brute force attack is attempting to try a lot of words per second. He may decide to go elsewhere, since he's getting the idea your server is swimming in molasses.
sleep(1); // 1 second delay
if (gettheLogin($_POST['username'], $_POST['password'])
{loggedInOK();}else{loginFailed();}
- One thing about "limit login attempts" scripts: programmers disagree about which type is best. Some hypothesize that since sessions, cookies and IP addresses are not very effective, since all can be manipulated by hackers, then to prevent brute force attacks the only practical solution is to base the number of attempts on the username provided, but this method is vulnerable to hackers doing denial of service on the site by blocking users from logging in. That's why we showed the simple sleep() script above. Some say that introducing a logrithmically increasing delay between password submission attempts to slow down automated password guessing programs is best (2 seconds, 4 seconds, 8 seconds, 16 seconds, etc.). If you want to go all out and get a really comprehensive script for preventing brute force attacks by limiting login attempts, your best bet is Blocking access to the login page after three unsuccessful login attempts. Hopefully, this script is not overkill.
- Keeping lots of login info is a good proactive security measure. Store the total number of logins for each user, as well as the data and the time of their last login. Storing the logins total is useful for security purposes. Keeping track of a user's last login is useful in the event that someone logged in using his/her account, without permission. You now know the time it happened, and if you store the date and the time of any changes in your database and by whom, you can track what that attacker (or snooper) did while logged in. The user account table in your MySQL database should have fields that track login_count, last_login, and current_login. When the user logs in, update his/her data in the database by incrementing his/her login count and by getting the timestamp using PHP's built-in date() function. After login, transfer the data from current_login to last_login and then insert the new date and the time into current_login.