I am writing a query to allow a user to search on what they provide keywords in asp.net, C# and mssql:
string projectPart = null;
string categoryPart = null;
string descriptionPart = null;
if (this.Textbox_ProjectNr.Text.Trim().Length > 0)
projectPart = " AND Number='" + this.Textbox_ProjectNr.Text.Trim() + "' ";
if (this.Textbox_Category.Text.Trim().Length > 0)
categoryPart = " AND Category LIKE '%" + this.Textbox_Category.Text.Trim() + "%' ";
if (this.Textbox_pDescription.Text.Trim().Length > 0)
descriptionPart = " AND ProductDescription LIKE '%" + this.Textbox_pDescription.Text.Trim() + "%' ";
string query = "SELECT * from Project = p.ID " + projectPart + descriptionPart + categoryPart;
I dont know whether this query is sufficient for a traditional query search. Because I see there are some bottlenecks of this search:
if the user does not type anything, it returns all of the data => For this I only do the query when one of the fields are filled.
if the user provides some keywords "P" for each field, the result will be millions of data.
I dont know how to improve the search query basically. any suggestions are appreciated.
Thanks in adavance.
The most important improvement is to protect you code against SQL injection attacks.
You should not concatenate the raw input in the SQL string. If someone searches for the following text for example:
Bwah ha ha'; DROP DATABASE northwind; PRINT'
This will be added to your query to produce
SELECT *
FROM mytable
WHERE category LIKE '%Bwah ha ha'; DROP DATABASE northwind; PRINT'%'
This is a valid SQL command and will happily execute and drop your database (or do anything else the attacker wants)
For more information see SQL Injection and Santitizng Inputs.
You must make this query injection proof! Do not concatenate user entered values, but use parameters, like this:
SqlCommand cmd = new SqlCommand(#"
SELECT * from Project
WHERE
( Number = #Number OR #Number IS NULL ) AND
( Category LIKE #Category OR #Category IS NULL ) AND
( ProductDescription LIKE #ProductDescription OR #ProductDescription IS NULL )", conn);
if(!String.IsNullOrEmpty(this.Textbox_ProjectNr.Text.Trim()))
cmd.Parameters.AddWithValue("#Number", this.Textbox_ProjectNr.Text.Trim());
if(!String.IsNullOrEmpty(this.Textbox_Category.Text.Trim()))
cmd.Parameters.AddWithValue("#Category", this.Textbox_Category.Text.Trim());
if(!String.IsNullOrEmpty(this.Textbox_pDescription.Text.Trim()))
cmd.Parameters.AddWithValue("#ProductDescription", this.Textbox_pDescription.Text.Trim());
Also, you can add some client validation on user entered values. For instance, ask for more than three (?) characaters before running that query.
<asp:TextBox ID="Textbox_ProjectNr" runat="server" />
<asp:RegularExpressionValidator ID="Textbox_ProjectNr_Validator" runat="server"
ControlToValidate="Textbox_ProjectNr"
ErrorMessage="Minimum length is 3"
ValidationExpression=".{3,}" />
First of all, you must protect yourself from sql injections. You haven't specified what connection to the database you are using but most libraries allow adding the parameters in a different field, so they are sanitized automatically.
Secondly, you can (and should) limit the results count using the "LIMIT" (for mysql) or "TOP X" Like so:
Select * from TableName LIMIT 100 or Select TOP 100 * from TableName
Related
I am using ASP.NET and a SQL Server database.
I have options to search values from gridview base on some criteria. The criteria are mostly independent from each other.
For example:
"where ProductType = " + Convert.ToInt32(recordType.persoRecord) +
" and AccountNumber like '%" + SearchValue + "%'";
or
"where fileid=" + File_ID + ShowSearch +
" and lower(j.CardHolderName) like '%" + SearchValue.Trim().ToLower() + "%'
There are a lot of options to search by user. I have millions of rows of data in this table, in order to fetch the data and bind it fast to gridview, I have created a stored procedure.
It works fine for fetching and binding but for searching, it's hard to manage. Due to I don't have much time, i want to configure the stored procedure to 'if there's a searching' fetch the searched data only.
Here is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SetJobsGrid]
(#FileID varchar(50),
#PageIndex int = 1,
#PageSize int = 1,
#DynamicQuery NVARCHAR(MAX),
#SearchFlag bit,
#RecordCount int output)
AS
BEGIN
DECLARE #SearchQuerySQL as nvarchar(MAX)
SET NOCOUNT ON;
select ROW_NUMBER()OVER (
order by j.creationdate asc,
j.id desc
) AS RowNumber
, j.id as 'Serial #', j.jobname as 'File Record Name',
j.RecordNumber as 'Record Number',i.issuername as'Issuer Name',p.ProductName as 'Product Name',p.productNumber as 'Product Number',
(j.AccountNumber) as AccountNumber ,j.CardHolderName as 'Card Holder Name', j.CardholdersBranchName as 'Card Holder Branch Name',
j.ShipmentBranchName as 'Shipment Branch Name', j.EmbossingCardholderName as 'Embossing Card Holder Name', j.MaskedPAN as 'PAN',
j.creationdate as 'File Record Creation Date', j.status as 'File Record Status', j.chipdatastatuserror as 'ChipDataStatus Erro',
j.chipdatastatuserrormessage as 'Error Message', j.chipdatastatus as 'Data Prepared',j.isduplicaterecord as 'isduplicate',j.isduplicatefromsamefile as 'IsDuplicateFromSameFile',
j.validationerrors , j.isworkordercreatedForCard,j.isworkordercreatedForPin,j.isworkordercreatedForCarrier,j.PersoMachineId,j.PinMachineId,j.CarrierMachineId
INTO #Results
FROM jobs j join issuer i on j.issuerid=i.id join Product p on p.id=j.productid WHERE fileid = #FileID
IF(#SearchFlag = 1)
begin
select #SearchQuerySQL = 'SELECT ' + #RecordCount + ' = COUNT(*) FROM #Results ' + #DynamicQuery
EXEC(#SearchQuerySQL)
select #SearchQuerySQL = 'SELECT * FROM #Results ' + #DynamicQuery + ' and RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1'
EXEC(#SearchQuerySQL)
end
ELSE
begin
SELECT #RecordCount = COUNT(*)
FROM #Results
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
end
DROP TABLE #Results
END
When the SearchFlag is set to true from ASP.NET, I want to fetch only searched value. #DynamicQuery set from asp for example:
WHERE AccountNumber LIKE '%" + SearchValue + "%'"
or with many different case.
When I run this stored procedure as in the above, I get an exception:
Conversion failed when converting the varchar value 'select ' to data type int
Regards
To make the stored procedure use your dynamic where statement, it is better to use sp_executesql
example:
EXEC sp_executesql
N'select * from Employees where Id = #param1',
N'#param1 int'
,#param1 = 1
for more information about dynamic query refer to the following site
SQL Server Dynamic SQL
Too long for a comment but you need to understand why the error occurs to fix it. You have this snippet in your code
select #SearchQuerySQL = 'SELECT ' + #RecordCount +
First, if you intend to assign a scalar value (e.g., #SearchQuerySQL) then you should use SET, not SELECT. That's another topic you can research at your leisure. The assignment expression that follows is where you intended to do string concatenation. Unfortunately, the interpretation of the plus sign operator varies according to datatypes involved.
What happens when the database engine encounters an operator that involves 2 different datatypes. Like any other language, one (or both) must be converted to the same type in order to perform the expressed operation. How does the engine do that? There are rules for datatype precedence. In this case, your int parameter has higher precedence and so those strings in that expression are converted to int. That fails with the error that you encountered.
If you want to write dynamic sql, you need to have an advanced understanding of tsql. You should also consider searching the internet first before trying to reinvent the wheel. Maybe Aaron's article on dynamic pagination might help - but it might be a bit much for you at this point.
And while you're mucking about with things, add some comments within the procedure declaration about the usage you intend to support. No one should have to read your code to understand what it does and how it should be used.
I'm attempting a query to get the latest N messages in a particular conversation from a table of messages. I think this is the correct sql:
select * from
(select * from messages where convoId = to order by timestamp DESC limit 10)
order by timestamp ASC;
I have attempted this in sqlite.swift:
static let table = Table("messages")
let query = (table.filter(convoId == to).order(timestamp.desc).limit(10)).select(table[*]).order(timestamp.asc)
which is not working once the amount of messages goes past the limit. Is there any way to see what sql is produced by the sqlite.swift query? Any suggestions?
EDIT: I have also attempted the raw SQL query but now I'm not sure how to extract the result. I feel like this should be a last resort:
let toQuoted = "'" + to + "'"
let subQueryStr: String = [
"(SELECT * FROM",
MessageDataHelper.TABLE_NAME,
"WHERE",
MessageDataHelper.CONVO_ID, "=", toQuoted, "ORDER BY", MessageDataHelper.TIMESTAMP, "DESC LIMIT", String(5), ")"
].joined(separator: " ")
let queryStr: String = [
"SELECT * FROM",
subQueryStr,
["ORDER BY", MessageDataHelper.TIMESTAMP, "ASC;"].joined(separator: " ")
].joined(separator: "\n")
let stmt = try db.prepare(queryStr)
for row in stmt {
// ? how can this be used to create model structure
for (index, name) in stmt.columnNames.enumerate() {
print ("\(name)=\(row[index]!)")
}
}
row[index] is of type Binding, so I'm unsure how to retrieve the value there. Help please!
Thanks
Okay, so looks like sub query might be too complex to express in sqllite.swift. I ended up going with the raw sql query. You can retrieve the result by casting the binding as mentioned here:
Getting results from arbitrary SQL statements with correct binding in SQLite.swift
thats the code...
SQL = string.Format("Insert into Orders (AID,ODate) Values({0},'{1}')", AID, odate);
Dbase.ChangeTable(SQL, "Database1.mdb");
SQL = "Select MAX(OID) as MAXOID from Orders";
dt = Dbase.SelectFromTable(SQL, "Database1.mdb");
OID = dt.Rows[0][0].ToString();
string PID = Session["PID"].ToString();
SQL = string.Format("Insert into ListedProducts (OID,PID,PCat,cnt) Values({0},{1},'{2}',{3})", OID, PID, "B", cnt);
Dbase.ChangeTable(SQL, "Database1.mdb");
Label6.Text = "Your Product has been added to your basket , go to your basket to commit your order.";
// HEREEEEEEE///
SQL = String.Format("Update [Orders] SET [price]=[price]+{0} Where [OID]={1}", int.Parse(cnt) * pr, OID);
//SQL = "UPDATE [Orders] SET [price]=" + int.Parse(cnt) * pr + " WHERE OID=" + OID;
////////////////
Dbase.ChangeTable(SQL, "Database1.mdb");
so it should work and it doesnt show me an error but it doesnt add anything to the database but if i wont be adding it would update.
my database consists of OID,ODate,price,AID..
Run the SQL Commands manually and ensure your logic is correct... it's possible that your UPDATE is running but not doing anything since your WHERE isn't finding the OID you're passing in.
Read this:
https://www.owasp.org/index.php/SQL_Injection_Prevention_Cheat_Sheet
On the dynamic sql part it has various such as this:
So, if you had an existing Dynamic query being generated in your code that was going to Oracle that looked like this:
String query = "SELECT user_id FROM user_data WHERE user_name = '" + req.getParameter("userID")
+ "' and user_password = '" + req.getParameter("pwd") +"'";
try {
Statement statement = connection.createStatement( … );
ResultSet results = statement.executeQuery( query );
}
You would rewrite the first line to look like this:
Codec ORACLE_CODEC = new OracleCodec();
String query = "SELECT user_id FROM user_data WHERE user_name = '" +
ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("userID")) + "' and user_password = '"
+ ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("pwd")) +"'";
And it would now be safe from SQL injection, regardless of the input supplied.
But the later is says:
Oracle 10g escaping
An alternative for Oracle 10g and later is to place { and } around the string to escape the entire string. However, you have to be careful that there isn't a } character already in the string. You must search for these and if there is one, then you must replace it with }}. Otherwise that character will end the escaping early, and may introduce a vulnerability.
I did not see an example, but does this mean I can use braces instead of the Codec ORACLE_CODEC....etc.? Does anyone have an example? Thanks.
No, this is not an injection prevention technique. The only way to be 100% sure that you're not vulnerable to injection is to use prepared statements and bind parameters for all user input that needs to be inserted into the query. Anything less than that, and you're pretty much just rolling the dice.
Consider the following query.
SELECT LP.project_id, LP.title, LP.type, LU.NAME, LP.reference, LP.status, LP.correspondence, LP.source, LP.deadline, LP.mstat, LP.num_pages, LP.done,
LP.ss_notes
FROM LogiCpsProjects AS LP LEFT OUTER JOIN
LogiCpsProjectAssignments AS LPA ON LP.project_id = LPA.project_id LEFT OUTER JOIN
LogiCpsUsers AS LU ON LPA.writer_id = LU.ID
WHERE (LP.status <> 'Closed') AND (LP.status <> 'Cancelled') AND (LP.type LIKE '%' + #type + '%') AND (LP.status LIKE '%' + #status + '%') AND
(LP.source LIKE '%' + #source + '%') AND (CAST(LP.mstat AS VARCHAR(50)) LIKE '%' + #mstat + '%')
ORDER BY LP.project_id
In this query I am trying to carry out a search that is based on four parameters namely #type, #status, #source and #mstat. The problem is this query works exactly as it should in Sql Server Management Studio for example if I enter 'Article' in type parameter and leave others blank then it returns all the records with type = 'Article' and if I enter type = 'Article' and status = 'Working' then it returns all records with type = 'Article' and status = 'Working' and so on... In simple words the search is dynamic as user can enter leave all parameters blank or put values in all four. It is working fine in MS but not in actual program.
I am using an SqlDataSource, with same query if I leave all parameters blank it does not return anything, if I enter 1 parameter it still does not return anything, it only return records if I enter all four parameters which means it is not working on blank parameters. To send parameters I am using four drop down lists. All of them look like this one.
<asp:DropDownList ID="ddlStatus" runat="server" Width="220px">
<asp:ListItem Selected="True" Value="" Text="">All</asp:ListItem>
<asp:ListItem>Details Pending</asp:ListItem>
<asp:ListItem>Working</asp:ListItem>
<asp:ListItem>Awaiting Feedback</asp:ListItem>
<asp:ListItem>Project Complete (AFB)</asp:ListItem>
<asp:ListItem>Revision Required Client</asp:ListItem>
<asp:ListItem>Revision Required Editor</asp:ListItem>
<asp:ListItem>To be Cancelled</asp:ListItem>
<asp:ListItem>Cancelled</asp:ListItem>
<asp:ListItem>Requirements Not Clear</asp:ListItem>
<asp:ListItem>Hold</asp:ListItem>
<asp:ListItem>Closed</asp:ListItem>
</asp:DropDownList>
The first item is defined as ALL and it has value and text set to "" (Empty string) so when user select ALL it means all records for that particular parameter.
What I am doing wrong ?
Pay attention to whether you're sending null or a blank string for all parameters. If you run the sql statement "select '%' + null + '%'" in management studio you'll find the result is null, which is not what you're looking for.