At my previous gig we stored the password as a hashed/encrypted/salted value (using MD5 at the time) as VARBINARY(32)
. To compare the password later, rather than try to decrypt the password, we would compared the encrypted + salted value that we stored to the encrypted + salted value of the password being attempted. If they matched, they got in, if they didn't match, they didn't get in.
The hashing work was done in the middle tier (both for saving the password initially and for comparing later), but a SQL Server-based example (to stop @Yahia's grumbling, this is not meant to tell you the most secure way possible, I am just illustrating the methodology with a very lightweight example. MD5 not strong enough for you? You can use a different and more complex algorithm along with more advanced salting techniques, especially if you perform the hashing in the application tier):
CREATE TABLE dbo.Users
(
UserID INT IDENTITY(1,1) PRIMARY KEY,
Username NVARCHAR(255) NOT NULL UNIQUE,
PasswordHash VARBINARY(32) NOT NULL
);
A procedure to create a user (no error handling or dupe prevention, just pseudo).
CREATE PROCEDURE dbo.User_Create
@Username NVARCHAR(255),
@Password NVARCHAR(16)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @salt NVARCHAR(16) = '$w0rdf1$h';
INSERT dbo.Users(Username, Password)
SELECT @Username,
CONVERT(VARBINARY(32), HASHBYTES('MD5', @Password + @Salt));
END
GO
Now a procedure to authenticate a user.
CREATE PROCEDURE dbo.User_Authenticate
@Username NVARCHAR(255),
@Password NVARCHAR(16)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @salt NVARCHAR(16) = '$w0rdf1$h';
IF EXISTS
(
SELECT 1 FROM dbo.Users
WHERE Username = @Username AND
PasswordHash = CONVERT(VARBINARY(32), HASHBYTES('MD5', @Password + @salt))
)
BEGIN
PRINT 'Please, come on in!';
END
ELSE
BEGIN
PRINT 'You can keep knocking but you cannot come in.';
END
END
GO
In reality you would likely perform the hashing within the application, and pass the hash values in as VARBINARY(32) - this makes it much harder to "sniff" the actual clear-text password from anywhere. And you maybe wouldn't store the salt in plain text with the code either, but retrieve it from elsewhere.
This is definitely more secure than storing the password unencrypted, but it removes the ability to ever retrieve the password. Win-win in my opinion.