A word of warning about external Maxtor (Seagate) OneTouch USB drives: I have two of them (a 1TB and a 500GB), and both have lost their partition tables in less than a month's span. Granted, these drives are 2 and 3 years old (respectively), but one would expect a drive to retain its data at least for the duration of the 5 year manufacturer's warranty.
After doing a lot of reading, it appears the USB->IDE interface used in the enclosure is of suspect quality. I can unscientifically confirm this, as I have been able to temporarily rectify intermittent problems with both of these drives by replacing aged USB cables (though the cables work flawlessly in other applications).
This problem appears to be quite common, and there are various methods for recovering the data. I have chosen two, and am trying the least invasive first: an open source program called TestDisk, which has a good reputation for repairing deleted/corrupt partition tables (and data).
If this fails, I'll simply open the case and remove the drive, then plug it directly into the internal IDE interface. (The 1TB drive may be a SATA drive, which would be even better.) In either case, the drives themselves are apparently rarely defective, so I'll only lose the portability of the drives, which in this case is not a huge concern, as they are always connected to the server.
These drives have been problematic for the majority of the time I've owned them, and it doesn't seem to matter what OS they're connected to. (I've had them on 3 different machines: XP, Windows 7, and Linux.) I don't believe these specific drives are commonly sold anymore, but still, I recommend that people steer clear of them, as well as their new Seagate counterparts.
For the record, I've never been a fan of either manufacturer (before or after Seagate's acquisition of Maxtor), and only purchased the drives because of their low price point. Once again, the old adage is reaffirmed: you get what you pay for.
Thank goodness all my other drives are Western Digital.
Sunday, November 13, 2011
Saturday, July 9, 2011
Securing login credentials in a MySQL database using PHP
Here's a little tidbit from a project I've been working on. I decided to write a new user account system from scratch over the weekend to replace a very old system I used in a Content Management System that I've been updating. One of the things I've been focusing on is security, as I've seen a rise in attempts to hack some of my older security systems. (Hey, it's the innarwebs, and there are 1337 people out there.)
The core of my new system is a double salted SHA256 password hash. There are many different ways to encrypt password data, but after a good deal of experimentation, I find this to be a great combination of security and efficiency, and is definitely more secure than my previous implementation (which wasn't flawed, just simply outdated).
This requires three components: a user-supplied password, a salt stored in the user record, and a master salt stored in a server-side file.
We have no control over the user's password other than to impose certain character length or combination requirements, so that can be as strong or as weak as the user creates. However, the point of this is primarily to protect the user's password in case the database becomes compromised, so we have to focus on the salts.
The master salt is stored in code, and remains constant. Because of this, we can (and should) be complex with it. A master salt could be something like:
or whatever suits your fancy.
The user salt is something that gets generated at the time the user record gets created (at new user account generation), and is a random string. For that, I use a function like:
So:
A sample password of "secret" using this combination might look like:
To get the full hash for the above, I'm basically using:
Now this is part of a MySQL query, so of course there's a little more to it, and that means we have to protect against SQL injection attacks. Fortunately for us, PHP has a built-in function that helps us do just that:
mysql_real_escape_string();
This escapes SQL control and delimiter characters (with a \ character) to make them literal, so that MySQL processes them as part of the lookup string instead of appending them to the query. (For more detail on this and how to prevent it, Google SQL injection.)
So using this injection prevention technique, we need to construct the lookup query to make sure a user's credentials are valid. There may be better ways to accomplish this, but mine is a two-step process.
First, find the record associated with the user's login name:
Since usernames are unique, I only want 1 result, and using the LIMIT keyword speeds up the query by returning the first result it finds, rather than continuing through the table looking for additional matches (which it will obviously never find).
If no matches are found, we simply drop out of our lookup, throwing a generic username/password fail message. In my opinion, telling a potential hacker that the specific username wasn't found makes for a weaker system. We don't want them to know that a username does or doesn't exist, because if they find one that does exist, that cuts their work in half.
If we find a match, however, it's time to employ our password lookup, which requires a little preparation. We need to grab a couple things from the record returned.
In most tables, each user record has a unique record ID set as the primary key, so we'll use that as the user ID. We'll store that as the $user_id variable. (Querying against the primary key instead of the username is just to save a few milliseconds.)
Remember that user salt that I stored in the user record? Grab that as well, and place it in the $user_salt variable. Our $master_salt variable should already be set in the code somewhere, so we already have that. Using a secure version of the $hash_string formula above, we can construct a query that will check the username against the password provided.
It should look something like this:
The rest is simple. If the password matches, we have a valid login. If it doesn't match, we throw the same username/password error message we would if the username was invalid. (Remember, even security through obscurity is still security, and every little bit helps.)
There's more that we can do to protect our users' data, but protecting the password is perhaps the most important consideration in any system.
Have you built a better mouse trap? If so, I'm very interested in hearing about it.
The core of my new system is a double salted SHA256 password hash. There are many different ways to encrypt password data, but after a good deal of experimentation, I find this to be a great combination of security and efficiency, and is definitely more secure than my previous implementation (which wasn't flawed, just simply outdated).
This requires three components: a user-supplied password, a salt stored in the user record, and a master salt stored in a server-side file.
We have no control over the user's password other than to impose certain character length or combination requirements, so that can be as strong or as weak as the user creates. However, the point of this is primarily to protect the user's password in case the database becomes compromised, so we have to focus on the salts.
The master salt is stored in code, and remains constant. Because of this, we can (and should) be complex with it. A master salt could be something like:
OhFeioA03*&#wij24
or whatever suits your fancy.
The user salt is something that gets generated at the time the user record gets created (at new user account generation), and is a random string. For that, I use a function like:
function create_salt() { return substr(md5(uniqueid(rand(), true)), 0, 8); }
So:
$password = "userpass"; $master_salt = "OhFeioA03*&#wij24"; $user_salt = create_salt();
A sample password of "secret" using this combination might look like:
8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92543bb60db959662524dae2c4dd4d1c245f0dee9947d39fc10737ff74515b2446
To get the full hash for the above, I'm basically using:
$hash_string = hash('sha256', $password).hash('sha256', $master_salt).hash('sha256', $user_salt);
Now this is part of a MySQL query, so of course there's a little more to it, and that means we have to protect against SQL injection attacks. Fortunately for us, PHP has a built-in function that helps us do just that:
mysql_real_escape_string();
This escapes SQL control and delimiter characters (with a \ character) to make them literal, so that MySQL processes them as part of the lookup string instead of appending them to the query. (For more detail on this and how to prevent it, Google SQL injection.)
So using this injection prevention technique, we need to construct the lookup query to make sure a user's credentials are valid. There may be better ways to accomplish this, but mine is a two-step process.
First, find the record associated with the user's login name:
$query = "SELECT * FROM `users_table` WHERE `username` = '".mysql_real_escape_string($username)."' LIMIT 1";
Since usernames are unique, I only want 1 result, and using the LIMIT keyword speeds up the query by returning the first result it finds, rather than continuing through the table looking for additional matches (which it will obviously never find).
If no matches are found, we simply drop out of our lookup, throwing a generic username/password fail message. In my opinion, telling a potential hacker that the specific username wasn't found makes for a weaker system. We don't want them to know that a username does or doesn't exist, because if they find one that does exist, that cuts their work in half.
If we find a match, however, it's time to employ our password lookup, which requires a little preparation. We need to grab a couple things from the record returned.
In most tables, each user record has a unique record ID set as the primary key, so we'll use that as the user ID. We'll store that as the $user_id variable. (Querying against the primary key instead of the username is just to save a few milliseconds.)
Remember that user salt that I stored in the user record? Grab that as well, and place it in the $user_salt variable. Our $master_salt variable should already be set in the code somewhere, so we already have that. Using a secure version of the $hash_string formula above, we can construct a query that will check the username against the password provided.
It should look something like this:
$query = "SELECT * FROM `users_table` WHERE `user_id` = ".$user_id." AND `password` = '".hash('sha256', mysql_real_escape_string($password).hash('sha256', $master_salt).hash('sha256', $user_salt)."' LIMIT 1";
The rest is simple. If the password matches, we have a valid login. If it doesn't match, we throw the same username/password error message we would if the username was invalid. (Remember, even security through obscurity is still security, and every little bit helps.)
There's more that we can do to protect our users' data, but protecting the password is perhaps the most important consideration in any system.
Have you built a better mouse trap? If so, I'm very interested in hearing about it.
Friday, June 24, 2011
Lockdown!
EDIT: I've added links to some walk-throughs here to make this a simple 1-2-3 process. See the end of the post for these.
One thing I don't discuss enough is security in relation to mobility, and it's funny that I don't because I'm entirely mobile and strongly advocate anything that promotes telecommuting and working from unconventional locations. While I don't profess to be a security expert, it is my understanding that this configuration is more than adequate for most Road/Code Warriors. (That has to be an existing term, I'm sure. If not, I hereby lay claim to coining it.)
So the point is to have access to your home office network while you're out on the road (or at the park with the kids, for fellow domesticated types). You want to see your office desktop from your smartphone screen so you can make a couple quick edits, start a long download, and start compiling your latest spaghetti code so that everything's ready to tinker with when you get back. But you don't want to leave the front door unlocked so that any 14 year old with a port scanner and a brute force password generator can take down all your hard work. Here's what I do.
First is to close off all inbound ports except one: SSH. I have a dedicated machine serving as my SSH server, and that's all it does. It's an ancient PII-400 machine that I had laying around, loaded with a trimmed down Linux install. It might not seem like much, but it does what it needs to do very well, which is to accept inbound SSH connections and route the remote connection. [Update: This is now done with a virtualized machine on the primary server.]
With the SSH port open, you'll need a server to listen and process your login information. In Linux, sshd is typically part of the standard distribution, and there are numerous walk-throughs available around the web to get you set up. In Windows, the most painless setup I've found is freeSSHd. Specific configuration is beyond the scope of this article, as there are numerous howto documents for each package floating in various places all over the web.
Once your SSH server is configured, you'll set up your terminal client. In Linux, the standard terminal will suffice just fine. In Windows, I prefer PuTTY, but there are others available. Create a connection to your server, and log in with your username and password.
Here's where things get fun. Port tunneling. Set up your SSH client to tunnel any required ports for things like RDP, VNC, or what have you, to the machine address you want to connect to, then point your portable client to connect to localhost on those ports. The ports get forwarded through your secure connection to the appropriate destination machine, and have the benefit of 128-bit encryption from point to point.
Step 1: Install your SSH daemon. Since the original writing of this post, I've found that freeSSHd has some packet overrun issues when dealing with RDP, so I've switched to the Cygwin OpenSSH package. There are numerous benefits to this approach, not the least of which are standards compliance, no-cost use, and 256-bit AES encryption if you want it. Follow HowToGeek's OpenSSH howto to get it running.
Step 2: Install PuTTY and configure it, which includes setting up your port tunnel.
Step 3: Close off all external ports except your SSH port, which will typically be port 22. If you have other services which require open ports, go ahead and leave those open, but the essential bit here is that you do NOT leave your RDP port (3389) open to the universe. That's the whole point of port tunneling, after all.
If you set up your SSH server to use RSA key access rather than simple password authentication, you can pretty much rest assured that you (or those you've provided your key to) will be the only one ever connecting to your SSH server.
I hope this has you on your merry way. Feel free to leave questions or comments below.
One thing I don't discuss enough is security in relation to mobility, and it's funny that I don't because I'm entirely mobile and strongly advocate anything that promotes telecommuting and working from unconventional locations. While I don't profess to be a security expert, it is my understanding that this configuration is more than adequate for most Road/Code Warriors. (That has to be an existing term, I'm sure. If not, I hereby lay claim to coining it.)
So the point is to have access to your home office network while you're out on the road (or at the park with the kids, for fellow domesticated types). You want to see your office desktop from your smartphone screen so you can make a couple quick edits, start a long download, and start compiling your latest spaghetti code so that everything's ready to tinker with when you get back. But you don't want to leave the front door unlocked so that any 14 year old with a port scanner and a brute force password generator can take down all your hard work. Here's what I do.
First is to close off all inbound ports except one: SSH. I have a dedicated machine serving as my SSH server, and that's all it does. It's an ancient PII-400 machine that I had laying around, loaded with a trimmed down Linux install. It might not seem like much, but it does what it needs to do very well, which is to accept inbound SSH connections and route the remote connection. [Update: This is now done with a virtualized machine on the primary server.]
With the SSH port open, you'll need a server to listen and process your login information. In Linux, sshd is typically part of the standard distribution, and there are numerous walk-throughs available around the web to get you set up. In Windows, the most painless setup I've found is freeSSHd. Specific configuration is beyond the scope of this article, as there are numerous howto documents for each package floating in various places all over the web.
Once your SSH server is configured, you'll set up your terminal client. In Linux, the standard terminal will suffice just fine. In Windows, I prefer PuTTY, but there are others available. Create a connection to your server, and log in with your username and password.
Here's where things get fun. Port tunneling. Set up your SSH client to tunnel any required ports for things like RDP, VNC, or what have you, to the machine address you want to connect to, then point your portable client to connect to localhost on those ports. The ports get forwarded through your secure connection to the appropriate destination machine, and have the benefit of 128-bit encryption from point to point.
Step 1: Install your SSH daemon. Since the original writing of this post, I've found that freeSSHd has some packet overrun issues when dealing with RDP, so I've switched to the Cygwin OpenSSH package. There are numerous benefits to this approach, not the least of which are standards compliance, no-cost use, and 256-bit AES encryption if you want it. Follow HowToGeek's OpenSSH howto to get it running.
Step 2: Install PuTTY and configure it, which includes setting up your port tunnel.
Step 3: Close off all external ports except your SSH port, which will typically be port 22. If you have other services which require open ports, go ahead and leave those open, but the essential bit here is that you do NOT leave your RDP port (3389) open to the universe. That's the whole point of port tunneling, after all.
If you set up your SSH server to use RSA key access rather than simple password authentication, you can pretty much rest assured that you (or those you've provided your key to) will be the only one ever connecting to your SSH server.
I hope this has you on your merry way. Feel free to leave questions or comments below.
Is Virtualization the Answer?
As many of you know, I spend a lot of time hassling with the many machines on my network. It has long passed the point of me being a full-time network administrator just to manage my home network. I have a web server, a mail server, a home theater media server, a primary development desktop, two additional desktops in the bedrooms, two portable PCs, two additional floating desktops, and all that doesn't even take into account the mobile devices, media extenders, access points, and spaghetti nightmare of network line that has to be maintained. That's 10 PCs, plus extra devices.
It's far too much. Is this what the connected house of tomorrow will become? No, there has to be a better answer. And there is definitely a way to minimize the extraneous hardware. Enter virtualization.
Virtualized machines are rapidly gaining in popularity and practicality, and while I've been tinkering with them over the past few years, I've only just truly realized the full effects of utilizing the technology. When my primary development/gaming PC failed a few months ago, I spent a lot of time assessing my options. I went without until I could put a practical plan in place. And the result surprised me.
Typically, I like to have the power of my primary machine within arm's reach, but in this case, it just wasn't worth the hassle anymore. My recent lifestyle push toward minimalization steered me toward condensing functionality.
The overhead up front was rather large (compared to what I normally invest in a machine). I built an AMD 6-core PC with 8 GB RAM (which I will expand later) and installed Windows 7 64-bit so that it could be used as the home theater centerpiece. Here's where the magic happens: using Oracle VirtualBox, I created virtual machines that would take over the duties of other machines throughout the network.
I have one dedicated mail server, one web server, and a third (new) virtual machine that is a dedicated remote access interface. It has only a minimal compliment of software, but is locked down tight so as to be the only interface between my home network and the outside world. I also have this new server pulling double duty as both the entertainment centerpiece (HTPC) and primary gaming machine, figuring that the most gaming we do is in the living room (alongside the other entertainment devices, such as the XBox).
Now, using a separate login, I can RDP into that main machine from another desktop and utilize the processing power to handle all my development needs, and can do so without interfering with the people watching a recorded show on the media center downstairs.
I've condensed 4 machines into 1, and under typical operation, the CPU utilization of this new machine is well under 10%. It seems to me that I have more than enough room to handle the majority of my computing needs in this one box. Granted, a virtual machine still has administrative needs, but it certainly cuts down on the hardware maintenance, and eliminates a lot of the network requirements as well. It seems to me that if the few people in the house would use portable PCs exclusively, my unpaid job here would be much easier.
It's far too much. Is this what the connected house of tomorrow will become? No, there has to be a better answer. And there is definitely a way to minimize the extraneous hardware. Enter virtualization.
Virtualized machines are rapidly gaining in popularity and practicality, and while I've been tinkering with them over the past few years, I've only just truly realized the full effects of utilizing the technology. When my primary development/gaming PC failed a few months ago, I spent a lot of time assessing my options. I went without until I could put a practical plan in place. And the result surprised me.
Typically, I like to have the power of my primary machine within arm's reach, but in this case, it just wasn't worth the hassle anymore. My recent lifestyle push toward minimalization steered me toward condensing functionality.
The overhead up front was rather large (compared to what I normally invest in a machine). I built an AMD 6-core PC with 8 GB RAM (which I will expand later) and installed Windows 7 64-bit so that it could be used as the home theater centerpiece. Here's where the magic happens: using Oracle VirtualBox, I created virtual machines that would take over the duties of other machines throughout the network.
I have one dedicated mail server, one web server, and a third (new) virtual machine that is a dedicated remote access interface. It has only a minimal compliment of software, but is locked down tight so as to be the only interface between my home network and the outside world. I also have this new server pulling double duty as both the entertainment centerpiece (HTPC) and primary gaming machine, figuring that the most gaming we do is in the living room (alongside the other entertainment devices, such as the XBox).
Now, using a separate login, I can RDP into that main machine from another desktop and utilize the processing power to handle all my development needs, and can do so without interfering with the people watching a recorded show on the media center downstairs.
I've condensed 4 machines into 1, and under typical operation, the CPU utilization of this new machine is well under 10%. It seems to me that I have more than enough room to handle the majority of my computing needs in this one box. Granted, a virtual machine still has administrative needs, but it certainly cuts down on the hardware maintenance, and eliminates a lot of the network requirements as well. It seems to me that if the few people in the house would use portable PCs exclusively, my unpaid job here would be much easier.
Subscribe to:
Posts (Atom)