Is it possible to enforce Read Only behaviour with SqlCommand? - c#

Is it possible to enforce read only permissions using the System.Data.SqlClient code accessing a Sql Server database?
I want to allow trusted users to write their own SELECT statements, in a web site.
NO Im not trolling here! Obvious solutions are to create a readonly user in the database, and use those credentials in the connection string, and surely only an idiot accepts a SQL statement in a webpage. This is a user deployment issue, I don't trust someone else to set that up correctly and don't want to write code to check that the readonly connection string is readonly.
One solution would be to parse the SQL and verify that it is a readonly command, or to do something similar. What I want to do is to do something like;
SqlConnection conn = new SqlConnection(myConnectionString, Flags.Readonly)
update
Given a connection string with SA priviledges, "create user blah with password=xxx" "use my-db" "create login blah" "grant select on mytable to blah". Then make a new connection string.

Create a new login in SQL Server and only give that login the permissions you want on the tables. Then in the connection string have the application use that login. You mention this as an obvious solution in your post but I don't see why you wouldn't want to do it this way.

You could use a transaction and always rollback? (but make sure the executed sql doesn't commit)

No, there is no built-in facility for ensuring that end-user actions don't have side effects. While it may be simple in your scenario, a general-purpose implementation of this would be incredibly complex, if not impossible. What if the select statement uses a UDF that has side effects?

create another database, possibly restore the nightly backup, so it a day old. only allow the users to access this database. Then users can't slow down production with their awful queries or really hurt anything if your security fails and an change is made.

Related

C# SQL Library is modifying my queries to fully qualified tables

It is changing my queries and appears to be fully qualifying my tables without me explicitly telling it to. Is there a way to stop it from doing that?
Here is the pertinent information as I see it. Let me know if anything else would be helpful.
We had a SQL Server named serverName. It's been in production for years. It was migrated away from a Windows 2008 Server to a Windows 2012 Server. The new server's name is sql_1234_4321 (not the real name but as terrible)
We have nth number of applications that were hitting the old serverName SQL Server so we took the old server offline and created a DNS entry for serverName that points at the new sql_1234_4321 hoping we wouldn't have to hit the connection strings for all the apps that were hitting the old server.
This worked for the most part except for some C# ASP.NET MVC apps.
They are using System.Data.SqlClient.SqlCommand.
Connection string:
Data Source=serverName;Initial Catalog=USData; Persist Security Info=True; User ID=appUn;Password=appPw
SQL query:
select FirstName from Customers
Code:
using (SqlCommand cmd = new SqlCommand(query, sqlConnection))
{
if (parameters != null)
{
cmd.Parameters.AddRange(parameters.ToArray());
}
var reader = cmd.ExecuteReader();
var results = new List<TType>();
while (reader.Read())
{
results.Add(convert(reader));
}
return results;
}
I get an error:
Could not find server 'serverName' in sys.servers.Verify that the correct server name was specified. If necessary, execute the stored procedure sp_addlinkedserver to add the server to sys.servers.
Why this error? The only time serverName is referenced is in the connection string. My query should just use default namespaces once its on the server. But it appears that my query is being fully qualified at some point in the process as the following:
select FirstName from serverName.USData.dbo.Customers
I added a linked server serverName on the new sql_1234_4321 server that just points back to itself and this seemed to fix the problem. However, this feels absolutely dirty and makes me wonder if it REALLY is doing a cross server query at that point or if its smart enough to say "HEY! we are hitting ourself so don't worry about going out to the network and making this more expensive than it should be" but i doubt it.
I thought about using synonyms but the problem is we have tables with the server name in them. And there may be queries hitting the server with the server name in them so the following would not work:
CREATE SYNONYM serverName FOR sql_1234_4321;
So then it would make sense that I'd have to make a specific synonym for each database on the server:
CREATE SYNONYM serverName.database1 FOR sql_1234_4321.database1;
CREATE SYNONYM serverName.database2 FOR sql_1234_4321.database2;
CREATE SYNONYM serverName.database3 FOR sql_1234_4321.database3;
CREATE SYNONYM serverName.database4 FOR sql_1234_4321.database4;
CREATE SYNONYM serverName.database5 FOR sql_1234_4321.database5;
CREATE SYNONYM serverName.database6 FOR sql_1234_4321.database6;
CREATE SYNONYM serverName.database7 FOR sql_1234_4321.database7;
CREATE SYNONYM serverName.database8 FOR sql_1234_4321.database8;
CREATE SYNONYM serverName.database9 FOR sql_1234_4321.database9;
CREATE SYNONYM serverName.database10 FOR sql_1234_4321.database10;
As you can see, this would be a nightmare to maintain and besides that feels super dirty.
My question is this... At what point is the table name being fully qualified out based on the connection string? Is there a way to prevent that from happening?
David Browne led me to find the issue. My query actually had a view referenced and the view had the reference to the old server. Huge oversight on my part to not notice that. Thanks David

Extremely slow mysql connection establishment only when called from code

I am currently trying to do something that should be simple and straight-forward - connect to a database server, run a query, see if I get anything back and if so send it back to the user. This is the code I'm using to do it:
MySqlDataReader reader = MySqlHeaper.ExecuteReader(connectionString, $"SELECT * FROM table WHERE insertDateTime > '{DateTime.Now.AddSeconds(-1800).ToString("yyyy-MM-ddTHH:mm:ss")}'";
I have also tried this with a MySqlCommand and MySqlConnection object pair, and either way the result is the same - it takes approximately 7100ms to connect to the MySql server. I know that sounds like a problem that should be on ServerFault, but my testing tells me otherwise. When I use the command line MySql client to connect to my database server using exactly the same credentials and run exactly the same query I get my connection established and my data back in nothing flat. I don't know at this stage if it's a server setting or not, but here's what I've tried so far:
Rebooting the server
Restarting the MySQL server
Setting the skip_name_resolve setting to 1 in order to prevent reverse name lookups on connect
Using alternative means of querying the server (mysql command line client and MySQL Workbench)
Opening all AWS IAM permissions on the RDS instance to allow everything from the server
Nothing seems to be making any difference, so I'm at a loss to explain this terrible performance. It's also only happening when I open the connection. Running queries, inserts, what have you is lightning fast. Any suggestions anyone might have would be most helpful.
I would not expect IAM permissions to have any impact on performance. I would expect them to be either successful or not successful.
I would execute some diagnostic protocols to get more information.
1) Try a subsequent query, to see if it is an issue with the stack being initialized. Are subsequent queries faster?
2) Try a query that is just an identity query. Something that doesn't require any sort of IO.
3) Try a query from a different platform (maybe a scripting language like ruby or php)
Once you answer those it should help you narrow it down.
This is most likely caused by Connector/NET executing a slow WMI query to query connection attributes when opening the connection; this is logged as MySQL bug 80030.
As far as I know, this isn't fixed in newer versions of the driver, but you can work around it by switching to MySqlConnector, an OSS MySQL ADO.NET library.

SQL Database connectionstring for the users in the network

I have developed a WindowsForm C# application which is connected to the SQL database with the following ConnectionString style:ยจ
ConnectionString = "Data Source=23PC\\SQLExpress;Initial Catalog=DB;User ID=USER1;Password=***;Integrated Security=false;";
And it works perfect for me and the users on the network. I wonder how I can change the CS to make it available for Windows Authentication login rather than Mixed Mode, i.e. without determining U and P.
This is the connection string I always use:
String.Format("Data Source={0};Initial Catalog={1};Integrated Security=True", myDBServerName, myDBName)
This will require that you configure your SQL Server permissions accordingly so that your Windows users have proper logins and access to the database.
Between this question and the corresponding DBA.SE question, there is a tremendous amount of ambiguity here. It's still not very clear what you're actually asking, but you have something of a question in this "I wonder how I can change the CS to make it available for Windows Authentication login rather than Mixed Mode ..."
To address that: Mixed Mode includes Windows Authentication and, as such, requires proper permissions management. I try to guide this sort of permissions management toward Active Directory Groups. You can add groups the same way that you add users. This is covered here. From there, you can set permissions by group (keeping in mind that DENY trumps all other permissions).
To check if Mixed Mode is enabled (and to enable if necessary), check out this answer.
Please do not upvote if useful. The reality is that this question is a dupe of something already on the site, it's just a matter of figuring out what that dupe may be.

Access denied for user 'root'#'localhost' (using password: YES) in Visual Studio

Kindly bear with me. I am a Microsoft SQL Server person with loads of Visual Studio experience, but I need to get something done using a MySQL database.
I am trying to create a little tool here that will allow our developers to quickly update database records, and I am using Visual Studio to create a small Windows Form to do this.
In a Microsoft SQL Server connection string, I could write something like this:
Server=myServerAddress;Database=myDataBase;User Id=username;Password=password;
In a MySQL connection string, there appear to be multiple other options, but the first one looks basically the same:
Server=myServerAddress;Database=myDataBase;Uid=username;Pwd=password;
When I attempt to open the MySQL connection from my PC, I get the exception listed in the title (actually, it shows the Uid value and the IP Address of my PC instead of localhost, but I am hoping more people will recognize the error easier this way):
public static void MySQLi_Connect() {
m_err = null;
var str = Properties.Settings.Default.ConnStr;
try {
m_conn = new MySqlConnection(Properties.Settings.Default.ConnStr);
m_conn.Open();
} catch (MySqlException err) {
ErrorLog("MySQLi_Connect", err);
}
}
I did a search, and it seems that the Uid on MySQL needs to be granted access from the specific IP Address that the connection is being made from.
Further, I found this on the mysql.com doc pages:
If you do not know the IP address or host name of the machine from which you are connecting, you should put a row with '%' as the Host column value in the user table. After trying to connect from the client machine, use a SELECT USER() query to see how you really did connect. Then change the '%' in the user table row to the actual host name that shows up in the log. Otherwise, your system is left insecure because it permits connections from any host for the given user name.
A few things:
It looks like I can connect to MySQL by using a % setting in the Uid jp2code, but MySQL says I need to change that back right away to remove system vulnerability.
Microsoft SQL Server did not seem to require this - or, if it did, I simply never was slapped in the face with this vulnerability issue like MySQL is doing.
Now, I ask:
If this is going to be a tool used by different developers on different PCs, is it common practice to turn the blind eye to this horrendous system vulnerability?
Is this not really as big of a concern as MySQL is making it appear?
What is the best way to continue with a Windows Forms application that needs to connect from various locations? Obviously, I do not want to continuously be adding more entries for a particular application every time another developer wants to use the tool or someone tries to run it from a different PC.
You can configure the security of your MySQL server as strong as you like, usually you dont connect users but applications. So if you have your root user without password in production environment is your fault. Usually developers have access to development environment, so this is not a big deal.
Of course try to have as many users as roles you need, for your example I think one user is enough. In production use a secure config file for save a secure password and set you mysqlserver restricted.
I was having the same issue and I found out that the password wasn't correct.
GO to your sql command line and type the code below:
mydb in the line below is the name of the database you are working on.
passwd in the line has to match the password you have in c# code so in your case "password"
grant all privileges on mydb.* to myuser#localhost identified by 'passwd';
Like OP says you can wildcard the hostname portion. I used this on our dev-server (not recommended for production servers):
update mysql.user set host = '%' where host='localhost';
Then I had to restart the server to make MySQL use it (propably I could just have restarted the MySQL service).

How to connect to different databases?

I'm currently developing an application based on ASP.NET MVC3, SQL Server 2008 and EF with database first.
My application requires every user to have it's own SQL Server database. These databases all have an identical structure.
My customers are identified with a customercode, e.g. fgt. This code is provided in the url.
I need to know how can I retrieve the customercode from the url and set the connection string accordingly.
Thanks for the help
My idea is to connect to the database once the customercode is retrieved from the URL and then prompt to user to enter his username and password for access data.
But yes, is a good idea to create a database to store the connection string of each customer.
Can anyone write the code that I need for do this please?. I am new to asp. I come from php.
(I'm just learning English. Sorry about the bad grammar)
Try something like this to get started:
string customerCode = Request.QueryString["cust"].ToString();
string myNewConnString = ConfigurationManager
.ConnectionStrings["MyDatabase"]
.ConnectionString
.Replace("[placeholder]", customerCode);
Where your connection string in your .config is something like this. Note that I've assumed you'll place a token to be replaced ([placeholder]).
<add name="MyDatabase"
connectionString="Data Source=192.168.0.1;Initial Catalog=[placeholder];User ID=foo;Password=bar"
providerName="System.Data.SqlClient" />
Suggest that you whitelist your customers and their connection strings.
setup a web service on your side.
your deployed application calls your web service using the customer code.
web service validates the customer code, and returns a valid conn string.
customer's app keeps the conn string in memory (session, cache, whathaveyou).
This would allow you to ensure the conn string is always valid for a given customer code. You'd have fine grain control on access to the database for any reason (non-payment, etc). Your service would have a few hits thanks to any caching that you build in.
maybe sqlshard could help you in using multiple databases?
http://enzosqlshard.codeplex.com/
Sounds like pretty insecure solution. What if customer put another customer code in URL? Where is validated if customer can access that code if you don't have any central database with customer's permissions?
You need authentication and some central store (additional database) where you will validate that user accessing the application has access permissions to provided URL. It will also validate if such database even exists. Next you need just SqlConnectionStringBuilder and use the customer code as a name of database (or part of the name). For security reason each database should have a separate SQL account with permissions to read only from that database. This db account can also be stored with that central storage with encrypted passwords.
There can be additional complexities if you also expect dynamical adding or removing customer codes = databases. That would require high privileged account to manage logins, accounts, databases, etc.
Because you are asking how to get part of Uri it looks like you have almost no experience with ASP.NET MVC and perhaps with everything related. You should definitely ask any more skilled colleague or boss to review your architecture because at this point it looks like you are going to have serious problems and you can develop very insecure application which can significantly harm reputation of your company and your customer.

Categories