I've got a fully functionable (secure) session to a SQL Server database (version 10.50.4000). This is stored in a public variable:
SqlConnection conn = new SqlConnection();
I only want to run SELECT queries. For anything else, the user account got no rights.
The queries are built with only one user entry, which is inserted into a simple Text Box.
Unfortunately I must not tell you the original command text. So I make it simple for you:
function print_users(string filtervalue)
{
SqlCommand cmd = null;
cmd = new SqlCommand("SELECT users From groups WHERE group_name LIKE '%#fv%'", this.conn)
cmd.Parameters.Add("#fv", SqlDbType.NVarChar);
cmd.Parameters["#fv"].Value=filtervalue;
rdr = cmd.ExecuteReader();
while(rdr.Read())
{
//Do something with the answer from the DB
}
}
But this does not do the trick. I also tried AddWithValue, but I got no luck.
When creating a stop-point on the line, where #fv should be replaced, I can go through the code line-by-line. And I can see that the command, where #fv should be replaced, is processed with no error. But #fv is not replaced (or at least I cannot see the replacement in the debug console).
What am I doing wrong?
EDIT:
thank you for your replies. Leaving out the single quotes ( ' ) did the trick.
And I also learned that this is not a string replacement. Thank you.
Just one word: The connection is not left open all the time. It's immediately closed, when it's not needed any more; and re-established when needed again - I just forgot to write that into my sample code.
Again: Thank you for your help!
You can't see it being replaced in your debug session; the replacement occurs in the SQL server code itself...
The client (your code) send both the SQL-string and the value of the parameter as seperate things to the server. There the SQL Engine 'replaces' the parameter with its value while executing it.
You should also put the 'wildcards' inside your parametervalue, not inside the query.
cmd = new SqlCommand("SELECT users From groups WHERE group_name LIKE #fv ", this.conn)
cmd.Parameters.Add("#fv", SqlDbType.NVarChar);
cmd.Parameters["#fv"].Value= "%" + filtervalue + "%";
The parameter is not working because it is inside a string literal. You want to build the string like this:
cmd = new SqlCommand("SELECT users From groups WHERE group_name LIKE '%' + #fv + '%'");
While we're at it, keeping a global connection like that is bad. It can cause strange side effects, especially in web apps. Instead, keep a global connection string, and then use that string to create a new connection on each request.
Also, "replace" is the wrong word here. Sql parameters are never replaced, even when they work. That's the whole point. There is no string replacement into your query at any point, ever. It's more like you declared an #fv variable at the server level in a stored procedure, and assigned your data directly to that variable. In this way, there is no possibility for a vulnerability in parameter replacement code, because the data portion of your query remains separate throughout the execution process. In same way, don't think in terms of "sanitizing" a parameter for a query; instead, think in terms of quarantining the data.
Related
I am very new to working with databases. Now I can write SELECT, UPDATE, DELETE, and INSERT commands. But I have seen many forums where we prefer to write:
SELECT empSalary from employee where salary = #salary
...instead of:
SELECT empSalary from employee where salary = txtSalary.Text
Why do we always prefer to use parameters and how would I use them?
I wanted to know the use and benefits of the first method. I have even heard of SQL injection but I don't fully understand it. I don't even know if SQL injection is related to my question.
Using parameters helps prevent SQL Injection attacks when the database is used in conjunction with a program interface such as a desktop program or web site.
In your example, a user can directly run SQL code on your database by crafting statements in txtSalary.
For example, if they were to write 0 OR 1=1, the executed SQL would be
SELECT empSalary from employee where salary = 0 or 1=1
whereby all empSalaries would be returned.
Further, a user could perform far worse commands against your database, including deleting it If they wrote 0; Drop Table employee:
SELECT empSalary from employee where salary = 0; Drop Table employee
The table employee would then be deleted.
In your case, it looks like you're using .NET. Using parameters is as easy as:
string sql = "SELECT empSalary from employee where salary = #salary";
using (SqlConnection connection = new SqlConnection(/* connection info */))
using (SqlCommand command = new SqlCommand(sql, connection))
{
var salaryParam = new SqlParameter("salary", SqlDbType.Money);
salaryParam.Value = txtMoney.Text;
command.Parameters.Add(salaryParam);
var results = command.ExecuteReader();
}
Dim sql As String = "SELECT empSalary from employee where salary = #salary"
Using connection As New SqlConnection("connectionString")
Using command As New SqlCommand(sql, connection)
Dim salaryParam = New SqlParameter("salary", SqlDbType.Money)
salaryParam.Value = txtMoney.Text
command.Parameters.Add(salaryParam)
Dim results = command.ExecuteReader()
End Using
End Using
Edit 2016-4-25:
As per George Stocker's comment, I changed the sample code to not use AddWithValue. Also, it is generally recommended that you wrap IDisposables in using statements.
You are right, this is related to SQL injection, which is a vulnerability that allows a malicioius user to execute arbitrary statements against your database. This old time favorite XKCD comic illustrates the concept:
In your example, if you just use:
var query = "SELECT empSalary from employee where salary = " + txtSalary.Text;
// and proceed to execute this query
You are open to SQL injection. For example, say someone enters txtSalary:
1; UPDATE employee SET salary = 9999999 WHERE empID = 10; --
1; DROP TABLE employee; --
// etc.
When you execute this query, it will perform a SELECT and an UPDATE or DROP, or whatever they wanted. The -- at the end simply comments out the rest of your query, which would be useful in the attack if you were concatenating anything after txtSalary.Text.
The correct way is to use parameterized queries, eg (C#):
SqlCommand query = new SqlCommand("SELECT empSalary FROM employee
WHERE salary = #sal;");
query.Parameters.AddWithValue("#sal", txtSalary.Text);
With that, you can safely execute the query.
For reference on how to avoid SQL injection in several other languages, check bobby-tables.com, a website maintained by a SO user.
In addition to other answers need to add that parameters not only helps prevent sql injection but can improve performance of queries. Sql server caching parameterized query plans and reuse them on repeated queries execution. If you not parameterized your query then sql server would compile new plan on each query(with some exclusion) execution if text of query would differ.
More information about query plan caching
Two years after my first go, I'm recidivating...
Why do we prefer parameters? SQL injection is obviously a big reason, but could it be that we're secretly longing to get back to SQL as a language. SQL in string literals is already a weird cultural practice, but at least you can copy and paste your request into management studio. SQL dynamically constructed with host language conditionals and control structures, when SQL has conditionals and control structures, is just level 0 barbarism. You have to run your app in debug, or with a trace, to see what SQL it generates.
Don't stop with just parameters. Go all the way and use QueryFirst (disclaimer: which I wrote). Your SQL lives in a .sql file. You edit it in the fabulous TSQL editor window, with syntax validation and Intellisense for your tables and columns. You can assign test data in the special comments section and click "play" to run your query right there in the window. Creating a parameter is as easy as putting "#myParam" in your SQL. Then, each time you save, QueryFirst generates the C# wrapper for your query. Your parameters pop up, strongly typed, as arguments to the Execute() methods. Your results are returned in an IEnumerable or List of strongly typed POCOs, the types generated from the actual schema returned by your query. If your query doesn't run, your app won't compile. If your db schema changes and your query runs but some columns disappear, the compile error points to the line in your code that tries to access the missing data. And there are numerous other advantages. Why would you want to access data any other way?
In Sql when any word contain # sign it means it is variable and we use this variable to set value in it and use it on number area on the same sql script because it is only restricted on the single script while you can declare lot of variables of same type and name on many script. We use this variable in stored procedure lot because stored procedure are pre-compiled queries and we can pass values in these variable from script, desktop and websites for further information read Declare Local Variable, Sql Stored Procedure and sql injections.
Also read Protect from sql injection it will guide how you can protect your database.
Hope it help you to understand also any question comment me.
Old post but wanted to ensure newcomers are aware of Stored procedures.
My 10ยข worth here is that if you are able to write your SQL statement as a stored procedure, that in my view is the optimum approach. I ALWAYS use stored procs and never loop through records in my main code. For Example: SQL Table > SQL Stored Procedures > IIS/Dot.NET > Class.
When you use stored procedures, you can restrict the user to EXECUTE permission only, thus reducing security risks.
Your stored procedure is inherently paramerised, and you can specify input and output parameters.
The stored procedure (if it returns data via SELECT statement) can be accessed and read in the exact same way as you would a regular SELECT statement in your code.
It also runs faster as it is compiled on the SQL Server.
Did I also mention you can do multiple steps, e.g. update a table, check values on another DB server, and then once finally finished, return data to the client, all on the same server, and no interaction with the client. So this is MUCH faster than coding this logic in your code.
Other answers cover why parameters are important, but there is a downside! In .net, there are several methods for creating parameters (Add, AddWithValue), but they all require you to worry, needlessly, about the parameter name, and they all reduce the readability of the SQL in the code. Right when you're trying to meditate on the SQL, you need to hunt around above or below to see what value has been used in the parameter.
I humbly claim my little SqlBuilder class is the most elegant way to write parameterized queries. Your code will look like this...
C#
var bldr = new SqlBuilder( myCommand );
bldr.Append("SELECT * FROM CUSTOMERS WHERE ID = ").Value(myId);
//or
bldr.Append("SELECT * FROM CUSTOMERS WHERE NAME LIKE ").FuzzyValue(myName);
myCommand.CommandText = bldr.ToString();
Your code will be shorter and much more readable. You don't even need extra lines, and, when you're reading back, you don't need to hunt around for the value of parameters. The class you need is here...
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
public class SqlBuilder
{
private StringBuilder _rq;
private SqlCommand _cmd;
private int _seq;
public SqlBuilder(SqlCommand cmd)
{
_rq = new StringBuilder();
_cmd = cmd;
_seq = 0;
}
public SqlBuilder Append(String str)
{
_rq.Append(str);
return this;
}
public SqlBuilder Value(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append(paramName);
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public SqlBuilder FuzzyValue(Object value)
{
string paramName = "#SqlBuilderParam" + _seq++;
_rq.Append("'%' + " + paramName + " + '%'");
_cmd.Parameters.AddWithValue(paramName, value);
return this;
}
public override string ToString()
{
return _rq.ToString();
}
}
I wonder if anyone can shed any light on why I'm not getting data in this piece of code:
Private Sub RecoverUnsentOrderToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles RecoverUnsentOrderToolStripMenuItem.Click
' Find orders for this branch with status = "1" - created but not acked from the server
Dim myxDBReader As SqlDataReader
Dim myxDBcmd As SqlCommand
Dim query As String
query = "select * from orders where branch = #branch and status = 1;"
myxDBCmd = New SqlCommand(query, myDBCnn)
myxDBcmd.Parameters.Add("#branch", SqlDbType.VarChar).Value = BranchCode
myxDBReader = myDBCmd.ExecuteReader
If myxDBReader.HasRows Then
Do While myxDBReader.Read
Stop
Loop
End If
BranchCode and my database connection are public variables. When I run this code, it gets as far as the "Stop" and stops, but when I try to use the results, for example in the immediate window by trying to ? myxdbreader(0).tostring, I'm getting "Invalid attempt to read when no data is present" exceptions. When I hover over myxdbreader to view the results, I get a list of the rows, but cannot see the data in them.
This is inside a reasonably large (for me, but not massive) VB application which executes all manner of queries and retrieves data without any problems. The code is copied and pasted from another section of the code where it works quite well. The database connection is a single one, opened when the application is started and passed around as required. Another part of the application writes into this "orders" table without any problem, using the same database connection.
I have another toolstripmenu function which is identical in every respect except the query, in this case it is simply
select * from linkstatus where id=1
and that has the same issue - stops inside the do while dbreader.read loop so it has obviously found a row, but will not allow me to access the data in the way that I normally do.
Because it gets to the "Stop", I know that HasRows is indeed true, and it appears to have read the first row. The only thing I can see that is different is that this code is run from a Menu that I added to the form today, whereas all the rest of the code is run from a variety of buttons on the main form.
Everywhere I've looked up that error message, it appears to be because people have not executed "Read".
This is vb.net from Visual Studio 2019, accessing SQL Server 2018.
You are using a Public connection object, which as the comments state isn't the way to go. But most importantly, note that only one SqlDataReader can be associated with one SqlConnection, compounding the issue of a single shared connection.
SqlDataReader.Read Method
Only one SqlDataReader per associated SqlConnection may be open at a time, and any attempt to open another will fail until the first one is closed. Similarly, while the SqlDataReader is being used, the associated SqlConnection is busy serving it until you call Close.
It's probable that you already have an open Reader, and that is why you are seeing results for another data set.
As noted in the comments by #jmcilhenny, #Jeroen Mostert and #Jimi the root cause of this was using a single public database connection. I have changed the code now so that it connects and disconnects to the database each time I need to access it using a private connection, and it now works correctly.
There is a legacy application that uses a table to translate job names to filenames. This legacy application queries it as follows:
SELECT filename FROM aJobTable WHERE jobname = 'myJobName'
But in reality those jobnames always match the filenames (e.g. 'myJobName.job' is the jobname but also the filename) That makes this table appear unnecessary. But unfortunately, we cannot change the code of this program, and the program just needs to select it from a table.
That's actually a bit annoying. Because we do need to keep this database in sync. If a jobname is not in the table, then it cannot be used. So, as our only way out, right now we have some vbscripts to synchronize this table, adding records for each possible filename. As a result, the table just 2 columns with identical values. -- We want to get rid of this.
So, we have been dreaming about some hack that queries the data with the jobname, but just always returns the jobname again, like a copy/mirror query. Then we don't actually have to populate a table at all.
"Exploits"
The following can be configured in this legacy application. My hunch is that these may open the door for some tricks/hacks.
use of either MS Access or SQL Server (we prefer sql server)
The name of the table (e.g. aJobTable)
The name of the filename column (e.g. filename)
The name of the jobname column (e.g. jobname)
Here is what I came up with:
If I create a table-valued function mirror(a) then I get pretty close to what I want. Then I could use it like
SELECT filename FROM mirror('MyJobName.job')
But that's just not good enough, it would be if I could force it to be like
SELECT filename FROM mirror WHERE param1 = 'MyJobName.job'
Unfortunately, I don't think it's possible to call functions like that.
So, I was wondering if perhaps somebody else knows how to get it working.
So my question is: "How can you create a table (or other object) that always returns the value passed to its WHERE-clause, like a mirror."
It's kinda hard to answer not knowing the code that the application use, but if we assume it only takes strings and concatenate them without any tests whatsoever, I would assume code like this: (translated to c#)
var sql = "SELECT "+ field +" FROM "+ table +" WHERE "+ conditionColumn +" = '"+ searchValue +"'";
As this is an open door for SQL injection, and given the fact that SQL Server allows you two ways of creating an alias - value as alias and alias = value,
you can take advantage of that and try to generate an SQL statement like this:
SELECT field /* FROM table WHERE conditionColumn */ = 'searchValue'
So field should be "field /* ",
and conditionColumn should be "conditionColumn */"
table name doesn't matter, you could leave an empty string for it.
I am working on an security remediation of an existing java web application. The application has some dynamic sql code executed by JDBC.But, this is not accepted by Static Code analysis tool we use. So, I am looking for a way to remediate the issue.Basically, I have validated all the input passed to code which constructs the query , so there is no possiblity of SQL Injection. But, the SCA tool still does not approve of this validation. So, want to know if there is any way I can avoid Dynamic Query logic. Prepared Statements cannot be used as the query is dynamicly constructed based on conditions.
I know Stored Procedure can help. But, I understand it has its own issues and the team is also not experienced on Stored Procedures. So, looking for a better way to address this issue. Also, since we are using SQL Server I didn't find any encoding function in the ESAPI toolkit to sanitize the query parameters which has support for oracle and mysql only.
Want to know if using a framework like Mybatis to offload the java code which constructs sql to xml files would resolve the issue. Can you guys let me know if there is any other better way.
You can generate SQL dynamically and use prepared statements.
Here is the idea how this can be done.
Now you have code like this:
StringBuilder whereClause = new StringBuilder();
if (name != null) {
whereClause.append(String.format("name = '%s'", name));
}
// other similar conditions
String sql = "select * from table" + (whereClause.length() != 0 ? "where " + whereClause.toString() : "");
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// use rs to fetch data
And you need to change this to something like
StringBuilder whereClause = new StringBuilder();
ArrayList<Object> parameters = new ArrayList<>();
if (name != null) {
whereClause.append("name = ?");
parameters.add(name);
}
// other similar conditions
String sql = "select * from table" + (whereClause.length() != 0 ? "where " + whereClause.toString() : "");
PreparedStatement stmt = connection.prepareStatement();
for (int i = 0; i < parameters.length(); ++i) {
setParameterValue(stmt, i + 1, parameter.get(i));
}
ResultSet rs = stmt.executeQuery(sql);
// use rs to fetch data
setParameterValue should look like this:
void setParameterValue(PreparedStatement ps, int index, Object value) {
if (value instanceof String) {
ps.setString(index, (String)value);
} if (value instanceof Integer) {
ps.setInt(index, (Integer)value);
} // and more boilerplate code like this for all types you need
}
With mybatis you can avoid writing such boilerplate code do generate dynamic sql and make this much easier. But I don't know how CSA treats mybatis generated SQL.
I've found this question while trying to solve similar problem myself.
First, we may factor sql code out of java files and store it in text file under resources folder. Then, from java code, use classloader's method to read sql as inputStream and convert it to String. Storing sql code in separate files will enable statical code analysis.
Second, we can use named parameters in sql in some form that is easily recognizable via regular expressions. E.g. ${namedParam} syntax which is familiar by different expression languages. Then we can write helper method to take this parametrised sql and Map<String, Object> with query params. Keys in this map should correspond to sql parameter names. This helper method would produce PreparedStatement with set parameters. Using named parameters will make sql code more readable and will save us some debugging.
Third, at last, we can use sql comments to mark parts of sql code as dependant on presence of some parameter. And use it in the previously described helper method to include in the resulting Statement only parts, for which entries in parameters Map exist. E.g.: /*${namedParam}[*/ some sql code /*]${namedParam}*/. This would be an unobtrusive way to insert conditions into our dynamic sql.
Following DRY principle, we could also try to employ some existing expression language engine, but it would get us one more dependency and processing expense.
I will post the solution here once I get working code.
I have a database with a bunch of stuff in it, and right now I'm reading in data, doing some processing on it, and then sticking it in a new database. My code generates this string:
query_string = "INSERT INTO OrgPhrase (EXACT_PHRASE,Org_ID) VALUES (HELLO,123)"
Then it's used this way:
Dim InsertCmd = New System.Data.OleDb.OleDbCommand(query_string, connection)
InsertCmd.ExecuteNonQuery()
The associated database (OLEdb connection) exists and opens fine, with all the tables and columns it's trying to work with already existing. The error message I get is "No value given for one or more required parameters"
Am I missing something? Did I spell something wrong? I don't have a ton of experience with database work, but I've never had this trouble inserting before.
I believe the query should be
query_string = "INSERT INTO OrgPhrase (EXACT_PHRASE,Org_ID) VALUES ('HELLO',123)"
Also, it may happen that the table has more than 2 columns that are NOT NUll and the values to them are required.
Consider parameterizing the query string. There are a couple of reasons for this. First, you can pass in the values without having to worry about whether or not you need single quotes. Second, you prevent SQL injection.
query_string = "INSERT INTO OrgPhrase (EXACT_PHRASE,Org_ID) VALUES (#ExactPhrase,#OrgId)"
You then create parametes based on the parameter names in the string. Unless, of course, your query string is always the same values, but that sounds a bit too hardcoded to be good.