Dynamic query as a parameter in stored procedure and SQL Injection - asp.net

I have the following stored procedure (modified for this post):
ALTER PROCEDURE [dbo].[SEL_ARTICLE](
#FILTER VARCHAR(8000) = ''
) AS
BEGIN
DECLARE #SQLQuery varchar(8000);
SET #SQLQuery = 'SELECT DISTINCT ArticleNo, desc, ...
FROM Article INNER JOIN
...
{0}
ORDER BY ArticleNo, ...';
SET #SQLQuery = REPLACE(#SQLQuery, '{0}', #FILTER);
EXEC(#SQLQuery);
END
I call this proc in my prog like this:
Public Function get_Article(ByVal strFilter As String) As DataTable
Dim tblData As New DataTable
Dim par As SqlParameter = New SqlParameter("#FILTER", SqlDbType.VarChar)
par.Value = strFilter
Load_Data("SEL_ARTICLE", tblData, par) 'SqlCommand used
Return tblData
End Function
The value of the parameter strFilter is dynamic (Fields, values) depending on what we search. Here is an example:
" AND ((Article = [input from User]) OR (Description LIKE '%[input from User]%'))"
The user must be able to enter the character ' (word in French) it is why I replace it server-side like this:
strFilter &= String.Format(" AND ((Article = {0}) OR (description LIKE '%{1}%'))", strArticleNo, strArticleName.Replace("'", "''"))
I prevent the user to enter the following characters: <,>,{,},;
Now my question:
Is this code safe against SQL injection attacks ?
Thank you!

Related

How to store more than 8000 characters and assign large concatenated string in SQL Server 2008?

I am trying to concatenate the large number of id'd and to update the status of all id's.
For example:
aclid in (4604019,4604018,4604017,4604016,4604015,4604014,4604013,4604012,4604011,4604010,4604009,4604008,4604007,4604006,4604005,4604004,4604003,4604002,4604001,4604000,4603999,4603998,4603997,4603996,4603995,4603994,4603993,4603992,4603991,4603990,4603989,4603988)`
Please check my stored procedure:
ALTER PROCEDURE [dbo].[VT_ACLReportChangeStatus]
(#ChangeStatus nvarchar(50) = null,
#ACLId nvarchar(max))
AS
/* Exec VT_ACLReportChangeStatus 'Complete','4599473,4599472,4599471,4599469,4599468' */
BEGIN
UPDATE VT_ACLReport
SET Status = #ChangeStatus
WHERE ACLId IN (SELECT * FROM SplitDelimiterString(#ACLId,','))
END
Please check my code behind:
ACLId = ACLId.ToString().Trim(',');
using (SqlConnection con = new SqlConnection(cs))
{
cmd = new SqlCommand("VT_ACLReportChangeStatus", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandTimeout = 3600;
cmd.Parameters.Add(new SqlParameter("#ACLId", SqlDbType.NVarChar,-1));
cmd.Parameters.Add(new SqlParameter("#ChangeStatus", SqlDbType.NVarChar, 50));
cmd.Parameters["#ACLId"].Value = ACLId;
cmd.Parameters["#ChangeStatus"].Value = ddlChangeStatus.SelectedItem.Text.ToString();
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
AclId column data type is bigint identity.
Please can you help me in concatenating large string and to update all rows whose aclid is present.
I would suggest, create one user defined table type in sql and one class in sourecode with your respective data. You can pass object of this class to sql and in your SP you can use join between main table and this table(which is received as input parameter) to update data.
Thanks all of you for your answers.. Actually it was silly mistake,while calling splitting function in Stored procedure. In function it was varchar(8000). :) solved it.
ALTER FUNCTION [dbo].[SplitDelimiterString] (#StringWithDelimiter VARCHAR(max), #Delimiter VARCHAR(max))
RETURNS #ItemTable TABLE (Item VARCHAR(max))
AS
BEGIN
DECLARE #StartingPosition INT;
DECLARE #ItemInString VARCHAR(max);
SELECT #StartingPosition = 1;
--Return if string is null or empty
IF LEN(#StringWithDelimiter) = 0 OR #StringWithDelimiter IS NULL RETURN;
WHILE #StartingPosition > 0
BEGIN
--Get starting index of delimiter .. If string
--doesn't contain any delimiter than it will returl 0
SET #StartingPosition = CHARINDEX(#Delimiter,#StringWithDelimiter);
--Get item from string
IF #StartingPosition > 0
SET #ItemInString = SUBSTRING(#StringWithDelimiter,0,#StartingPosition)
ELSE
SET #ItemInString = #StringWithDelimiter;
--If item isn't empty than add to return table
IF( LEN(#ItemInString) > 0)
INSERT INTO #ItemTable(Item) VALUES (#ItemInString);
--Remove inserted item from string
SET #StringWithDelimiter = SUBSTRING(#StringWithDelimiter,#StartingPosition +
LEN(#Delimiter),LEN(#StringWithDelimiter) - #StartingPosition)
--Break loop if string is empty
IF LEN(#StringWithDelimiter) = 0 BREAK;
END
RETURN
END

How to store the value of last inserted id(PK) into a variable where the primary key is a GUID in VB.Net

My question is related to the question asked in here How to get last inserted id?
But the scope_identity() will not work for me as in my case the primary key value for the table is a GUID value.
Also I have seen the question in here
SQL Server - Return value after INSERT
but the link does not explain how can i store the value in a variable. I need to store the value in a variable which I can use for multiple entry.
I am inserting hard coded value into multiple SQL Server tables. All the primary key columns are GUID.
The table structure are as follows.
This is the code I use to insert data into survey table.
Protected Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
Dim survey = Guid.NewGuid().ToString()
Dim SurveyTitle As String = "Diversity Monitoring Survey"
Dim SurveyDetail As String = ""
Core.DB.DoQuery("insert into survey(id,title, detail,employerid,userid) values(#id,#title, #detail, #eid, #uid);", Core.DB.SIP("title", SurveyTitle), Core.DB.SIP("detail", SurveyDetail), Core.DB.SIP("eid", LocalHelper.UserEmployerID()), Core.DB.SIP("uid", LocalHelper.UserID()), Core.DB.SIP("id", survey))
End Sub
Where DoQuery is
Shared Sub DoQuery(ByVal commandText As String, ByVal ParamArray params As SqlParameter())
Dim conn As SqlConnection = Nothing
Try
conn = GetOpenSqlConnection()
DoQuery(conn, commandText, params)
Finally
If conn IsNot Nothing Then conn.Dispose()
End Try
End Sub
Now I want to retrieve of just inserted SurveyId value and store it into a variable strSurveyId
Dim strSurveyID As String
So that I can insert that value in the table SurveyQuestionCategory:
Core.DB.DoQuery("insert into surveyquestioncategory(title, detail, surveyid) values(#title, #detail, #sid)", Core.DB.SIP("title", strSurveyQuestionCategoryTitle), Core.DB.SIP("detail", strSurveyQuestionCategoryDetail), Core.DB.SIP("sid", strSurveyID))
scope_identity() will not work in my case as the the is GUID.
I have tried this
SELECT * from [MPBlLiteDev].[dbo].[Survey] where id=SCOPE_IDENTITY()
But it gives me a error
Operand type clash: uniqueidentifier is incompatible with numeric
Please suggest with code.
Please go through the below stored procedure
Create proc SPInsertSurvey
(
#Title varchar(MAX),
#OutSurveyID UNIQUEIDENTIFIER output
)
as
DECLARE #Table TABLE (SurveyID UNIQUEIDENTIFIER)
begin
insert into survey(Title)
Output inserted.SurveyID into #Table values (#Title)
set #OutSurveyID =(select SurveyID from #Table)
end
You can execute it by using below Syntax
DECLARE #return_value int,
#OutSurveyID uniqueidentifier
EXEC #return_value = [dbo].[SPInsertSurvey]
#Title = N'''S1''',
#OutSurveyID = #OutSurveyID OUTPUT
SELECT #OutSurveyID as N'#OutSurveyID'
SELECT 'Return Value' = #return_value
Hope this will help
SCOPE_IDENTITY() will only return the newly generated Identity value if there is any, for GUID values you would need a table variable with OUTPUT clause in your insert statement something like this.....
DECLARE #NewIDs TABLE (ID UNIQUEIDENTIFIER)
insert into survey(id,title, detail,employerid,userid)
OUTPUT inserted.id INTO #NewIDs(ID)
values(#id,#title, #detail, #eid, #uid);
SELECT * FROM #NewIDs

Using VB.net to alter Stored Parameters

I am having an issue modifying a stored procedure variable with VB.net. I have found many posts on the topic that made it seem like a simple .AddWithValue solution but I keep ending up with a the following exception:
The IListSource does not contain any data sources.
The stored procedure is a Dynamic Pivot table that works great with a static value for #dcs
CREATE PROCEDURE [dbo].[dcspull]
as
DECLARE #cols AS NVARCHAR(MAX),
#colsNull AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX),
#dcs AS INT
select #cols = STUFF((SELECT ',' + QUOTENAME(FirstName)
FROM[Training Data]
WHERE DataCenterID = #dcs
GROUP by FirstName
ORDER by FirstName
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
select #colsNull = STUFF((SELECT ', coalesce(' + QUOTENAME(FirstName)+''''')
AS '+QUOTENAME(+FirstName)
FROM [Training Data]
WHERE DataCenterID = #dcs
GROUP by FirstName
ORDER by FirstName
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
set #query = 'SELECT Topic, ' + #colsNull + '
FROM
(SELECT FirstName, Topic, Level
FROM [Training Data]) x
PIVOT
(MAX(Level)
FOR FirstName IN (' + #cols + ')) p'
execute(#query)
The only value I am trying to change via VB.Net is #dcs. When #dcs is set statically the Gridview populates as expected, but as soon as I try to pass a value to the parameter it breaks.
Dim SQLCon As String = ConfigurationManager.ConnectionStrings("tCString").ToString
Dim DBConnSQL As New SqlConnection(SQLCon)
Dim da As New SqlDataAdapter
Dim ds As New DataSet
DBConnSQL.Open()
Dim dbcheckone As New SqlCommand("dcspull", DBConnSQL)
dbcheckone.parameters.addwithvalue("#dcs", Convert.ToInt64(DropDownList1.Text))
da.SelectCommand = dbcheckone
da.Fill(ds)
GridView1.DataSource = ds
GridView1.DataBind()
I am fairly new to stored procedures and am assuming this is something I just don't understand about the relationship.
First, I don't see any parameters in your stored procedure. They should come before AS.
Sample Create SP:
CREATE PROCEDURE HumanResources.uspGetEmployeesTest2
#LastName nvarchar(50),
#FirstName nvarchar(50)
AS
Now, when you add those parameters it should work under these conditions:
You need to set command Type
dbcheckone.CommandType = CommandType.StoredProcedure
Even though you don't use other parameters during your call, your procedure signature and procedure itself don't match. You can't omit non-optional parameters. So you need to list every parameter
dbcheckone.parameters.addwithvalue("#cols"...
dbcheckone.parameters.addwithvalue("#colsNull"...
dbcheckone.parameters.addwithvalue("#query"...
dbcheckone.parameters.addwithvalue("#dcs", Convert.ToInt32(DropDownList1.Text))
About the last line here - you set it in your code to Convert.ToInt64 but your parameter is int, therefore it should match int32.

why i get System.IndexOutOfRangeException: EmpPassword

myreader = obj.ExecuteReader
If myreader.Read Then
Session.Item("emp_id") = myreader("emp_id")
Session.Item("Email") = myreader("e_mail")
Session.Item("password") = myreader("EmpPassword")
ElseIf TextBox1.Text = "123456" Then
Response.Redirect("home.aspx")
Else
Response.Redirect("personal.aspx")
End If
The error is generated when you try to read a field from the reader that is not present in the SELECT field list of your query. So, if you want to read the EmpPassword value you need to change your query to
Dim query = "SELECT emp_id, E_mail, EmpPassword from personaldetails " & _
"where [E_mail] =#E_mail and EmpPassword =#EmpPassword"
Dim obj = new SqlCommand(query, connection)
.... add the parameters....
myreader = obj.ExecuteReader
.......
However, you already know the password and the email value because you need to set the parameters required by the where clause, so you could avoid to read them from the reader and add the fields to the select list.
Select statement should include all fields that you want to retrieve -
SELECT emp_id, [E_mail], EmpPassword
FROM personaldetails
WHERE [E_mail] = #E_mail
AND EmpPassword = #EmpPassword
- OR -
Since you already know e_mail and EmpPassword, you can select emp_id only, and assign e_mail and EmpPassword from original values -
select [emp_id] from personaldetails
where [E_mail] =#E_mail and EmpPassword =#EmpPassword
Session.Item("emp_id") = myreader("emp_id")
Session.Item("Email") = e_mail
Session.Item("password") = EmpPassword

SQL Query not inputting value of my variable

I'm having some trouble writing a query using variables. Here's my code
Dim bondnumber as String = "69836"
Dim PasswordCheck As String = "DECLARE #investor varchar(10),
#thepassword varchar(20), #linkedserver2 varchar(25), #sql varchar(1000) "
PasswordCheck += "SELECT #investor = '" & bondnumber & "',
#linkedserver2 = 'binfodev', "PasswordCheck += "#sql = 'SELECT * FROM ' +
#linkedserver2 + ' WHERE bondno = ''#investor'' ' EXEC(#sql)"
It doesn't seem to be passing the variables properly in the query and i'm not sure where i'm going wrong
any ideas?
What is the problem you are seeing specifically? More info would help.
What I can tell, is that you're code translates to a long line of SQL (substituting '69836' for bondnumber)
DECLARE #investor varchar(10), #thepassword varchar(20), #linkedserver2 varchar(25), #sql varchar(1000) SELECT #investor = '69836', #linkedserver2 = 'binfodev', #sql = 'SELECT * FROM ' + #linkedserver2 + ' WHERE bondno = ''#investor'' ' EXEC(#sql)
I'll bet if you execute that in a query window it will fail. Try adding ; at the end of each logical statement.
Have you considered just making this code a stored procedure and passing params to this? Code like this is pretty hazardous (SQL Injection), hard to read, and just a bit ugly in general.
Sample Stored Procedure Code:
CREATE PROCEDURE dbo.usp_MyStoredProcedure
#Param1 INT = NULL
AS
SELECT * FROM MyTable Where Col1 = #Param1
#Jamie - Well I personally tend to find it is much clearer to break things a part a little bit although its not technically necessary. I am just saying to build your parameter variables separately and then add them as parameterized (something like the following):
Dim sql As String = "SELECT * FROM #LinkedServer WHERE bondno = #BondNumber"
Dim c As New SqlConnection("Your Connection String Here")
Dim cmd As SqlCommand = c.CreateCommand()
With cmd
.CommandType = CommandType.Text
.CommandText = sql
.Parameters.Add(New SqlParameter("#LinkedServer", SqlDbType.VarChar)).Value = "binfodev"
.Parameters.Add(New SqlParameter("#BondNumber", SqlDbType.VarChar)).Value = "69836"
End With
Dim dt As New DataTable
Dim da As New SqlDataAdapter(cmd)
da.Fill(dt)

Resources