Is this sql vulnerability in my code? - asp.net

Is this sql vulnerability in my code?Because I have Parameterized the SQL ,so no sql Injection? anyone give some suggests will appreciate!If yes, How to repair?
ASP.NET CODE:
public DataTable CompanySearchUser(int pageSize, int currentPage, string whereCondition)
{
DbParameter[] parms = {
DbHelper.MakeInParam("#PageSize",(DbType)SqlDbType.Int,4,pageSize),
DbHelper.MakeInParam("#PageNumber",(DbType)SqlDbType.Int,4,currentPage),
DbHelper.MakeInParam("#where",(DbType)SqlDbType.NVarChar,500,whereCondition)
};
DataTable userlist = DbHelper.ExecuteDataset(CommandType.StoredProcedure, "spCompanySearchUser", parms).Tables[0];
return userlist;
}
SQL CODE:
ALTER PROC [dbo].[spCompanySearchUser]
#PageSize INT
#PageNumber INT,
#where nvarchar(550)--like 'and a=1 '
AS
DECLARE #RowStart INT
DECLARE #RowEnd INT
DECLARE #SQL NVARCHAR(4000)
IF #PageNumber > 0
BEGIN
SET #PageNumber = #PageNumber - 1
SET #RowStart = #PageSize * #PageNumber + 1;
SET #RowEnd = #RowStart + #PageSize - 1;
SET #SQL='
WITH AllUsers
AS (SELECT
UB.UserBaicInfoID,
UB.UserName,
UB.HighestEducation,
UB.Age,
UB.Sex,
UB.WorkExperience,
UB.PositionDesired,
UB.UpdateTime,
Row_number() OVER (ORDER BY UB.UpdateTime DESC) AS RowNumber
From UserBasicInfo UB
WHERE ResumeState=1 '+#where+')
SELECT * FROM AllUsers WHERE RowNumber >=' + Str(#RowStart) + ' AND RowNumber <= ' + Str(#RowEnd) + ''
EXEC sp_executesql #SQL
END
Is this sql vulnerability in my code?Because I have Parameterized the SQL ,so no sql Injection? anyone give some suggests will appreciate!If yes, How to repair?

cannot test it right now but i would say yes it is.
you pass in #where and you do a concatenation, your #where is doing actual sql code not a real "where" clause
then you do a exec on the final sql code
this can be exploited
not sure what your whereCondition look like but try this
public DataTable CompanySearchUser(int pageSize, int currentPage, string whereCondition)
{
/**** add this to test ****/
whereCondition += " or 1=1";
/**** add this to test ****/
DbParameter[] parms = {
DbHelper.MakeInParam("#PageSize",(DbType)SqlDbType.Int,4,pageSize),
DbHelper.MakeInParam("#PageNumber",(DbType)SqlDbType.Int,4,currentPage),
DbHelper.MakeInParam("#where",(DbType)SqlDbType.NVarChar,500,whereCondition)
};
DataTable userlist = DbHelper.ExecuteDataset(CommandType.StoredProcedure, "spCompanySearchUser", parms).Tables[0];
return userlist;
}

It's still insecure and non-performant.
You should just do the SQL dynamically and add the parameters there rather than relying on sp_executeSQL.
Building the string with the # placeholder items cmd.Parameters.AddWithValue("#placeholder", value) values, then adding those parameters, will generally be better, more secure, more manageable, and won't waste SQL's time since it won't be able to optimize your statements.
You may actually get better performance doing it dynamically in code rather that in a stored proc since SQL will cache every one of those statements and re-execute them if an identical statement comes along.

Related

Inconsistent results of stored procedure execution - ASP.net code vs Direct SP call

Thanks in advance for anyone's help. This is mystery which is driving me crazyyyy :(.
IF I run this following stored procedure directly on SQL server 2008R2, it returns the desired rows. But if I call this via ASP.net(3.5) it returns empty data from the last Select statement in SP.
Is there any scoping involved in this regarding the temp table #_CalendarDate?
Stored Procedure:
USE[DB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [WC].[spsGetDayCyclePeriod]
(
#Param_StartDate datetime,
#NumberOfDayRange int,
#Campus_Type varchar(2)
)
AS
DECLARE #DateRangeStart datetime
DECLARE #DateRangeEnd datetime
DECLARE #_CalendarDate TABLE (CollegeDate datetime)
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET DATEFIRST 1
SELECT #DateRangeStart=max(CalendarDate) FROM [X].dbo.CalendarEvents
WHERE CalendarDate <= #Param_StartDate and left(CalendarType,1)= #Campus_Type
and (CalendarType <> #Campus_Type+'_H' and CalendarType<>'H'
and convert(INT, right(CalendarType, len(CalendarType)-3))>0)
SELECT #DateRangeEnd=min(CalendarDate) FROM [X].dbo.CalendarEvents
WHERE CalendarDate >= dateadd(day, #NumberOfDayRange-1, #Param_StartDate)
and left(CalendarType,1)= #Campus_Type and (CalendarType <> #Campus_Type+'_H'
and CalendarType<>'H' and convert(INT, right(CalendarType, len(CalendarType)-3))=0)
--Get all Dates within range
;WITH CollegeDate AS
(
SELECT #DateRangeStart AS DateValue
union all
SELECT dateadd(day, 1, DateValue)
FROM CollegeDate
WHERE dateadd(day, 1, DateValue) <= #DateRangeEnd
)
INSERT INTO #_CalendarDate (CollegeDate)
SELECT DateValue FROM CollegeDate OPTION (MAXRECURSION 0)
SELECT * from #_CalendarDate
END
ASP.Net code:
DataTable dayCycle = new DataTable();
var dateTimestr = startDate.ToString("yyyy-MM-dd HH:mm:ss");
using (SqlCommand sqlCommand = new SqlCommand("WC.spsGetDayCyclePeriod", new SqlConnection(Connection)))
{
sqlCommand.CommandType = CommandType.StoredProcedure;
var range = endDate.Subtract(startDate).Days;
sqlCommand.Parameters.Add(new SqlParameter("#Param_StartDate", dateTimestr));
sqlCommand.Parameters.Add(new SqlParameter("#NumberOfDayRange", range));
sqlCommand.Parameters.Add(new SqlParameter("#Campus_Type", campus));
//dayCycle = SqlHelper.GetDataTableUsingSqlCommand(sqlCommand);
try
{
SqlDataAdapter _dap = new SqlDataAdapter(sqlCommand);
_dap.Fill(dayCycle);
}
catch (Exception ex)
{ throw new Exception(ex.ToString()); }
return dayCycle;
Pass #Param_StartDate as a date time object rather than a string.
Thanks everyone for help.
Solved the problem myself ! Hopefully it will help someone else as well in future. Here is the answer:
In above ASP.net code:
sqlCommand.Parameters.Add(new SqlParameter("#Campus_Type", campus));
campus is a enum type and when I was calling the above method with enum type I was actually passing the int value instead of string. So this is what I change to. This was confusing because when I was debugging my code I was using cursor on top of the #campus which basically calls toString so I was seeing the right value (which was wrong) the actual value was passed was number of enum.
sqlCommand.Parameters.Add(new SqlParameter("#Campus_Type", campus.ToString()));
And the above change solved the problem.
What I learned from this is Always...Always confirm that the arguments you intend to pass to SP is what SP is receiving so retrieve back your passed arguments by running the following before you do anything with Stored procedures...
Select #Your_Param1, #Your_Param2
And then check on code side that you are receiving what you are expecting.

SQL stored procedure sent data for "IN" condition

I have a stored procedure and it has where condition using 'IN' key word. So I want to send more than one value to stored procedure .
This is my stored procedure:
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
SELECT
UserName, UserId
FROM
company
WHERE
(COM_KEY IN (#COM_KEY))
END
So I pass the value in here
string companyID = "";
for (int i = 0; i < lbCompanies.Items.Count; i++)
{
if (i == 0)
{
companyID += Convert.ToInt32(lbCompanies.Items[i].Value);
}
else
{
companyID += "," + Convert.ToInt32(lbCompanies.Items[i].Value);
}
}
DataSet ApproveList = DataRepository.TUsersProvider.xxx(companyID);
but there is an error
Conversion failed when converting the varchar value '3087,4058' to data type int
How can I solve it?
You cannot pass a comma-delimited string into #COM_KEY. You should pass an array.
One way to accomplish this task would be using Table-Valued Parameters.
Have a look at this article, under Solution #3: TSQL: Passing array/list/set to stored procedure (MS SQL Server).
In essence, you treat your series of ints as a table you apply JOIN upon, and not query it via WHERE ... IN () phrase.
You can create a UserDefinedFunction which parses the string and puts each integer extracted into a table to solve this issue
CREATE Function fnSplitter (#IDs Varchar(100) )
Returns #Tbl_IDs Table (ID Int) As
Begin
-- Append comma
Set #IDs = #IDs + ','
-- Indexes to keep the position of searching
Declare #Pos1 Int
Declare #pos2 Int
-- Start from first character
Set #Pos1=1
Set #Pos2=1
While #Pos1<Len(#IDs)
Begin
Set #Pos1 = CharIndex(',',#IDs,#Pos1)
Insert #Tbl_IDs Select Cast(Substring(#IDs,#Pos2,#Pos1-#Pos2) As Int)
-- Go to next non comma character
Set #Pos2=#Pos1+1
-- Search from the next charcater
Set #Pos1 = #Pos1+1
End
Return
End
Now alter your stored procedure like this
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
SELECT UserName, UserId
From company
WHERE COM_KEY IN (Select ID From dbo.fnSplitter(#COM_KEY))
END
Check this link for detailed Implemetation
The most trivial solution to your problem is to vale SQL string(for dynamic Query) and then execute the statement
ALTER PROCEDURE [dbo].[xxx]
#COM_KEY varchar(max) = NULL
AS
BEGIN
DECLARE #SQL nvarchar(MAX)
SET #SQL=N' SELECT
UserName, UserId
FROM
company
WHERE
(COM_KEY IN ('+#COM_KEY+'))'
EXCE #SQL
END
Solution mentioned by #ssilas777 is the sophisticated version for the same.
Also note that using IN is not always advisable. I have run into
query time out errors
when IN has lot of IDs
Change your code from:
if (i == 0)
{
companyID += Convert.ToInt32(lbCompanies.Items[i].Value);
}
else
{
companyID += "," + Convert.ToInt32(lbCompanies.Items[i].Value);
}
to:
if (i == 0)
{
companyID +="'" + Convert.ToInt32(lbCompanies.Items[i].Value) + "'";
}
else
{
companyID += ",'" + Convert.ToInt32(lbCompanies.Items[i].Value) + "'";
}

Using a stored procedure to (+1 or -1) a value from a table

I have a button event that has to disable itself after a certain number of users is reached.
We are storing the currently accessed user count in the db, and I am trying to create a stored procedure, that when called, will automatically increment the user count on that table.
I am going to create a separate procedure to subtract.
In my mind, I would need to get the current value, add one, then save it in the table, bringing me to this code:
CREATE PROCEDURE procDrRefBooksAddUser
-- Add the parameters for the stored procedure here
#fileId int = NULL,
#count int = 0,
#newCount int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT UserCount FROM tblDrRefBooks WHERE DrRefBookFileID = #fileId
SET #count = UserCount
SET #newCount = #count + 1
-- Insert statements for procedure here
UPDATE tblDrRefBooks SET tblDrRefBooks.UserCount = #newCount WHERE DrRefBookFileID = #fileId
END
Unfortunately, I am unable to get it right, coming up with errors such as:
Msg 207, Level 16, State 1, Procedure procDrRefBooksAddUser, Line 17
Invalid column name 'UserCount'
Could someone please tell me where I'm going wrong.
Edit: Update on answer (finally)
My SQL is:
ALTER PROCEDURE [dbo].[procDrRefBooksAddUser]
-- Add the parameters for the stored procedure here
#fileId int = NULL,
#newCount int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
-- SET NOCOUNT ON;
UPDATE tblDrRefBooks
SET #newCount = UserCount = UserCount + 1
WHERE DrRefBookFileID = #fileId
SELECT #newCount AS iden
END
And code behind is:
using (SqlConnection db = DataConn.SqlConnection())
{
db.Open();
SqlTransaction transaction = db.BeginTransaction();
try
{
lbltest.Text = "Starting...";
using (SqlCommand acommand =
new SqlCommand(
"EXEC procDrRefBooksAddUser #fileID, #count",
db, transaction) { CommandType = CommandType.Text })
{
acommand.Parameters.Add(new SqlParameter("#fileID", SqlDbType.Int)).Value = Int32.Parse(location.Value);
acommand.Parameters.Add(new SqlParameter("#count", SqlDbType.Int) { Direction = ParameterDirection.Output });
using (SqlDataReader rdr = acommand.ExecuteReader())
{
btnLoad.Text = "Here: " + rdr["iden"];
}
}
transaction.Commit();
}
catch (Exception ea)
{
transaction.Rollback();
lbltest.Text = ("Insertion Error: " + ea.Message);
}
}
But I get this error: Insertion Error: Invalid attempt to read when no data is present.
SET #count = UserCount
is incorrect. You can't access columns from previous SELECT statements like that.
You can just do it all in one atomic statement. This increments the column and sets the output parameter with the new value of UserCount.
UPDATE tblDrRefBooks
SET #newCount = UserCount = UserCount + 1
WHERE DrRefBookFileID = #fileId
This avoids race conditions from reading and writing the value in separate operations.
Use below code in your procedure :
SELECT #count=UserCount FROM tblDrRefBooks WHERE DrRefBookFileID = #fileId
For reference case, it should be like this
SELECT #Count = UserCount FROM tblDrRefBooks WHERE DrRefBookFileID = #fileId
#newCount = #Count + 1
But you can just avoid that and do this instead
UPDATE tblDrRefBooks SET UserCount = UserCount + 1
WHERE
DrRefBookFileID = #fileId AND
UserCount < #MaxUserCount
Update
Notice I added AND UserCount < #MaxUserCount. Your stored procedure might not capture all the count as page refreshes quickly [race condition]. You may have an old #Count value.
Make sure the count is only increase if MAX is not reached
Just get the count at any point in time with another PROC. Don't do it in the same PROC as for updating count as you can get OLD count value
Can have a flag on your table that is SET in the PROC once MAX is reached
no need to use temporary variables and use of select query.
try this..
UPDATE tblDrRefBooks
SET tblDrRefBooks.UserCount = UserCount +1
WHERE DrRefBookFileID = #fileId

Generate ASP.Net Membership password hash in pure T-SQL

I'm attempting to create a pure t-sql representation of the default SHA-1 password hashing in the ASP.Net Membership system. Ideally, what I would get would be this:
UserName Password GeneratedPassword
cbehrens 34098kw4D+FKJ== 34098kw4D+FKJ==
Note: that's bogus base-64 text there. I've got base64_encode and decode functions that round-trip correctly. Here's my attempt, which doesn't work:
SELECT UserName, Password, dbo.base64_encode(HASHBYTES('SHA1', dbo.base64_decode(PasswordSalt) + 'test')) As TestPassword FROM aspnet_Users U JOIN aspnet_membership M ON U.UserID = M.UserID
I've tried a number of variations on the theme, to no avail. I need to do this in pure T-Sql; involving a console app or something like that will double the work.
So if anyone can supply what precisely the syntax should be to duplicate that password from the ASP.Net membership stuff, I would greatly appreciate it.
I wrote a hashing stored proc by reverse enginering the C# code from here ASP.NET Identity default Password Hasher, how does it work and is it secure? and some fantastic PBKDF2 SQL functions from here Is there a SQL implementation of PBKDF2?
First create these two functions taken from Is there a SQL implementation of PBKDF2?
create FUNCTION [dbo].[fn_HMAC]
(
#hash_algorithm varchar(25),
#key VARCHAR(MAX),
#message VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
--HASH key if longer than 16 characters
IF(LEN(#key) >64)
SET #key = HASHBYTES(#hash_algorithm,#key)
DECLARE #i_key_pad VARCHAR(MAX), #o_key_pad VARCHAR(MAX), #position INT
SET #position = 1
SET #i_key_pad = ''
SET #o_key_pad = ''
--splice ipad & opod with key
WHILE #position <= LEN(#key)
BEGIN
SET #i_key_pad = #i_key_pad + CHAR(ASCII(SUBSTRING(#key, #position, 1)) ^ 54)
SET #o_key_pad = #o_key_pad + CHAR(ASCII(SUBSTRING(#key, #position, 1)) ^ 92)
SET #position = #position + 1
END
--pad i_key_pad & o_key_pad
SET #i_key_pad = LEFT(#i_key_pad + REPLICATE('6',64),64)
SET #o_key_pad = LEFT(#o_key_pad + REPLICATE('\',64),64)
RETURN HASHBYTES(#hash_algorithm,CONVERT(VARBINARY(MAX),#o_key_pad) + HASHBYTES(#hash_algorithm,#i_key_pad + #message))
END
GO
and
CREATE function [dbo].[fn_PBKDF2]
(
#hash_algorithm varchar(25),
#password varchar(max),
#salt varchar(max),
#rounds int,
#outputbytes int
)
returns varchar(max)
as
begin
declare #hlen int
select #hlen = len(HASHBYTES(#hash_algorithm, 'test'))
declare #l int
SET #l = (#outputbytes +#hLen -1)/#hLen
declare #r int
SET #r = #outputbytes - (#l - 1) * #hLen
declare #t varchar(max), #u varchar(max), #block1 varchar(max)
declare #output varchar(max)
SET #output = ''
declare #i int
SET #i = 1
while #i <= #l
begin
set #block1 = #salt +cast(cast(#i as varbinary(4)) as varchar(4))
set #u = dbo.fn_HMAC(#hash_algorithm,#password,#block1)
set #t = #u
declare #j int
SET #j = 1
while #j < #rounds
begin
set #u = dbo.fn_HMAC(#hash_algorithm,#password,#u)
declare #k int
SET #k = 0
DECLARE #workstring varchar(max)
SET #workstring = ''
while #k < #hLen
begin
set #workstring = #workstring + char(ascii(substring(#u,#k+1,1))^ascii(substring(#t,#k+1,1)))
set #k = #k + 1
end
set #t = #workstring
set #j = #j + 1
end
select #output = #output + case when #i = #l then left(#t,#r) else #t end
set #i = #i + 1
end
return master.dbo.fn_varbintohexstr(convert(varbinary(max), #output ))
end
GO
then create the stored proc to generate the hash password
CREATE PROCEDURE [dbo].[EncryptPassword2]
#passwordIn AS VARCHAR(MAX),
#passwordOut VARCHAR(max) OUTPUT
AS
-- Generate 16 byte salt
DECLARE #saltVarBin VARBINARY(max)
SET #saltVarBin = (SELECT CAST(newid() AS binary(16)))
-- Base64 encode the salt
DECLARE #saltOut VARCHAR(max)
SET #saltOut = cast('' as xml).value('xs:base64Binary(sql:variable("#saltVarBin"))', 'varchar(max)')
-- Decode salt to pass to function fn_PBKDF2
DECLARE #decodedsalt varchar(max)
SET #decodedsalt = convert(varchar(max),(SELECT CAST('' as xml).value('xs:base64Binary(sql:variable("#saltOut"))', 'varbinary(max)')))
-- Build the password binary string from 00 + salt binary string + password binary string created by 32 byte 1000 iteration ORC_PBKDF2 hashing
DECLARE #passwordVarBinStr VARCHAR(max)
-- Identity V1.0 and V2.0 Format: { 0x00, salt, subkey }
SET #passwordVarBinStr = '0x00' + REPLACE(master.dbo.fn_varbintohexstr(#saltVarBin) + (SELECT dbo.fn_PBKDF2('sha1', #passwordIn, #decodedsalt, 1000, 32)),'0x','')
-- Identity V3.0 Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey } (comment out above line and uncomment below line)
--SET #passwordVarBinStr = '0x01000000010000271000000010' + REPLACE(master.dbo.fn_varbintohexstr(#saltVarBin) + (SELECT dbo.fn_PBKDF2('SHA2_256', #passwordIn, #decodedsalt,10000, 32)),'0x','')
-- Convert the password binary string to base 64
DECLARE #passwordVarBin VARBINARY(max)
SET #passwordVarBin = (select cast('' as xml).value('xs:hexBinary( substring(sql:variable("#passwordVarBinStr"), sql:column("t.pos")) )', 'varbinary(max)') from (select case substring(#passwordVarBinStr, 1, 2) when '0x' then 3 else 0 end) as t(pos))
SET #passwordOut = cast(''as xml).value('xs:base64Binary(sql:variable("#passwordVarBin"))', 'varchar(max)')
RETURN
Finally execute the stored proc using
DECLARE #NewPassword varchar(100)
DECLARE #EncryptPassword VARCHAR(max)
select #NewPassword = 'password12344'
EXECUTE EncryptPassword2 #NewPassword, #PasswordOut = #EncryptPassword OUTPUT;
PRINT #EncryptPassword
Please note that the stored proc may need to be changed for later versions of SQL server as this was written specifically for 2005 and I belive conversion to base64 is different in later versions.
if you are running 2005 or higher, you can create a CLR (.NET) UDF:
[SqlFunction(
IsDeterministic = true, IsPrecise = true,
DataAccess = DataAccessKind.None,
SystemDataAccess = SystemDataAccessKind.None
)]
public static string EncodePassword(string pass, string salt) {
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(salt);
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider()) {
return Convert.ToBase64String(sha1.ComputeHash(dst));
}
}
you need to include the following namespaces in your class:
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
the class must be public.
build the .dll then run the following (per database you want to call the UDF) SQL statement:
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
IF OBJECT_ID (N'dbo.EncodePassword', N'FS') IS NOT NULL
DROP FUNCTION dbo.EncodePassword;
IF EXISTS (SELECT name FROM sys.assemblies WHERE name='UDF')
DROP ASSEMBLY UDF
CREATE ASSEMBLY UDF FROM 'FULL_PATH_TO.dll' WITH PERMISSION_SET=SAFE
GO
CREATE FUNCTION EncodePassword(
#pass NVARCHAR(4000),
#salt NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
-- return NULL if any input parameter(s) are NULL
WITH RETURNS NULL ON NULL INPUT
AS
EXTERNAL NAME UDF.[NAMESPACE.CLASSNAME].EncodePassword
GO
obviously, replace 'NAMESPACE.CLASSNAME' with the namespace (if any) and name of your class. and you might want to mess with the input parameter and return value sizes.
then call the UDF with T-SQL:
SELECT UserName,Password
,dbo.EncodePassword('PASSWORD', PasswordSalt) As TestPassword
FROM aspnet_Users U
JOIN aspnet_membership M ON U.UserID = M.UserID
works for me :)
Instead of using CLR you can create this function in SQL. On this page you will find very nice example:
http://svakodnevnica.com.ba/index.php?option=com_kunena&func=view&catid=4&id=4&Itemid=5&lang=en#7
P.S. byte[] src = Convert.FromBase64String(salt); is correct way...
Fox
OP requested "pure" sql - I think using CLR is cheating ;) I was stubborn and had to figure it out for myself so here's what I did.
NOTE: Make a backup first!!
Select * into dbo.aspnet_Membership_BACKUP from [dbo].[aspnet_Membership]
Function to calculate the hashes:
/*
Create compatible hashes for the older style ASP.Net Membership
Credit for Base64 encode/decode: http://stackoverflow.com/questions/5082345/base64-encoding-in-sql-server-2005-t-sql
*/
Create Function dbo.AspNetHashCreate (#clearPass nvarchar(64), #encodedSalt nvarchar(64))
Returns nvarchar(128)
as
begin
declare #binSalt varbinary(128)
declare #binPass varbinary(128)
declare #result nvarchar(64)
Select #binPass = CONVERT(VARBINARY(128), #clearPass)
-- Passed salt is Base64 so decode to bin, then we'll combine/append it with password
Select #binSalt = CAST(N'' as XML).value('xs:base64Binary(sql:column("bin"))','VARBINARY(128)')
from (Select #encodedSalt as bin) as temp;
-- Hash the salt + pass, then convert to Base64 for the output
Select #result = CAST(N'' as XML).value('xs:base64Binary(xs:hexBinary(sql:column("bin")))', 'NVARCHAR(64)')
from (Select HASHBYTES('SHA1', #binSalt + #binPass) as bin) as temp2;
-- Debug, check sizes
--Select DATALENGTH(#binSalt), DATALENGTH(#binPass), DATALENGTH(#binSalt + #binPass)
return #result
end
I was changing a Membership database from "clear" passwords to the more secure hashed format - call it like this:
Update [dbo].[aspnet_Membership] set PasswordFormat = 1, Password = dbo.AspNetHashCreate(password, PasswordSalt) where PasswordFormat = 0
Even with my database originally set to "clear" passwords, the salt values were created with each record, however, if for some reason you don't have salt values you can create them with this:
/*
Create compatible salts for the older style ASP.Net Membership (just a 16 byte random number in Base64)
Note: Can't use newId() inside function so just call it like so: dbo.AspNetSaltCreate(newId())
Credit for Base64 encode: http://stackoverflow.com/questions/5082345/base64-encoding-in-sql-server-2005-t-sql
*/
Create Function dbo.AspNetSaltCreate (#RndId uniqueidentifier)
Returns nvarchar(24)
as
begin
return
(Select CAST(N'' as XML).value('xs:base64Binary(xs:hexBinary(sql:column("bin")))', 'NVARCHAR(64)')
from (select cast(#RndId as varbinary(16)) as bin) as temp)
end
Then use it like this:
Update [dbo].[aspnet_Membership] set PasswordSalt = dbo.AspNetSaltCreate(newId()) where PasswordSalt = ''
Enjoy!
According to this SO post, this is the process they use to encode/hash your password/salt.
public string EncodePassword(string pass, string salt)
{
byte[] bytes = Encoding.Unicode.GetBytes(pass); //HERE
byte[] src = Encoding.Unicode.GetBytes(salt); //and HERE
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
byte[] inArray = algorithm.ComputeHash(dst); //then they has the bytes not the string...
return Convert.ToBase64String(inArray);
}
I could be wrong but it looks like you are missing the step where you get the bytes for the password and salt. Can you try adding that and see if it works?

SQL use comma-separated values with IN clause

I am developing an ASP.NET application and passing a string value like "1,2,3,4" into a procedure to select those values which are IN (1,2,3,4) but its saying "Conversion failed when converting the varchar value '1,2,3,4' to data type int."
Here is the aspx code:
private void fillRoles()
{
/*Read in User Profile Data from database */
Database db = DatabaseFactory.CreateDatabase();
DbCommand cmd = db.GetStoredProcCommand("sp_getUserRoles");
db.AddInParameter(cmd, "#pGroupIDs", System.Data.DbType.String);
db.SetParameterValue(cmd, "#pGroupIDs", "1,2,3,4");
IDataReader reader = db.ExecuteReader(cmd);
DropDownListRole.DataTextField = "Group";
DropDownListRole.DataValueField = "ID";
while (reader.Read())
{
DropDownListRole.Items.Add((new ListItem(reader[1].ToString(), reader[0].ToString())));
}
reader.Close();
}
Here is my procedure:
CREATE Procedure [dbo].[sp_getUserRoles](#pGroupIDs varchar(50))
AS BEGIN
SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)
END
Here is a workaround I found to do what you are trying to achieve
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where (',' + #pGroupIDs +',' LIKE '%,' + CONVERT(VARCHAR, id) + ',%')
End
This gets your comma delimited list and compares it to the id's(which are represented like so ',1,', ',2,' etc) in the table using LIKE
If you dont want to use dynamic sql, the best way ive found is to create a function which turns a delimited string into a table, something like this works for an Integer list:
CREATE FUNCTION [dbo].[StringToIntList]
(#str VARCHAR (MAX), #delimeter CHAR (1))
RETURNS
#result TABLE (
[ID] INT NULL)
AS
BEGIN
DECLARE #x XML
SET #x = '<t>' + REPLACE(#str, #delimeter, '</t><t>') + '</t>'
INSERT INTO #result
SELECT DISTINCT x.i.value('.', 'int') AS token
FROM #x.nodes('//t') x(i)
ORDER BY 1
RETURN
END
Then use that in your sp:
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
SELECT * FROM CheckList_Groups Where id in (
SELECT ID FROM dbo.StringToIntList(#pGroupIds,',')
)
End
Sure it can't do that,
The generated query would be sth like this
SELECT * FROM CheckList_Groups Where id in ('1,2,3,4')
and sure it can't be executed.
you can build the query in your stored procedure then execute it with exec
'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')'
or
SELECT * FROM CheckList_Groups Where charindex(','+id+',' , #pGroupIDs)>0
but you first must add the ',' to start and end of your parameter in your c# code
It is not possible to put those values (the comma separated string) in a parameter-value.
What you'll have to do, is to create the SQL Statement in your stored procedure dynamically, by string concatenation. You'll have to execute it with the sp_executesql stored procedure then.
CREATE PROCEDURE [dbo].[getUserRoles]( #groupIds NVARCHAR(50) )
AS BEGIN
DECLARE #statement NVARCHAR(255)
SELECT #statement = N'SELECT * FROM CheckList_Groups Where id in ( ' + #pGroupIDs + N')'
execute sp_executesql #statement
END
Also, not that I named the SP getUserRoles instead of sp_getUserRoles.
The reason is very simple: when you execute a stored procedure whose name starts with sp_, then SQL Server will first query the master database to find that stored procedure, which causes a performance hit offcourse.
The way you are trying to do this is slightly wrong. You will need to use EXECUTE in order to achieve this.
CREATE PROCEDURE [dbo].[sp_getUserRoles](#pGroupIDs nvarchar(50))
As
BEGIN
EXECUTE (N'SELECT * FROM CheckList_Groups Where id in (' + #pGroupIDs + ')';
END
DECLARE #TagId NVARCHAR(100) = '43,81'
SELECT * FROM TABLE WHERE TagId IN (SELECT TRIM(VALUE) FROM STRING_SPLIT( #TagId , ',' ) )
USE STRING_SPLIT FUNCTION FOR THIS
You need to use SP_executesql to achieve this functionllity
CREATE Procedure [dbo].[sp_getUserRoles](
#pGroupIDs varchar(50)
)
As
BEGIN
EXECUTE sp_executesql
N'SELECT * FROM CheckList_Groups Where id in (#pGroupIDs)',
N'#level varchar(50)',
#level = #pGroupIDs;
End
The IN clause can't take a bound parameter like that. What it's being given when the query is actually created is SELECT * FROM CheckList_Groups Where id in ('1,2,3,4'). Essentially the IN clause is being passed a single string.
First create function -
Just run this code
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CSVToTable] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE
(id int not null)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR ) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%',#INSTR)
SELECT #VALUE = LEFT(#INSTR , #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab(id) VALUES (#VALUE)
END
RETURN
END
GO
Then -
Use function in bracket with select statment -
DECLARE #LIST VARCHAR(200)
SET #LIST = '1,3'
SELECT Id, Descr FROM CSVDemo WHERE Id IN (SELECT * FROM dbo.CSVToTable(#LIST))

Resources