Running into misbehaviour during hashing the password field - sql-server

I want to store hashed passwords in my database and I have used the following code:
ALTER PROCEDURE AddUser
#name NVARCHAR(MAX),
#password NVARCHAR(MAX),
#responseMessage NVARCHAR(MAX) OUTPUT
AS
Begin
SET NOCOUNT ON
INSERT INTO [User] (Username, PasswordHashed)
VALUES (#name, HASHBYTES('SHA2_512', #password));
END
Current instance of my table has the following state (The User's password is hi):
For validating users I'm using the following code:
SELECT COUNT(*)
FROM [User]
WHERE [User].Username = 'Bamdad' AND [User].PasswordHashed = HASHBYTES('SHA2_512', 'hi');
But the result is 0. Why doesn't the latter code work?

You are specifying your password 'hi' as varchar but procedure requires an nvarchar. So the varchar gets promoted to nvarchar with an extra byte, hence the difference in encryption.

Related

Create a Stored Procedure for AES Encryption in MS SQL Server 2008

I have an SQL Server 2008 table with a structure similar to the following:
ID int PRIMARY KEY IDENTITY(1,1)
Name nvarchar(100)
LongText ntext
What I am trying to achieve is simple. Before inserting data inside this table, I want to encrypt the LongText using AES_192 algorithm. I am using the following SP to encrypt data:
create proc sp_Encrypt_LongText
#rawText ntext = null,
#encryptedText nvarchar(max) output
as
begin
OPEN SYMMETRIC KEY Encryption_Symmetric_Key
DECRYPTION BY CERTIFICATE Encryption_Certificate WITH PASSWORD = 'mypassword'
set #encryptedText = ENCRYPTBYKEY(KEY_GUID(N'Encryption_Symmetric_Key'), cast(#rawText as nvarchar(max)))
CLOSE SYMMETRIC KEY Encryption_Symmetric_Key
end
and for decryption, I have created the following SP:
alter proc sp_Decrypt_LongText
#encryptedText ntext = null,
#decryptedText varchar(max) output
as
begin
OPEN SYMMETRIC KEY Encryption_Symmetric_Key
DECRYPTION BY CERTIFICATE Encryption_Certificate WITH PASSWORD = 'mypassword'
set #decryptedText = cast(DECRYPTBYKEY(cast(#encryptedText as nvarchar(max))) as varchar(max))
CLOSE SYMMETRIC KEY Encryption_Symmetric_Key
end
The procedures seem to work fine when I use the exec command. So far, so good. The problem is that the data is inserted and fetched inside the table using stored procedures; one each for insert and select. What I have as of now is as follows:
For insertion:
create proc sp_InsertData
#Name nvarchar(100),
#LongText ntext = NULL
as
INSERT INTO TABLE tbl VALUES (#Name, #LongText)
For fetching
create proc sp_FindDataById
#Id int
as
SELECT ID, Name, LongText from tbl where ID=#Id
My question is, how do I plug the encryption/decryption procedures inside these SPs to make them work?. I have looked into several articles for achieving this, but I keep running into one issue or another; mostly because of the ntext datatype. Or maybe I might be going on the wrong path here. Any kind of help is appreciated.
PS: Due to some reasons specified by the DBAs, I can't change the data type of LongText from ntext to nvarchar or varchar. Hence, all the casting is applied in the procedures.
Okay, so I managed to convince the DBAs to have the data transferred to a new column with varbinary(max) data type. Then I transferred the values into this new column after encrypting them, and then dropped the older column and renamed the new one to the old one's name. Took some work, but everything is running smoothly now. I managed to create a stored procedure and two functions to further modularize the scripts.
For opening the symmetric key
CREATE PROCEDURE sp_OpenEncryptionKeys
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
OPEN SYMMETRIC KEY Encryption_Symmetric_Key
DECRYPTION BY CERTIFICATE Encryption_Certificate
END TRY
BEGIN CATCH
--catch
END CATCH
END
For encrypting:
CREATE FUNCTION Encrypt
(
#ValueToEncrypt varchar(max)
)
RETURNS varbinary(max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result varbinary(max)
SET #Result = EncryptByKey(Key_GUID('My_Encryption_Symmetric_Key'), #ValueToEncrypt)
-- Return the result of the function
RETURN #Result
END
For decrypting:
CREATE FUNCTION Decrypt
(
#ValueToDecrypt varbinary(max)
)
RETURNS varchar(max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result varchar(max)
SET #Result = DecryptByKey(#ValueToDecrypt)
-- Return the result of the function
RETURN #Result
END
For inserting
exec sp_OpenEncryptionKeys
INSERT INTO tbl VALUES ('Name', Encrypt('some text here'))
For fetching
exec sp_OpenEncryptionKeys
SELECT ID, Decrypt(LongText) from tbl
Hope this helps someone.

T-SQL Cursor in stored procedure

I am using a stored procedure and I want to use cursor for inserting new data (if data exist I want to update)
ALTER Procedure [dbo].[conn]
#ResellerID int,
#GWResellerID int,
#UserName varchar(50),
#Password varchar(50),
#URL varchar(100),
#ServiceType int,
#ServiceDesc varchar(50),
#FeedFrom bit,
#PublicKey varchar(max)
AS
declare gateway cursor for
select *
from reseller_profiles
where main_reseller_ID = #ResellerID
OPEN gateway
FETCH NEXT FROM gateway INTO #ResellerID
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO [dbo].tblGatewayConnection([ResellerID],[GWResellerID], [UserName], [Password], [URL], [ServiceType], [ServiceDesc],[feedFromMain], publicKey)
VALUES (#ResellerID, #GWResellerID, #UserName, #Password, #URL, #ServiceType, #ServiceDesc, #FeedFrom, #PublicKey)
FETCH NEXT FROM gateway INTO #ResellerID
END
CLOSE gateway
DEALLOCATE gateway
My table name is tblGatewayConnection has these columns:
resellerID
gwResellerID
userName
password
url
serviceType
serviceDesc
feedFromMain
publicKey
While I insert data using the stored procedure, I get an exception
Cursorfetch: The number of variables declared in the INTO list must match that of selected columns.
What did I miss ?
Any help will be appreciated.
Thanks.
Why even bother with a cursor?!?!?!?!? I won't tell you what's wrong with your cursor - because instead of fixing the cursor, you should learn to avoid it in the first place!
Seriously - avoid RBAR (row-by-agonizing-row) processing whenever you can, and here, it's really utterly pointless to use a cursor - just use this nice and clean set-based statement:
ALTER PROCEDURE [dbo].[conn] #ResellerID INT,
#GWResellerID INT,
#UserName VARCHAR(50),
#Password VARCHAR(50),
#URL VARCHAR(100),
#ServiceType INT,
#ServiceDesc VARCHAR(50),
#FeedFrom BIT,
#PublicKey VARCHAR(max)
AS
INSERT INTO dbo.tblGatewayConnection
(ResellerID, GWResellerID, UserName, Password,
URL, ServiceType, ServiceDesc, feedFromMain,
publicKey)
SELECT
ResellerID, GWResellerID, UserName, Password,
URL, ServiceType, ServiceDesc, feedFromMain,
publicKey
FROM
dbo.reseller_profiles
WHERE
main_reseller_ID = #ResellerID
and you're done!! No messy cursor, no unnecessary local variables - just a simple INSERT ... SELECT and you've achieved what you want!
I am not sure the error message could be any more self explanatory:
Cursorfetch: The number of variables declared in the INTO list must match that of selected columns.
You are selecting all the columns from reseller_profiles
declare gateway cursor for
select *
from reseller_profiles
where main_reseller_ID = #ResellerID
And trying to put them into a single variable:
FETCH NEXT FROM gateway INTO #ResellerID
The number of columns you select in your cursor must match the number of variables you are inserting to, so you would need something like
declare gateway cursor for
select reseller_id
from reseller_profiles
where main_reseller_ID = #ResellerID
HOWEVER you should not be using a cursor for this, you can use the same thing using INSERT .. SELECT:
INSERT INTO [dbo].tblGatewayConnection
( [ResellerID],[GWResellerID], [UserName], [Password], [URL],
[ServiceType], [ServiceDesc],[feedFromMain], publicKey
)
SELECT Resellerid, #GWResellerID, #UserName, #Password,
#URL, #ServiceType, #ServiceDesc, #FeedFrom, #PublicKey
FROM reseller_profiles
WHERE main_reseller_ID = #ResellerID;
As has been said, you should avoid cursors at all costs, if you absolutely have to use a cursor, declare the most light weight cursor you can. In your case for example, you were only moving forwards within the cursor, only reading data, not modifying it, and only accessing the cursor locally, therefore you would declare the cursor as follows:
DECLARE gateway CURSOR LOCAL STATIC FAST_FORWARD
FOR
SELECT ...
FROM ..
Although cursors perform terribly at the best of times they are given an even worse reputation by lazy declarations.
Finally, You should get out of the habit of using SELECT *

Inserting a hashbyte value into a table via stored procedure

I have created a table with username,password,hashvalue as columns.
create table userDetails
(
username varchar(50),
password varchar (50),
hashvalue varchar (3000)
)
I want to have each password stored in the table to be hashed by executing a stored procedure and in the same stored procedure, the hashed value is inserted into the table.
I created the stored procedure as follows :
CREATE PROCEDURE hashpwd
#username varchar(50) = default ,
#pwd varchar(50) = default
AS
BEGIN
DECLARE #hashed varchar(4000);
SET #hashed = HASHBYTES('SHA1', #pwd); --edited to #pwd
select username,password from userDetails
WHERE username LIKE #username AND password LIKE #pwd;
END
BEGIN
insert into userDetails
(hashvalue) values (#hashed);
END
The procedure can be executed, but the result is wrong. It produces NULL and a random string of characters. A 3rd row was also created when there are only 2 rows in the table initially.
Any guidance is appreciated.
Create PROCEDURE hashpwd
#username varchar(50) = default ,
#pwd varchar(50) = default
AS
BEGIN
DECLARE #hashed varchar(4000);
SET #hashed = HASHBYTES('SHA1', #pwd); --(#pwd instead of #hashed)
Update userDetails set hashvalue = #hashed
WHERE username LIKE #username AND password LIKE #pwd
END
You should use Update statement instead of Insert and statement should appear with in the procedure body.
I keep getting an error when submitting my response, but for the previous response above, replace the where clause with this
WHERE username = #username
The reason you are seeing the wrong results is due to the data type for your hashvalue column. As the HASHBYTES function returns VARBINARY, you should use this as your data type.
create table userDetails
(
username varchar(50),
password varchar (50),
hashvalue varbinary(40) -- Note, this should only be 40 characters long
)
I'm not really sure what youre stored procedure is meant to do. I would imagine that for a given username & password, you want to update the hashvalue on the corresponding row? If so, I would do this.
CREATE PROCEDURE hashpwd
#username varchar(50) = default ,
#pwd varchar(50) = default
AS
BEGIN
UPDATE userDetails
SET hashvalue = HASHBYTES('SHA1', #pwd); --edited to #pwd
WHERE username LIKE #username AND password LIKE #pwd;
END

Store such characters in SQL Server 2008 R2

I'm storing encrypted passwords in the database, It worked perfect so far on MachineA. Now that I moved to MachineB it seems like the results gets corrupted in the table.
For example: ù9qÆæ\2 Ý-³Å¼]ó will change to ?9q??\2 ?-³?¼]? in the table.
That's the query I use:
ALTER PROC [Employees].[pRegister](#UserName NVARCHAR(50),#Password VARCHAR(150))
AS
BEGIN
DECLARE #Id UNIQUEIDENTIFIER
SET #Id = NEWID()
SET #password = HashBytes('MD5', #password + CONVERT(VARCHAR(50),#Id))
SELECT #Password
INSERT INTO Employees.Registry (Id,[Name],[Password]) VALUES (#Id, #UserName,#Password)
END
Collation: SQL_Latin1_General_CP1_CI_AS
ProductVersion: 10.50.1600.1
Thanks
You are mixing 2 datatypes:
password need to be nvarchar to support non-Western European characters
literals need N prefix
Demo:
DECLARE #pwdgood nvarchar(150), #pwdbad varchar(150)
SET #pwdgood = N'ù9qÆæ\2 Ý-³Å¼]ó'
SET #pwdbad = N'?9q??\2 ?-³?¼]?'
SELECT #pwdgood, #pwdbad
HashBytes gives varbinary(8000) so you need this in the table
Note: I'd also consider salting the stored password with something other than ID column for that row
If you want to store such characters, you need to:
use NVARCHAR as the datatype for your columns and parameters (#Password isn't NVARCHAR and the CAST you're using to assign the password in the database table isn't using NVARCHAR either, in your sample ...)
use the N'....' syntax for indicating Unicode string literals
With those two in place, you should absolutely be able to store and retrieve any valid Unicode character

How to convert varbinary to GUID in TSQL stored procedure?

how can I convert the HASHBYTES return value to a GUID?
This is what I have so far.
CREATE PROCEDURE [dbo].[Login]
#email nvarchar,
#password varchar
AS
BEGIN
DECLARE #passHashBinary varbinary;
DECLARE #newPassHashBinary varbinary;
-- Create a unicode (utf-16) password
Declare #unicodePassword nvarchar;
Set #unicodePassword = CAST(#password as nvarchar);
SET #passHashBinary = HASHBYTES('md5', #password);
SET #newPassHashBinary = HASHBYTES('md5', #unicodePassword);
Simply cast it:
select cast(hashbytes('md5','foo') as uniqueidentifier)
But there are two questions lingering:
why cast HASHBYTES to guid? Why not use the appropriate type for storage, namely BINARY(16)
I hope you are aware that MD5 hashing passwords is basically useless, right? Because of rainbow tables. You need to use a secure hashing scheme, like an HMAC or the HA1 of Digest.

Resources