How to correctly insert a parameter into an existing sql query to avoid SQL Injections - sql-injection

I have seen some answers already but my query is a little bit different:
Here is an original query:
cmd.CommandText = "select count(Table1.UserID) from Table1 INNER JOIN
Table2 ON Table1.ID = Table2.ID where Table1.Userid = " + UserID + " and
Table1.Number != '" + Number +"' and Table2.ID < 4";
Here is a modified query for SQL Injections:
cmd.CommandText = "select count(Table1.UserID) from Table1 INNER JOIN
Table2 ON Table1.ID = Table2.ID where Table1.Userid = #userId and
Table1.ID != #Number and Table2.ID < 4";
If you can notice, the first query has UserId surrounded by double quotes: ..." + UserID +"... and Number us surrounded by single and double quotes: ...'" + Number + "'...
Here is how I'm setting parameters:
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Number", Number);
cmd.Parameters.AddWithValue("#userId",UserID);
where UserID is an integer and Number is a string.
So, my question is, if the modified query formatted the right way? Is there any difference how to put #UserId and #Number parameters into a query considering the different ways they are specified in the original query?

I have been working on .net Mvc for a long time, and I can ensure you the parameters are correctly fixed by yourself in the second case, and you do not need to worry. By the way you can still debug and test if you can inject yourself. Briefly, your code looks great and invulnerable.
This is how i do it, which is similar and also as safe as yours:
string Query = #"select a1, a2, a3, a4 from table1 where a1 in
(select b1 from table2 where b2 = #start or b2 = #end)";
using (SqlCommand Comm = new SqlCommand(Query, Conn))
{
Comm.Parameters.Add("#start", SqlDbType.NVarChar).Value = start;
Comm.Parameters.Add("#end", SqlDbType.Int).Value = end;
}

In your initial query, the double quotes belonged to the actual text of the query, not the parameter. The single quotes you would add when appending a string into the sql query. I do not know why you would put single quotes around something called Number. If in fact that is a numeric type variable, it can go into the query without the single quotes. But if it has single quotes, the only thing that happens is that Sql sees it as a string, and then converts it to a number if it is going to use it as one. For example, if Table1.Number is numeric.
But, as you have noted, building your query string by appending your parameters into your query string is terrible practice as it opens the door, wide open, for sql injection attacks. So, you go with parameterized queries, as you have.
In parameterized queries, you do not worry about quotes. For parameters that are string values, the environment will worry about encasing them in quotes as it builds the command to pass to your sql db. For parameters that are numeric, quotes are not needed, and again, that is taken care of for you.

I think your 2nd version of the query is much better and from the looks of it, it should work just fine.
Adding parameters instead of concatenating your values is much safer against sql injection. And in this example, and I can't see any way to do a sql injection.
EDIT
When using parametrised queries, you dont need to add any quotes, just like when you declare a variable and use it in a query - you dont need to use quotes.
DECLARE #x CHAR(10) = 'abc'
SELECT #x
When using concatenation of values inside a query, if the value you're trying to add into the query is a CHAR, you need to wrap it between single quotes. If it's an INT, it shouldn't be wrapped between single quotes.
SELECT 'abc', 1
The double quotes you have in your first query dont have anything to do with the sql statement, they are used in your c# code to build the sql statement string you're trying to assign to CommandText.
string abcVar = "abc";
int intVar = 1;
string sqlCommand = "SELECT '" + abcVar + "', " + intVar;

Related

How to properly parameterize my postgresql query

I'm trying to parameterize my postgresql query in order to prevent SQL injection in my ruby on rails application. The SQL query will sum a different value in my table depending on the input.
Here is a simplified version of my function:
def self.calculate_value(value)
calculated_value = ""
if value == "quantity"
calculated_value = "COALESCE(sum(amount), 0)"
elsif value == "retail"
calculated_value = "COALESCE(sum(amount * price), 0)"
elsif value == "wholesale"
calculated_value = "COALESCE(sum(amount * cost), 0)"
end
query = <<-SQL
select CAST(? AS DOUBLE PRECISION) as ? from table1
SQL
return Table1.find_by_sql([query, calculated_value, value])
end
If I call calculate_value("retail"), it will execute the query like this:
select location, CAST('COALESCE(sum(amount * price), 0)' AS DOUBLE PRECISION) as 'retail' from table1 group by location
This results in an error. I want it to execute without the quotes like this:
select location, CAST(COALESCE(sum(amount * price), 0) AS DOUBLE PRECISION) as retail from table1 group by location
I understand that the addition of quotations is what prevents the sql injection but how would I prevent it in this case? What is the best way to handle this scenario?
NOTE: This is a simplified version of the queries I'll be writing and I'll want to use find_by_sql.
Prepared statement can not change query structure: table or column names, order by clause, function names and so on. Only literals can be changed this way.
Where is SQL injection? You are not going to put a user-defined value in the query text. Instead, you check the given value against the allowed list and use only your own written parts of SQL. In this case, there is no danger of SQL injection.
I also want to link to this article. It is safe to create a query text dynamically if you control all parts of that query. And it's much better for RDBMS than some smart logic in query.

How to pick up data from row and put it into tPostgresqlInput?

I have a requets which giving me an ids. I need to iterate them into another request, so I have a sheme like this: scheme
In tPostgresqlInput I have this code rc.id = upper('18ce317b-bf69-4150-b880-2ab739eab0fe') , but instead of id I need to put smthn like globalMap.get(row4.id). How did I do this?
Apparently this is a syntax issue
Try with :
"select * FROM table LEFT JOIN table on parameter JOIN table on parameter
WHERE 1=1 AND
column = 'content'
AND upper(rc.id) = upper('"+((String)globalMap.get("row4.id")) +"')"
Expressions in tDBInput should always begin and end with double quotes.
Don't forget to cast globalMap.get() with the type of your element (here I put String)
.equals is not a DB function but a java function. I have replaced it with '='
Let me know if it's better

Passing a null parameter to a native query using #Query JPA annotation

In a Spring Boot application, I have a SQL query that is executed on a postgresql server as follows :
#Query(value = "select count(*) from servers where brand= coalesce(?1, brand) " +
"and flavour= coalesce(?2, flavour) ; ",
nativeQuery = true)
Integer icecreamStockCount(String country, String category);
However,
I get the following error when I execute the method :
ERROR: COALESCE types bytea and character varying in PostgreSQL
How do I pass String value = null to the query?
**NOTE : ** I found that my question varied from JPA Query to handle NULL parameter value
You need not coalesce, try this
#Query("select count(*) from servers where (brand = ?1 or ?1 is null)" +
" and (flavour = ?2 or ?2 is null)")
Integer icecreamStockCount(String country, String category);
When I encounted this error, I ended up using a combination of OR and CAST to solve the issue.
SELECT COUNT(*)
FROM servers
WHERE (?1 IS NULL OR brand = CAST(?1 AS CHARACTER VARYING))
AND (?2 IS NULL OR flavour = CAST(?2 AS CHARACTER VARYING))
This works even if ?1, ?2, brand and flavor are all nullable fields.
Note that passing null for ?1 means "all servers regardless of brand" rather than "all servers without a brand". For the latter, you could use IS DISTINCT FROM as follows.
SELECT COUNT(*)
FROM servers
WHERE (CAST(?1 AS CHARACTER VARYING) IS NOT DISTINCT FROM brand)
AND (CAST(?2 AS CHARACTER VARYING) IS NOT DISTINCT FROM flavour)
Finally, certain parameter types such as Boolean cannot be cast in SQL from BYTEA to BOOLEAN, for those cases you need a double cast:
SELECT COUNT(*)
FROM servers
WHERE (?1 IS NULL OR is_black = CAST(CAST(?1 AS CHARACTER VARYING) AS BOOLEAN))
In my eyes this is a problem in Hibernate which could be solved by passing Java null parameters as plain SQL NULLs rather than interpreting null as a value of type BYTEA.
If you really need to use native query, there is a problem because it's an improvement not implemented yet, see hibernate. If you don't need to use native you can do (where ?1 is null or field like ?1). Assuming you do need native,
you may treat the String before by setting this empty and then calling the repository and this one would be like:
#Query(value = "select count(*) from servers where (?1 like '' or brand like ?1) " +
"and (?2 like '' or flavour like ?2)",
nativeQuery = true)
Integer icecreamStockCount(String country, String category);
There is always javax.persistence.EntityManager bean as option for native query situations and I recommend it instead of previous approach. Here you can append to your query the way you want, as follows:
String queryString = "select count(*) from servers ";
if (!isNull(country)) queryString += "where brand like :country";
Query query = entityManager.createNativeQuery(queryString);
if (!isNull(country)) query.setParameter("country", country);
return query.getResultList();
Observations:
Newer versions have improved this '+' concatenation Strings. But you can build your queryString the way you want with StringBuilder or String Format, it doesn't matter.
Be careful with SQL injection, the setParameter method avoid this kind of problem, for more information see this Sql Injection Baeldung
So this is not the exact answer to the question above, but I was facing a similar issue, I figured I would add it here, for those that come across this question.
I was using a native query, in my case, it was not a singular value like above, but I was passing in a list to match this part of the query:
WHERE (cm.first_name in (:firstNames) OR :firstNames is NULL)
I was getting the bytea error, in the end I was able to send an empty list.
(null == entity.getFirstName()? Collections.emptyList() : entity.getFirstName())
In this case, sending the empty list to the resolver worked, where as null did not.
hope this saves you some time.
null parameters are not allowed before Hibernate 5.0.2.
See https://hibernate.atlassian.net/browse/HHH-9165
and the replies to https://www.postgresql.org/message-id/6ekbd7dm4d6su5b9i4hsf92ibv4j76n51f#4ax.com

Using many connection.cursor()

I want to fetch data from 3 tables in a single database at once. I used 3 conn.cursor() to it.. Are there any sophisticated ways to do it?
conn = psycopg2.connect(database="plottest", user="postgres")
self.statusbar.showMessage("Database opened Sucessfully", 1000)
cur = conn.cursor()
cur1 = conn.cursor()
cur2 = conn.cursor()
cur.execute("SELECT id ,actual from \"%s\" " % date)
rows = cur.fetchall()
cur1.execute("SELECT qty from DAILY where date = \'%s\'" % date)
dailyqty = cur1.fetchone()
cur2.execute("SELECT qty from MONTHLY where month = \'%s\'" % month)
monthqty = cur2.fetchone()
Awoogah awoogah, SQL injection warning! Don't write code using string interpolation. What happens if someone calls your code with the "date" ');-- DROP TABLE DAILY;-- ?
Use bind parameters. Always.
The only exception is for dynamic identifiers, like in the case above where you seem to use a table named after the current date. In that case you must "double quote" them and double any contained double-quotes. In your case that means that date should be date.replace('"', '""') where you substitute it into the SQL.
Now, back to our regular programming.
Since you fetchall from each cursor you can just re-use it. You don't need new cursors each time.
You can also combine the daily and monthly stats if you want, with a UNION ALL. I fixed your capitalisation and parameter binding in the process:
cur.execute("""SELECT 1, qty FROM daily WHERE date = %s
UNION ALL
SELECT 2, qty FROM monthly WHERE month = %s
ORDER BY 1""",
(date, month))
Note that string interpolation isn't used, instead a 2-tuple of parameters is passed to psycopg2 to bind directly. There's no need for quotes around the parameters, psycopg2 adds them if needed.
This avoids a client-server round trip by bundling the two queries. The extra column andORDER BY is technically needed so you can safely assume the first row is the daily results and second is the monthly. In practice PostgreSQL won't re-order them with UNION ALL though.
You can combine
SELECT a1 FROM t1 WHERE b1 = 'v1';
and
SELECT a2 FROM t2 WHERE b2 = 'v2';
to a single statement like this:
SELECT t1.a1, t2.a2 FROM t1, t2
WHERE t1.b1 = 'v1' AND t2.b2 = 'v2';
provided that both queries return exactly one row.

T-SQL: Quote syntax in building SQL string

I am building a SQL sting, mostly the WHERE clause, on the fly based on parameters passed in and the executing the built string. So in the end it is: EXEC (#strSELECT + #strFROM + #strWHERE + #strORDERBY).
My #strSELECT looks like: SET #strSELECT = 'SELECT DISTINCT FieldA, FIELDB, FIELDC'
This all works so far, but now I added a nested function field to the end of #strSELECT so the whole #strSELECT now looks like: SET #strSELECT = 'SELECT DISTINCT FieldA, FIELDB, FIELDC, UPPER(REPLACE(CONVERT(VARCHAR, CAST(MyStringDateField AS DATETIME),6),' ','-')) AS FormattedDate'
I know the new nested function field works because I tested it without concantenation and I also know that the problem is with the single quotes [' ','-'] within the function. Can anyone help me with the correct quote syntax when building the query string? thnx.
You'll have to make your nested function look like this:
UPPER(REPLACE(CONVERT(varchar, CAST(MyStringDateField AS datetime),6),'' '',''-''))
Double up on the quotes inside the string. This works for me and returns "02-MAR-11":
DECLARE #strSELECT nvarchar(200)
SET #strSELECT = 'SELECT UPPER(REPLACE(CONVERT(VARCHAR, CAST(GETDATE() AS DATETIME),6),'' '',''-'')) AS FormattedDate'
EXECUTE sp_executesql #strSELECT
To insert a single quote in a T-SQL string you prepend another single quote. Thus your #strSelect statement becomes
SET #strSELECT = 'SELECT DISTINCT FieldA, FIELDB, FIELDC, UPPER(REPLACE(CONVERT(VARCHAR, CAST(MyStringDateField AS DATETIME),6),'' '',''-'')) AS FormattedDate'
That doesn't mean that this is the right way to do it though. You should try to use parameterized queries wherever possible to avoid sql injection attacks