How to search "Polish" text from SQL Server using C#? - c#

I have to search a Polish text from SQL database in my windows application. In the SQL Database, the field is in Polish language.
Eg: The DB fields are Id, Name and values are 1 , Ąćłń.
I have a textbox for entering searching text. When I enter the textbox value "Ąćłń", now the result is empty. The select query is working fine with English characters. My question is how can I search a Polish text?

This should not be any different than searching English or any other language text....
connect to your database
fire off a parametrized search query, making sure to use nvarchar as your parameter type
show the results!
So basically, something a bit like:
public List<object> Search(string searchArgument)
{
string searchQry = "SELECT (list of columns) FROM dbo.YourTableName WHERE Name = #SearchArg;";
// Define connection and command to use
using (SqlConnection conn = new SqlConnection("....."))
using (SqlCommand cmdSearch = new SqlCommand(searchQry, conn))
{
// add parameter to search command - make sure to define NVarChar and a valid length!
cmdSearch.Parameters.Add("#SearchArg", SqlDbType.NVarChar, 50);
// set parameter value
cmdSearch.Parameters["#SearchArg"].Value = searchArgument;
// open connection, execute query, handle results
conn.Open();
using (SqlDataReader rdr = cmdSearch.ExecuteReader())
{
// loop over rows returned
while (rdr.Read())
{
// do something with results - create your objects that you want to return...
}
// close reader
rdr.Close();
}
// close connection
conn.Close();
}
}
And using an OR Mapper like Entity Framework would make this even easier still.

Alter fulltext catalog CATALOGNAME
REBUILD WITH ACCENT_SENSITIVITY=OFF;
Or specify an accent-sensitive collation in the query. For example:
select *
from users
where name like 'HELEN%' collate SQL_Latin1_General_CP1_CI_AI
If you don't have fulltext catalog (which you should) you can configure the database so that it is accent-sensitive; change the collation to SQL_Latin1_General_CP1_CI_AI.
Options 1 and 2 are simplest from a database administrator's perspective
Option 3 is simplest from a programmer's perspective
Other collation related tips
Version of SQL Server via Select ##Version
SQL Server collation via SELECT CONVERT (varchar, SERVERPROPERTY('collation'));
Table datatype definition of your Polish text (i.e. Nvarchar or varchar)
The ideal polish Collation is case insensitive SQL_Polish_Cp1250_CI_AS_KI_WI
Patch your SQL Server to the most recent level
Default SQL Server collation is SQL_Latin1_General_CP1_CI_AS
Review this Article
WITH ACCENT_SENSITIVITY = {ON|OFF}
Specifies if the catalog to be altered is accent-sensitive or accent-insensitive for full-text indexing and querying.
Notice here:
select * from member where name like 'müller' COLLATE German_PhoneBook_CI_AI
it will find "müller", "mueller", and "Müller"
and
SELECT * FROM mytest WHERE SOUNDEX (col1 )=SOUNDEX ('Muller')
it will find "müller", "mueller", and "Müller"

Related

How to generate a SQL Command string that is safe from SQL Injection?

I have a situation where I have to take input from a user, create a SQL command and send that command to a service that will execute the SQL. The service ONLY allows for a SQL string -- not additional parameters; so I am forced to create the entire SQL statement on my end of things.
I do not have any kind of access to the database itself -- only a service that sits overtop of it.
I realize the following is NOT safe:
var sql = $"SELECT * FROM tablename WHERE name = '{incomingdata.searchName}'";
But if I generate SQL with parameters, would this be safe from SQL injection?
var sql = $#"
DECLARE #Name varchar(50);
SET #Name = '{incomingdata.searchName}';
SELECT * FROM tablename WHERE name = #Name";
This is not an ideal situation. I would try and look for a parametrized way to solve this problem failing that I would test the input and in ANY case where a test fails not allow the query at all and ask the user to re-enter.
Do the following tests:
Length of input is smaller than a max name size (25 characters?)
All input characters are in the alphabet
No reserved SQL words (easy to find with a google search)
If the input does not fail any of these tests you should be OK. DON'T try to sanitize the input -- this can be hard/impossible to do with international character sets.
Disclosure: My background is C++, Java and TypeScript/JavaScript, not C#
Would this be more appropriate:
SqlCommand sql = new SqlCommand("SELECT * FROM tablename WHERE name = #Name");
sql.Parameters.Add("#Name", SqlDbType.VarChar, 50).Value = incomingdata.searchName);
Sanitizing the user data before it gets to this stage, using a trusted package, might also be helpful.
The second example is better, but still not fully secure.
SQL injection attacks can still occur if the input data is maliciously crafted, regardless of whether the input data is directly embedded into the string or used as a parameter.
A more secure way to handle this situation is to use parameterized queries, which automatically escape any special characters in the input data and prevent SQL injection attacks.
Unfortunately, if the service you are using only accepts raw SQL strings and does not support parameters, your options are limited. In this case, it is recommended to validate and sanitize the input data before constructing the SQL string to minimize the risk of SQL injection.
To make the SQL statement safe from SQL injection, you can use parameterized queries. In .NET, you can use the SqlCommand.Parameters property to define parameters and their values. The following is an example:
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "SELECT * FROM tablename WHERE name = #Name";
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("#Name", incomingdata.searchName);
using (SqlDataReader reader = command.ExecuteReader())
{
// process the results
}
}
}
In this example, the value of incomingdata.searchName is passed as a separate parameter and not directly concatenated into the SQL string. This protects against SQL injection because the parameter value is automatically escaped and properly formatted by the .NET framework.
You are along the right lines. You never want to use a variable that can be changed. Instead, you need to use SQL parameters. You can add a SQL parameter like this:
command.Parameters.Add(new SqlParameter("#Name", "incomingdata.searchName"));
And then refer to it in your query like this:
SELECT *
FROM tablename
WHERE name = #Name";
Once this is done, when a user tries to change the value of a variable the query will return no results. It should be said that this way of doing it does result in the SQL property assuming the type of the C# variable. There are other ways of doing this if you want to specify the type of the property to be different from the variables type. This is a useful resource https://jonathancrozier.com/blog/preventing-sql-injection-in-c-sharp-applications

What's causing this syntax error? oledb INSERT statement

I posted about a different issue I had earlier, and with some help resolved that.
I am now receiving a syntax error centered around the following code:
importConnection.Open();
Console.WriteLine("Foxpro connection open");
OleDbCommand deleteOleDbCommand = new OleDbCommand(#"TRUNCATE TABLE CLIENT",
importConnection);
Console.WriteLine("writing to table");
Console.ReadKey();
using (OleDbCommand importCommand = new OleDbCommand(
string.Format("INSERT INTO CLIENT (Title,Fname,Sname)" + "VALUES ({0},{1},{2})",
exportReader.GetValue(0), exportReader.GetValue(1), exportReader.GetValue(2))))
importCommand.ExecuteReader();
With this being the output:
Output Text
using break points I have determined that the export reader calls are receiving data. I've narrowed it down to an issue with:
"VALUES ({0},{1},{2})",
exportReader.GetValue(0), exportReader.GetValue(1), exportReader.GetValue(2))))
I have confirmed that data can be inserted by doing the following:
using (OleDbCommand importCommand =
new OleDbCommand(string.Format(
"INSERT INTO CLIENT (Title,Fname,Sname)" + "VALUES (Mr,Joshua,Cameron-Mackintosh)",
importConnection)))
This causes no problems, so I know the issue does not lie with the underlying connection or command.
Others correctly comment about SQL-Injection, however VFP is less impacted to SQL-Injection, it can be just the same. Main reason, VFP doesn't really work with multiple queries the same way other sql engines allow by a ";" identifying break between statements. However, with mismatched quotes, it CAN and WILL break your sql-statements from actually running.
Having said that, VFP OleDb provider does allow parameterizing, but does so without "named" parameters. It does it with "?" as a place-holder for where the value would be inserted by the .net framework, and you don't have to convert the data type as long as it is in the same expected format (ex: string, numeric, date)
change your OleDbCommand to
"INSERT INTO CLIENT (Title, Fname, Sname ) values ( ?, ?, ? )"
Then, set your parameters via
importCommand.Parameters.Add( "parmForTitle", exportReader.GetValue(0));
importCommand.Parameters.Add( "parmForFName", exportReader.GetValue(1));
importCommand.Parameters.Add( "parmForSName", exportReader.GetValue(2));
Also, the parameters must be added in the exact same sequential order as they appear in the query. So, I prefixed them with "parmFor" to indicate it is the parameter placement for the corresponding field being inserted (or updated, or used in select, insert or delete too). The command objects work the same for all the commands. Even if you write a select statement and have values in a WHERE, JOIN or whatever other position.
THEN, ExecuteNonQuery()
It is saying "foxpro connection string" there. If it is done against
a VFP database, then "Truncate table client" wouldn't work in the
first place. That command does not exist in VFP. Instead you could
try using "Delete From Client" which marks the records for deletion.
Or you can use "zap" command with ExecSript that would correspond to
"truncate table" but then the connection needs to use the table
exclusively.
You should quote the string values. Better yet, for any SQL operation you should use parameters. When you use parameters, you should do that in a correct way for the connection you use. Here you are using an OLEDB connection, then you should use ? as a parameter place holder.
A revised version of your code would then be:
importConnection.Open();
Console.WriteLine("Foxpro connection open");
OleDbCommand deleteOleDbCommand = new OleDbCommand(#"Delete from CLIENT",
importConnection);
Console.WriteLine("writing to table");
Console.ReadKey();
using (OleDbCommand importCommand = new OleDbCommand(
#"INSERT INTO CLIENT (Title,Fname,Sname) VALUES (?,?,?)",
importConnection))
{
importCommand.Parameters.AddWithValue("title","");
importCommand.Parameters.AddWithValue("fname","");
importCommand.Parameters.AddWithValue("sname","");
// maybe in a loop here
importCommand.Parameters["title"].Value = exportReader.GetValue(0);
importCommand.Parameters["fname"].Value = exportReader.GetValue(1);
importCommand.Parameters["sname"].Value = exportReader.GetValue(2);
importCommand.ExecuteNonQuery();
// ...
}
PS: You could directly feed the values on the Parameters.AddWithValue instead of creating a separate .Parameters["..."].Value = ... but then you would only be able to do that for a single insertion (not something related to VFP, it is the case for OleDb or Sql or whatever).
You don't need ExecuteReader for an insert statement. Just use ExecuteNonQuery.
In your case, if your columns are character typed, you need to use single quotes with them for example;
VALUES ('{0}', '{1}', '{2}')
Also use white space (not have to but as a good practice) before VALUES part.
"INSERT INTO CLIENT (Title,Fname,Sname)" + " VALUES (Mr,Joshua,Cameron-Mackintosh)",
// ^^^ here
But more important;
You should always use parameterized queries. Prepared statements automatically handle for escape characters for example and this kind of string concatenations are open for SQL Injection attacks.
You must give space to your concatenated strings.
"INSERT INTO CLIENT (Title,Fname,Sname)" + "[space here]VALUES (Mr,Joshua,Cameron-Mackintosh)"
however, it should look like this:
"INSERT INTO CLIENT (Title,Fname,Sname) VALUES (?,?,?)"
Always make use of parametrized queries. Please refer to:
http://blogs.technet.com/b/neilcar/archive/2008/05/21/sql-injection-mitigation-using-parameterized-queries.aspx

Why do I receive an incorrect syntax error when try to parameterize a table name?

I have the following code:
string strTruncateTable = "TRUNCATE TABLE #TableNameTruncate";
SqlCommand truncateTable = new SqlCommand(strTruncateTable, myConnection);
truncateTable.Parameters.AddWithValue("TableNameTruncate", tbTableName.Text);
truncateTable.ExecuteNonQuery();
Whenever I run the application, I get the following error:
Incorrect syntax near '#TableNameTruncate'
How can I fix the issue?
How can I fix the issue?
By specifying the table name as part of the SQL. Table and column names can't be parameterized in most database SQL dialects, including SQL Server.
You should either perform very stringent validation on the table name before putting it into the SQL, or have a whitelisted set of valid table names, in order to avoid SQL injection attacks in the normal way.
You can only parameterized your values, not your column names or table names no matter you use DML statements or DDL statements.
And by the way, parameters are supported for Data manipulation language operations not Data Manipulation language operations.
Data manipulation language =
SELECT ... FROM ... WHERE ...
INSERT INTO ... VALUES ...
UPDATE ... SET ... WHERE ...
DELETE FROM ... WHERE ...
TRUNCATE TABLE is a Data Definition Language statement. That's why you can't use TRUNCATE TABLE with parameters even only if you try to parameter a value. You need to specify it as a part of SQL query.
You might need to take a look at the term called Dynamic SQL
As mentioned by Jon Skeet, table name cannot be parametrized for truncate operation.
To fix this issue, fully qualified query needed to be written.
So you can put a conditional check by the parameter value #TableNameTruncate and using if or switch case statement create fully qualified query then execute it.
or simply
string strTruncateTable = "TRUNCATE TABLE " + TableNameTruncate.Value;
SqlCommand truncateTable = new SqlCommand(strTruncateTable, myConnection);
truncateTable.Parameters.AddWithValue("TableNameTruncate", tbTableName.Text);
truncateTable.ExecuteNonQuery();

Querying a database for username and password

I'm totally new to C#. I am validating username and password with case-sensitive
from database sql server 2008. Here is my login code; what should I do?
SqlConnection conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;Initial Catalog=db_WiBo;Integrated Security=True");
SqlCommand cmd = new SqlCommand("SELECT * from tb_Account where Username= '"+textBox1.Text+"' AND Password= '"+textBox2.Text+"' ", conn);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader()
if (reader.HasRows)
{
reader.Close();
this.Hide();
}
Thanks in advance
1) you should be using stored procedures or parameterised query instead of SQL Concatenation. you are presenting a huge SQL Server injection security flaw in your code already.
what is the problem you are having? code looks fine - are you sure the inputs match the data in the database if there are no records being retrieved?
the code simply executes the query and if found, closes the reader and hides the form.
you should also not store raw passwords in the DB - another security flaw. instead, hash them/encrypt them and check that value against the hashed/encrypted value from the input.
for case sensitivity, you could use Collation
http://technet.microsoft.com/en-us/library/ms184391.aspx
http://blog.sqlauthority.com/2007/04/30/case-sensitive-sql-query-search/
A few things:
You are missing a ';' at the end of SqlDataReader reader = cmd.ExecuteReader().
As was already mentioned, use a Stored Procedure for what you are doing.
As was also mentioned, use parameterized queries since you are accepting input from textboxes.
Use a using statement to manage your connection and command objects.
Don't forget to .Close() your connection when you are done.
Oh, and I almost forgot, hash your passwords!
Here is an example of the above:
using (SqlConnection conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;Initial Catalog=db_WiBo;Integrated Security=True"))
{
using (SqlCommand cmd = new SqlCommand(#"SELECT * from tb_Account where Username= '#username' AND Password= '#password' ", conn))
{
cmd.Parameters.Add("#username", textBox1.Text);
cmd.Parameters.Add("#password", textBox2.Text);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
reader.Close();
this.Hide();
}
conn.Close();
}
}
Other than your SQL injection issues, you need to use a collation or cast to binary. I found another SO question that has a bunch of helpful things:
How to do a case sensitive search in WHERE clause (I'm using SQL Server)?
First of all: Do not use string concat for that. This can easily be used for code injection.
You can use stored procedures instead, or LinQ to SQL or even use the built-in membershipprovider
Notwithstanding the design issues already pointed out with respect to security, if your SQL Server instance's default collation is case-insensitive, you'll need to select a suitable collation. The default collation for a SQL Server installation is [usually, depending on the server locale] SQL_Latin1_General_Cp1_CI_AS, meaning Latin-1 code page, case-insensitive, accent-sensitive.
When you create a database, you can specify a default collation for that database. And when you create a table, you may specify the collation to be used for each char, varchar, nchar or nvarchar column.
You may also change these via appropriate DDL statements. Note that altering the collatino may affect data integrity, causing things like primary keys and unique indices to be broken.
Create a table with and specifying the collation for its columns is easy:
create table account
(
id int not null identity(1,1) primary key clustered ,
user_id varchar(32) collate SQL_Latin1_General_Cp1_CS_AS not null unique ,
password varchar(32) collate SQL_Latin1_General_Cp1_CS_AS not null unique ,
...
)
You may use alter table and alter database to change the collations as well.
Supported collations can be found at http://technet.microsoft.com/en-us/library/ms180175.aspx and http://technet.microsoft.com/en-us/library/ms188046.aspx
You should
Note that mixed collations can cause problems when comparing 2 columns, and
Heed the security issues raised by others.

How to protect user specified table name from SQL Injection in C#, using MySQL

As a reaction to MySqlParameter as TableName, it seems that MySQL's Connector/NET library doesn't support table names from user input in the following way:
MySqlCommand cmd = new MySqlCommand("SELECT * FROM #table");
cmd.Parameters.AddWithValue("#table",TableNameFromUserInput);
So I tried to find another way, but I couldn't find any libraries that did this for me.
When searching for how to do this manually, I couldn't found anything that didn't tell you to use "prepared statements" from some already defined library, no matter the language.
To my limited knowledge of SQL, I only think it's necessary to put backticks (`) around the user input and then check the input for backticks - but I wanted to be sure.
Question
How do you protect a user specified table name from SQL-Injection when the MySQL Connector doesn't support it?
Check if
TableNameFromUserInput
is an existing table, before executing the query.
That prevents other errors like "table doesnt exist" aswell.
you cannot add tableName (as well as columnName) as parameter in prepared statements because it only supports for a value. For your safety, add additional code to validate tableName, ex.
string tableName = "hello";
tableName = UDFunctionClean(tableName);
string query = String.Format("SELECT * FROM `{0}`", tableName);
MySqlCommand cmd = new MySqlCommand(query);

Categories