Brian Desmond's Blog

Inherits Network.Admin
Implements IOneManBand

Password encryption - in your SProcs!

Whilst working on the login system for a now complete project, I learnt about a couple of unsupported but useful functions in sql - pwdencrypt and pwdcompare. SQL uses these to 1-Way hash passwords. Here's an example of how to use them:

You'll want a simply users table, note the varbinary(256) column for password

CREATE TABLE [Users] (
 [Username] [nvarchar] (20) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
 [Password] [varbinary] (256) NOT NULL ,
 CONSTRAINT [PK_mmj_Users] PRIMARY KEY  CLUSTERED
 (
  [Username]
 )  ON [PRIMARY]
) ON [PRIMARY]
GO

The create user operation is pretty straightforward:

create PROCEDURE mmj_CreateUser
(
 @Username nvarchar(20),
 @Password nvarchar(15)
)
AS

INSERT INTO
  Users
 (
  Username,
  [Password]
 )
 VALUES
 (
  @Username,
  CONVERT(varbinary(256),pwdencrypt(@password))
 )

And finally, the validate user procedure:

ALTER  PROCEDURE ValidateUser
(
 @Username nvarchar(20),
 @Password nvarchar(15),
 @Valid bit OUTPUT
)
AS

SELECT
 @Valid = pwdcompare(@Password, [Password])
FROM
 Users
WHERE
 Username = @Username

So, to sum things up:

pwdencrypt accepts a (n)varchar/(n)char value which you then convert to binary format. It always comes out to length 256. pwdcompare accepts two arguments: the supplied password, and the hashed password. It hashes the supplied password using pwdencrypt, and compares that to the hashed password (your password column). If the supplied password is correct, it returns 1, otherwise 0.

Posted: Aug 15 2003, 12:44 AM by bdesmond | with 5 comment(s)
Filed under:

Comments

Douglas Reilly said:

Historically, there has been at least one occasion where these functions broke version to version.

For instance, from 6.5 to 7, the functions worked differently. 6.5 code broke when used on 7.0, and compatibility is explicitly not assured even on a service pack.

Using the .NET hashing utilities is a cleaner, documented way to get passwords hashed.
# August 15, 2003 9:09 AM

HumanCompiler said:

Doug, it might be cleaner code wise, but is it really that "cleaner" overall? You're just taking it further from the DB and creating more seperating for a (what should be) simple task. Until Yukon, I think the word "cleaner" is an opinion ;)

Thanks for posting that, Brian! I may end up using that until Yukon! :D
# August 15, 2003 1:32 PM

Brian Desmond said:

I agree that there was a problem with this thing from version to version. You should always test things like service packs before applying them. So, if the service pack breaks it, you'll know ahead of time because you tested it (and won't encounter the problem on the live box).
# August 15, 2003 10:19 PM

Adam Kinney said:

Yeah, great info. I was wondering if something like this was available in sql and hadn't looked yet.

Oh yeah, and happy belated birthday :)
# August 15, 2003 11:37 PM

TrackBack said:

# February 19, 2004 2:10 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)