Programmatically checking a connection string username/password? - c#

I have a customer provided database which is configured with multiple users. Each user has their own username/password etc, which are database logins for SQL Server.
In legacy software, the user is asked for their database and password which is then used to form a connectionstring in code.
I am tasked with replacing a lot of the database code for this, and I need a way of checking a username and password against the database quite quickly. At present, using Linq, if the password is incorrect I eventually get a System.Data.SqlClient.SqlException but this takes quite a while to actually throw (I assume a timeout?).
Does anybody know a good way that I can test my username/password combo against the database quickly?

You could try opening a connection through ADO, with a reduced timeout
SqlConnection conn = new SqlConnection (yourconnectionstring + ";Connection Timeout=1;");
try
{
conn.Open();
conn.Close();
}
catch (SqlException ex)
{
if (ex.Number == 18456)
{
// invalid login
}
}
18456 is SQL Invalid login
The default connection timeout is 15 seconds.

I would argue that you don't want the database (or anything, really) to be able to tell you that the credentials that you've passed in are incorrect. Why? Because that allows for brute force checking. Even introducing a small delay goes a long way in thwarting such attempts.

Related

Exception thrown: 'System.Data.SqlClient.SqlException' in System.Data.dll when trying to make simple insertion into SQL Server database

I'm new to C# and I have encountered error when trying to create a new user from my C# project and insert into the database. I kept encountering the following error when running the project:
Exception thrown: System.Data.SqlClient.SqlException' in System.Data.dll
but can't seem to find which part of the code went wrong. I have tried googling for similar solutions but have not been of help.
The following is my database and table from SQL Server Management Studio:
The following is the connection string in my web.config file to connect to the SQL Server database:
<connectionStrings>
<add name="con" connectionString="Data Source=XXX-091220MI02\SQLEXPRESS;Initial Catalog=serviceportal;Integrated Security=true"/>
</connectionStrings>
The following is the my code in Visual Studio when trying to insert into database:
protected void Button1_Click(object sender, EventArgs e)
{
try
{
SqlConnection conn = new SqlConnection(strcon);
if(conn.State == ConnectionState.Closed)
{
conn.Open();
}
SqlCommand cmd = new SqlCommand("INSERT INTO user (username, password) VALUES (#user, #pass)", conn);
System.Diagnostics.Debug.WriteLine("Textbox 1 Text: " + TextBox1.Text.Trim());
System.Diagnostics.Debug.WriteLine("Textbox 2 Text: " + TextBox2.Text.Trim());
cmd.Parameters.AddWithValue("#user", TextBox1.Text.Trim());
cmd.Parameters.AddWithValue("#pass", TextBox2.Text.Trim());
cmd.ExecuteNonQuery();
conn.Close();
Response.Write("<script>alert('Sign Up Successful. Redirecting to Login Page ...');</script>");
Response.Redirect("Login.aspx");
}
catch(Exception ex)
{
Response.Write("<script>alert('" + ex.StackTrace +"');</script>");
}
}
Please change the insert statement as 'user' is keywords in database.
INSERT INTO [dbo].[user] (COLUMNS) VALUES();
Please ensure your connection string is correct.
Try using this -
insert into [dbo].[user] (username, password) values (#user, #pass)
USER is a reserved word for sqlserver; if you call your table User you'll have to quote its name when using it. Consider renaming your table to Users, or quoting it ( insert into [user] ). One nice thing about giving tables plural names is they seldom collide with keywords, (though "should table names be singular or plural is a bit of a holy war" ..)
Bad:
Good:
Note: the fact that those images show a create table statement is immaterial; using "user" without quoting it doesn't work in an insert in the same way it doesn't work in a create
Some other points:
consider avoiding AddWithValue on SQLServer - https://www.dbdelta.com/addwithvalue-is-evil/ - I don't care so much about insert statements but it causes problems with select statements
give your textboxes etc proper names. It takes a few seconds to rename a control, do not stick with the default "TextBox1" name- by the time you've added a hundred controls your code will be meaningless and hard to follow
do not store passwords in plain text. Read up on what hashing and salting is, and always always always salt-hash your passwords. Code like this coupled up with human nature of reusing passwords is the reason why password lists and rainbow tables exist and why one hack leads to many. Once someone has hacked your site and scraped all the user's carefully chosen mega secure 16 character random char passwords into a list they can just try them anywhere else to get a win. You can't turn round and say "well it's their fault for reusing a password" - there are two faults, but storing the password in plain text is the bigger one (because it's easier to fix than human nature). If you don't want to store user data securely, hand the problem of authentication/authorisation off to someone else, like auth0 or Microsoft azure AD
if you're learning db interaction, using Dapper or EF might be the better way to go; this is really low level code you're writing- it's a lot of lines/effort for very little overall action. In EF all your code would have looked something like:
db.Users.Add(new User { Username = userTextbox.Text, Password = passTextBox.Text });
db.SaveChanges();
...and it would have worked, because EF knows how to quote table names

Database failed to open - Login failed for User

I'm currently writing code to save information to a database.
I think I'm at now approaching the last hurdle for getting it to work.
My issue is that when I click save I get a message box saying, Cannot open Database [Database file path] requested by the login. The login failed. Login failed for 'User-PC\User'.
However I didn't create any login details for the database as in the future I want anyone who uses the program to be able to log information into it.
I'm not quite sure where to look to solve the issue, but I am currently downloading SSMS to see if I can fix it there?
Any help would be great, thanks!
My code is,
string constring = "Data Source=(LocalDB)\MSSQLLocalDB;Initial Catalog=c: \users\user\documents\visual studio 2015\Projects\LossApplication\LossApplication\LossDB.mdf;Integrated Security=Yes; Trusted_Connection=True; ";
string query = " insert into LossDB.LossTable (lossid,Equipment, Event, responsinility, start) values(#lossid, #equipment, #Cause, #reason, #start) ;";
SqlConnection conLossDB = new SqlConnection(constring);
SqlCommand cmdLossDB = new SqlCommand(query, conLossDB);
cmdLossDB.Parameters.AddWithValue("#lossid", textBox1.Text);
cmdLossDB.Parameters.AddWithValue("#Equipment", comboBox1.Text);
cmdLossDB.Parameters.AddWithValue("#Cause", comboBox2.Text);
cmdLossDB.Parameters.AddWithValue("#Reason", comboBox3.Text);
cmdLossDB.Parameters.AddWithValue("#start", dateTimePicker1.Text);
//Defines which boxes to read in order to input the text from the defined boxes into the corresponding columns
SqlDataReader myReader;
try
{
conLossDB.Open();
myReader = cmdLossDB.ExecuteReader();
MessageBox.Show("Loss Entry Saved");
//Opens the database and carries out the defined command outlined in the code above
while (myReader.Read())
{
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Using Integrated Security means Windows Authentication mode of the sql server, there must be a valid Windows user that has access to the database. If you want to use this mode and give access to all user on the computer, you can configure Windows Users group (to which all users typically belongs to) to have access to your database (see Security->Logins in your database configuration).
Another approach is to create a SQL user and user SQL Server Authentication mode. Then you use this user login and password to get access to the database regardless of Windows user. In this case your connection string will looks like
(LocalDB)\MSSQLLocalDB;Initial Catalog=<path>\LossDB.mdf;User ID=<UserLogin>;Password=<***>;Trusted_Connection=True;

VSTO Addin Connecting to MSSQL

I have a VSTO addin. Right now I am hard coding two dictionaries for development. They need to persist so I am going to use mssql. I have created the tables and all, so everything is done on that end. But I'm having some trouble getting a few things to work so I have a few questions:
First, is it possible to use Windows Forms in VSTO to create a CRUD form for the database tables? There are 2 tables and they aren't too long.
Next, when connecting with SqlConnection, the example connection strings I am seeing don't make any sense. Here is an example: tmpConn = new SqlConnection("server=(local)\\SQLEXPRESS;Trusted_Connection=yes"); I'm not sure how to make the connection string. Right now my db is a local mssql instance, but in the future it will be hosted on a local network server. It says SQLEXPRESS now, is that the same as mssql? If not how can I change it to mssql? How do I construct the correct string for mssql and how do I authenticate? Is it done in the connection string or somewhere else?
Next to Last, is there an easy way to query a database with 3 columns, ID (PK), domain, and dirname. And place domain and dirname into a dict where domain is the key and dirname the value, the ID is uneeded for this.
Here is what I have so far:
public void retrieveClient(SqlConnection conn)
{
try
{
using (conn)
{
SqlCommand command = new SqlCommand(
"SELECT ClientDirName, ClientEmailDomain FROM ClientTable;",
conn);
conn.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
string clientDir = reader.GetString(0);
string clientEmail = reader.GetString(1);
clientDict.Add(clientEmail, clientDir);
}
}
else
{
MessageBox.Show("No rows found in ClientTable", "Rows Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
reader.Close();
}
}
catch (InvalidOperationException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (SqlException ex)
{
MessageBox.Show(String.Format("Exception while accessing ClientTable: {0}", ex), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
And finally, the part where it says string clientDir = reader.GetString(0) and string clientEmail = reader.GetString(1) Will it return the correct values here? The table has 3 values as I said, ID, Dir, and Email. But as you can see my SqlCommand only selects ClientDirName and ClientEmailDomain. Does this mean the GetString() methods will return those with 0 and 1? Or would it be 1 and 2 since ID is 0? I'm not sure how it works, does reader.GetString() base the indexes off the actual table or the SqlCommand?
I also wanted to know if the while (reader.Read()) part will add each row one by one so that my clientDict.Add() would work properly or not.
If it possible to do CRUD with a database with windows forms please give me some guidance or a small example. Other than that I just need my code examined to make sure it's doing what I want. I've been trying to test it but there are a lot of other missing pieces at the moment so I need assistance.
Any assistance would be much appreciated. And if someone could provide a few small example connection strings for both local and network mssql servers that require authentication it would be a huge help. I've been looking everywhere and can't get this working. I've heard EF could do it but I can't find a single example or guide that does what I need. If someone could share one that may help too, if you with EF is the way to go.
Thanks in advance!
You may treat your VSTO based add-in as a regular .Net application. There is no anything specific from the Db perspective. The only difference is that your VSTO assembly is run within the host application (not standalone). So, I'd suggest reading any good book on how to use SQL databases (ADO.NET) in .net applications or use EF (Entity Frameworks) first. It will give you the answer to all your questions.

Best practice to retrieve error message from database

We have list of error messages in our database table and we fetch these error messages from table when we face some business validation error.
for e.g. If in c# code we find that the calculated risk % is more then allowed value, when use below code
string sError = GetErrorText(6610); // get error message from application cache
DisplayErrorPopup(sError ); // load a popup to display error to user.
Now we have found that there are scenarios where we have to validate stuff few rules from stored Procedure. for e.g. "No active supervisor for worker."
My question is how should we handle this scenario when validation happens in database?
A. Should we return error text "No active supervisor for worker." as out param of SP and pass it to DisplayErrorPopup
OR
B. Return the error id (which is present in table) and then use GetErrorText(834) and then pass the text to DisplayErrorPopup;
My concerns are
1. Is there any industry standard to best practice to handle error messages and texts.
2. Is there any drawback of returning string / varchar from database when we have option of returning number.
Please guide me on this.
I would suggest throwing (raising) a custom error in SQL (you could select the text out of a table if you like) and letting your application catch that error. This will allow you to let your application decide how to handle different errors based on how critical they are.
using (SqlConnection conn = new SqlConnection(connection))
{
SqlCommand sqlCommand = new SqlCommand(query, conn);
sqlCommand.CommandTimeout = timeout;
sqlCommand.CommandType = CommandType.Text;
conn.Open();
object result = sqlCommand.ExecuteScalar();
return result;
}
Running the above inside of a try catch block will allow you to handle your errors more elegantly inside of your C# app
Both your options are equally correct.You can either return an error text from database, or return some error code and display error message based on that code in your application.
Also, try this atricle from codeproject as it has both industry standard and best practice to handle error messages and texts explained in detail.

Checking whether a database is available?

My problem involves checking if I have a valid database connection before reading from the database. If the database is down I'd like to write to a xml file instead. I have the location of the database (if it's up) at runtime so if the database was working I can create a new sqlConnection to it.
Use a typical try...catch...finally structure, and based on the specific exception type and message, decide whether you want to write to xml or not.
try
{
SqlConnection connection = new SqlConnection(DB("Your DB Name"));
connection.Open();
}
catch (Exception ex)
{
// check the exception message here, if it's telling you that the db is not available. then
//write to xml file.
WriteToXml();
}
finally
{
connection.Close();
}
I would just use something like:
using(SqlConnection conn = new SqlConnection(c)) {
conn.Open();
}
It will throw an exception if invalid. You could write to the xml in the exception.
An easy way would be to execute a simple query and see if an error occurs:
For Oracle:
SELECT * FROM DUAL
For SQL Server
SELECT 1
Basicly just some kind of relatively "free" query that will let you know that the database is up and running and responding to requests and your connection hasn't timed out.
You cannot really tell whether the DB is up and running without actually opening a connecting to it. But still, connection might be dropped while you're working with it, so this should be accounted for.

Categories