PHPSecure 2.0
PHPSecure 2.0
As a PHP developer over the last couple of years, I've developed several webpages, most of which needed some form of login. Realizing that this was going to be a common need throughout my development career, I decided to develop a PHP security engine capable of most common security tasks.
My first version of this engine was developed in response to a need for a high-security database management application for a client I was working with. While it worked well, it was rather inefficient, and required a fair amount of overhead. Once it was in fair operating order, I took the code and saved it, deeming (rightly) that I'd be able to use it again. Since that time, I've used it in a total of 3 websites, upgrading and tweaking it as I went. However, this still didn't change the fact that it was rather inefficient, and had a fair amount of overhead that was completely needless.
Thus, over the past 3 days, I've rebuilt the system from scratch. The engine currently consists of 393 lines of code, as well as a settings file, strings file, and publicly available javascript implementation of the md5 hash function. The new security system has many important features:
A user system, complete with ready-to-use login form incorporating extreme password security
A configurable and powerful \"groups\" system to distinguish users from each other
A decent set of function calls, including a replacement for the mysql_query() function
Development enhancements--ensure a user is allowed to perform certain actions in certain areas of your website as he/she is attempting to perform them
Maintenance tasks, such as opening SQL connections, are performed for you
Multiple security features, including:
Inclusion hack blocking
Automated buffer overflow attack prevention for all $_GET and $_POST variables
SQL injection hack prevention
clean_input() function to clean up any user input
Cookie-based login system featuring three layers of encryption--if a user has Javascript enabled, it is impossible for anyone, even the website the administrator, to get access to their unencrypted password
This is certainly a developer's tool--it won't help you unless you know how to use a database and know PHP fairly well. What it WILL do is reliably, securely, and efficiently check user logins and groups, as well as ensure input variable security. However, it does have a few requirements in order to perform its job as expected:
An installation of the Crypt/Blowfish PEAR extension
An installation of PHPMailer, a PHP implementation of SMTP mail
A MySQL database (though this can be replaced with Microsoft SQL by doing a simple find/replace on the security pages)
So, on to the main point of this post. Basically, at this point, I'm finishing up some small tweaks and doing a boatload of testing in preparation of moving to a public release. At this point, my purpose in posting this is twofold.
1. I want to gauge interest in a developer's aid such as this
2. I want to see what features and comments YOU have on this project. What would YOU like to see in the way of function calls, security features, or automatic protection?
Any comments/questions/suggestions? I'll be more than happy to look into even the most farfetched idea, if only to see if it's possible. Hopefully I should be able to finish up the rest of this project in a couple of days, and when I do I'll make this available as a public download.
My first version of this engine was developed in response to a need for a high-security database management application for a client I was working with. While it worked well, it was rather inefficient, and required a fair amount of overhead. Once it was in fair operating order, I took the code and saved it, deeming (rightly) that I'd be able to use it again. Since that time, I've used it in a total of 3 websites, upgrading and tweaking it as I went. However, this still didn't change the fact that it was rather inefficient, and had a fair amount of overhead that was completely needless.
Thus, over the past 3 days, I've rebuilt the system from scratch. The engine currently consists of 393 lines of code, as well as a settings file, strings file, and publicly available javascript implementation of the md5 hash function. The new security system has many important features:
A user system, complete with ready-to-use login form incorporating extreme password security
A configurable and powerful \"groups\" system to distinguish users from each other
A decent set of function calls, including a replacement for the mysql_query() function
Development enhancements--ensure a user is allowed to perform certain actions in certain areas of your website as he/she is attempting to perform them
Maintenance tasks, such as opening SQL connections, are performed for you
Multiple security features, including:
Inclusion hack blocking
Automated buffer overflow attack prevention for all $_GET and $_POST variables
SQL injection hack prevention
clean_input() function to clean up any user input
Cookie-based login system featuring three layers of encryption--if a user has Javascript enabled, it is impossible for anyone, even the website the administrator, to get access to their unencrypted password
This is certainly a developer's tool--it won't help you unless you know how to use a database and know PHP fairly well. What it WILL do is reliably, securely, and efficiently check user logins and groups, as well as ensure input variable security. However, it does have a few requirements in order to perform its job as expected:
An installation of the Crypt/Blowfish PEAR extension
An installation of PHPMailer, a PHP implementation of SMTP mail
A MySQL database (though this can be replaced with Microsoft SQL by doing a simple find/replace on the security pages)
So, on to the main point of this post. Basically, at this point, I'm finishing up some small tweaks and doing a boatload of testing in preparation of moving to a public release. At this point, my purpose in posting this is twofold.
1. I want to gauge interest in a developer's aid such as this
2. I want to see what features and comments YOU have on this project. What would YOU like to see in the way of function calls, security features, or automatic protection?
Any comments/questions/suggestions? I'll be more than happy to look into even the most farfetched idea, if only to see if it's possible. Hopefully I should be able to finish up the rest of this project in a couple of days, and when I do I'll make this available as a public download.
I've recently designed and still master a PHP site and am still researching into all of this security stuff. Before I can gauge my own interest, I have some questions as to how my current practices measure up to the engine's (probably poorly in some cases).
What are inclusion hacks?Inclusion hack blocking
I'm also a little clueless as to how properly secure against these and even how they come about. Could you explain? All of my $_GET's are ints, so I convert them to such before using them. For the $_POST's, I make sure that their length is within a certain range of lengths, depending on the field. Again, I have no idea how far any of this goes to protect against overflows, since this wasn't really what I had in mind when I enacted these policies (more like content control).Automated buffer overflow attack prevention for all $_GET and $_POST variables
Is there anything to worry about if I'm using mysql_real_escape_string() for all variables in database queries?SQL injection hack prevention
I store my passwords as hashes in the database, so the real password isn't accessible by the admin (i.e. me) regardless of javascript or anything. I authenticate once and then use php sessions from there, which includes support for session cookies. This does mean that the user has to re-login every time he/she closes the browser. If I wanted to change this, the best course of action I can think of is generating some sort of random hash, saving it in the database under the user in a new column, and then in a cookie. This way someone looking at the cookie wouldn't have anything even logically connected with the password. It still seems as though if somebody had access to your cookies that they could just duplicate them to gain access, but this seems unavoidable?Cookie-based login system featuring three layers of encryption--if a user has Javascript enabled, it is impossible for anyone, even the website the administrator, to get access to their unencrypted password
Happy to oblige; I'm hoping to turn this into a sort of open-sourced development process.Jeff250 wrote:I've recently designed and still master a PHP site and am still researching into all of this security stuff. Before I can gauge my own interest, I have some questions as to how my current practices measure up to the engine's (probably poorly in some cases).
Inclusion hacks are rare, and can only come from someone else on the same server that knows the filepath to your website. Basically, they would simply call an include() on your page using a direct filepath, and could then echo variables until they found whatever they were looking for.What are inclusion hacks?
This is a common problem, and one that I hope I've solved effectively. In essence, since $_GET and $_POST are arrays, I simply used a foreach loop to go through each $_GET and $_POST, truncating each of them exceeding 10,000 characters to that length with substr().I'm also a little clueless as to how properly secure against these and even how they come about. Could you explain? All of my $_GET's are ints, so I convert them to such before using them. For the $_POST's, I make sure that their length is within a certain range of lengths, depending on the field. Again, I have no idea how far any of this goes to protect against overflows, since this wasn't really what I had in mind when I enacted these policies (more like content control).
Well, that's a lot more secure than nothing--however, I've actually never been able to get that function call working correctly. In this case, the clean_input() function I've created uses htmlentities() to convert ' and " symbols (as well as other common signs) to their HTML equivalents, effectively removing their ability to be used in SQL. In addition, every SQL statement made using the custom database_request() function checks for SQL keywords (SELECT, INSERT, UPDATE, DELETE, DROP, and TRUNCATE), and compares those to a list of SQL statements a user is allowed to make. If one of these words is found in the query, and the user is not allowed to make a statement with that word, it throws an error and stops page operation.Is there anything to worry about if I'm using mysql_real_escape_string() for all variables in database queries?
Ahh, yes. The infamous question of "How much security is enough?" In response, I'll simply tell you how how this encryption system works.I store my passwords as hashes in the database, so the real password isn't accessible by the admin (i.e. me) regardless of javascript or anything. I authenticate once and then use php sessions from there, which includes support for session cookies. This does mean that the user has to re-login every time he/she closes the browser. If I wanted to change this, the best course of action I can think of is generating some sort of random hash, saving it in the database under the user in a new column, and then in a cookie. This way someone looking at the cookie wouldn't have anything even logically connected with the password. It still seems as though if somebody had access to your cookies that they could just duplicate them to gain access, but this seems unavoidable?
On the server resides a javascript page that implements the md5() hashing routine. As the user submits his/her login, the md5() function encrypts the password and sends it. This adds a first layer of security--md5() is a currently unbroken, one-way encryption hash. Thus, by the time the username and password reach the server, the password is already encrypted.*
Next, the server checks it against a password stored in the database. To further secure things against all forms of attacks, the hashes stored in the database are 64-byte sha256 hashes of the md5 hash--this, in essence, makes even brute-forcing one of these passwords impossible even if you have unrestricted database control. With only one hash, it's possible to make a random password generator to simply try things until one matches the stored hash--in which case you know the user's password. With a double-encrypted hash, it becomes exponentially harder to brute-force the password.
In order to store the cookie, the system doesn't simply put the user's username and password in a cookie--as you pointed out, that would be downright stupid. Rather, in this application the security system creates a vertical pipe-delimited string consisting of several elements--the username, the md5 hash of the password, whether or not the user wants to be remembered on this computer, and the current time according to the server (to provide an element of randomness). It then encrypts this data with Blowfish encryption and turns the binary Blowfish output back into hexadecimal, then stores the resulting hex hash in the cookie.
Since Blowfish is a two-way encryption/decryption algorithm, whenever the user comes back to the page or goes to another page within the site, the cookie is read, decrypted, exploded into its component parts, and used to determine the user's username and password, which then are compared to the database again as if the user had just logged in.
It's true that if the cookie is stolen, it might be used to log onto that site by another person--however, this is a necessary risk, and one that you take every time you check that "remember me" button on any website login.
I haven't incorporated session identification in this system--that, I suppose, should be my next project.
*If the user has javascript disabled, the security system will detect it and md5() the password before storing it in a variable. It removes a minor point of security, since the password isn't encrypted before being sent to the server and might be read in-transit--but then, since most websites that have a real need for this much security use SSL anyway, this isn't that much of a concern.
Using Javascript seems like an interesting way to encrypt the passwords before they're sent, especially on sites without SSL, like mine. I'll consider that.
In general, I think I'm fairly comfortable with the security of my site, but I'd still be interested in seeing this project if nothing else just to see more explicitly how you handle security issues.
In general, I think I'm fairly comfortable with the security of my site, but I'd still be interested in seeing this project if nothing else just to see more explicitly how you handle security issues.
Does it? Assuming you knew that the hash in the database was the hash of a hash of the password, like an admin might, it seems to me that brute-forcing users' passwords would still take as much time to crack a password big-oh-wise, namely O(n), where n is the number of possible password combinations. For example, if passwords were a minimum of 4 characters, you might start out with something like aaaa, aaab, aaac, etc., hash it, and then hash the hash. It might slow down a brute forcing program by some numerical constant coefficient, but I don't see how it would in any way affect big-oh or create exponential time.Stryker wrote:With only one hash, it's possible to make a random password generator to simply try things until one matches the stored hash--in which case you know the user's password. With a double-encrypted hash, it becomes exponentially harder to brute-force the password.
Hmm... you may have a point about that. However, if an administrator is really phishing for passwords, there's really nothing you can do about it--imagine what would happen if Richard Cranium came phishing for your password! There's quite literally nothing preventing him; you just have to trust the administrator. This double-encryption layer is mostly designed to keep a hacker from figuring out passwords from a stolen database. Again, it's not foolproof and it's not a cureall--but in general, it's another layer between \"data theft\" and \"failed data theft\".
These days I'm using salted hashes in the database (salt being the user name + a constant string). Client-side, I'm using Javascript to hash the password with the salt and then with a random challenge string sent by the server, which is \"unique\" to the session. I think I've got all of my bases covered, not that this all isn't overkill for the purposes and scope of the site.