I have some scenarios where I need to have multiple calls to .SubmitChanges() on a datacontext, but I want to explicitly control the transaction myself to make it atomic. For a while I have been doing this by creating a connection, creating a transaction on that connection, then creating the datacontext and passing it both. Lets assume for now I dont want to use TransactionScope instead. My code looks like this:
Using conn As New SqlConnection("connection string...")
conn.Open()
Using trans = conn.BeginTransaction()
Dim dc as new DataContext(conn)
dc.Transaction = trans
' do some work
trans.Commit()
End Using
End Using
I began using the Linq To SQL profiler and it breaks this code. For some reason they require you to use the .Connection property on the datacontext to create the transaction. It fails if you use the connection variable directly (which I think is silly). My question is, is it more appropriate to do it this way:
Using conn As New SqlConnection("connection string...")
conn.Open()
Dim dc as new DataContext(conn)
Using trans = dc.Connection.BeginTransaction()
dc.Transaction = trans
' do some work
trans.Commit()
End Using
End Using
Which is the more widely accepted way to do this?
The second snippet doesn't seem appropriate to me. With the second snippet you need to create the transaction after creating the context, which is -at least- from a readability / maintainability perspective less useful. I try to imagine how your code would look when you need to create two DataContext classes, and create the transaction (only) after creating the first context. This makes it pretty hard to keep clean separated code.
I think you should send a mail to Hibernating Rhinos and ask if they fix this bug.
Product prod = db.Products.Single(p => p.ProductID == 15);
if (prod.UnitsInStock > 0)
prod.UnitsInStock--;
using(TransactionScope ts = new TransactionScope()) {
db.SubmitChanges();
ts.Complete();
}
http://msdn.microsoft.com/en-us/library/bb425822.aspx
use TransactionScope
http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx
http://www.codeproject.com/KB/dotnet/TransactionScope20.aspx
Related
I am the maintainer (but thankfully not the creator) of a very old, very large and very badly written classic ASP site for an electronics manufacturer.
Security is a joke. This is the only thing done to sanitize input before throwing it into the mouth of MySQL:
Function txtval(data)
txtval = replace(data,"'","'")
txtval = trim(txtval)
End Function
productid = txtval(Request.QueryString("id"))
SQL = "SELECT * FROM products WHERE id = " & productid
Set rs = conn.execute(SQL)
Because of that, the site is unfortunately (but perhaps not surprisingly) victim of SQL injection attacks, some of them succesful.
The simple means taken above is not nearly enough. Nor is using Server.HTMLEncode. Escaping slashes doesn't help either as the attacks are quite sophisticated:
product.asp?id=999999.9+UnIoN+AlL+SeLeCt+0x393133353134353632312e39,0x393133353134353632322e39,0x393133353134353632332e39,0x393133353134353632342e39,0x393133353134353632352e39,0x393133353134353632362e39
The url above (an arbitrary attempt taken from the access log) gives the folling response from the site:
Microsoft OLE DB Provider for ODBC Drivers error '80004005'
[MySQL][ODBC 5.3(w) Driver][mysqld-5.1.42-community]
The used SELECT statements have a different number of columns
/product.asp, line 14
This means that the injection made it through but in this case did not succeed in getting any data. Others do, however.
The site consists of hundreds of ASP files with spaghetti code summing up to many thousands of lines without much structure. Because of that it is not an option to go for parameterized queries. The work would be enormous and error prone as well.
One good thing though is that all input parameters in the code are consistently passed through the txtval function, so here is a chance to do it better by augmenting the function. Also, since all SQL calls are done with conn.execute(SQL) it is quite straightforward to search and replace with eg. conn.execute(sanitize(SQL)) so here is a chance to do something about it too.
Given the circumstances, what are my options to prevent or at least minimize the risc of SQL injection?
Any input is much appreciated.
Updates:
1.
I do understand that parameterized queries is the correct way to handle the problem. I use that myself when I create websites. But given the way the site is built and the size of it, it will take 1-2 months to modify, test and debug it. Even if that is what we end up with (which I doubt) I need to do something right now.
2.
The replacement with the html entity is not a typo. It replaces single quote with its html entity. (I didn't make the code!)
3.
In the specific example above, using CInt(id) would solve the problem, but it could be anything, not only numerical inputs.
UPDATE 2:
Ok, I know that I am not asking for the correct solution. I knew that from the start. That's why I wrote "Given the circumstances".
But still, filtering inputs for mysql keywords like select, union etc would at least make it better. Not good, but a little bit better. And this is what I am asking for, ideas to make it a little bit better.
Although I appreciate your comments, telling me that the only good option is to use parameterized queries doesn't really help. Because I know that already :)
I wouldn't give up on parameterized queries. They are the single best tool you can use to protect yourself from SQL Injection. If your plan is to replace all of these calls:
conn.execute(SQL)
to these calls:
conn.execute(sanitize(SQL))
then you're already looking at modifying each interaction with SQL (BTW, don't forget Command.Execute() and Recordset.Open(), which may also be used to run SQL statements). And since you're already planning on changing these calls, consider calling a custom function to run the statement. For example, replace:
set rs = conn.execute(SQL)
with:
set rs = MyExecute(SQL)
and then use your custom function to set up a proper parameterized query using a Command object instead. You'll need to cleverly parse the SQL statement in this custom function. Identify the values in the where clause, determine their type (perhaps you can query the table schema), and add parameters accordingly. But it can be done.
You can also take this opportunity to sanitize the input. Use a RegExp object to quickly strip [^0-9\.] from numeric fields, for example.
But there's still the opportunity that you'll return a recordset from this function that will be used to write values directly to the page without being HTML-encoded first. That's a real concern, especially since it sounds like your site has already been targeted in the past. I wouldn't trust any data coming from your database. The only option I see here (that wouldn't involve touching every page) is to return a "clean", HTML-encoded recordset instead of the default one.
Unfortunately, you're still not out of the woods. XSS attacks can be done via QueryString parameters, cookies, and form controls. How safe are you going to feel after "fixing" the SQL Injection issues knowing that XSS is still a very real possibility?
My advice? Explain to your supervisor the security threats plaguing your site and convince him/her the need for a thorough review or a complete rewrite. It may seem like a lot of resources to throw at an "old, already-working website", but the moment someone defaces your website or truncates your database tables, you'll wish you invested the time.
This attack should only affect numeric values passed in your SQL.
There may or may not be a quick fix depending on whether the same txtval function is used for both numeric and string values (and others like date too).
If txtval is only used for numeric values (probably unlikely) then you could protected by adding single quotes around the value, eg:
Function txtval(data)
txtval = replace(data,"'","'")
txtval = "'" & trim(txtval) & "'"
End Function
If it is used for all value types then your only option might be to search through all the code and either:
1) Add single quotes to all numeric SQL, eg:
SQL = "SELECT * FROM products WHERE id = '" & productid & "'"
2) Create a new function just for sanitizing number values and then change all your queries to use that (not a quick fix), eg:
Function numval(data)
If IsNumeric(data) Then
numvalue = CDbl(data)
Else
numvalue = 0 'or NULL?
End If
End Function
And then change your queries, eg:
productid = numval(Request.QueryString("id"))
SQL = "SELECT * FROM products WHERE id = " & productid
Is there common code (ie. in an include file) that is used to open the database and create the conn variable used in your sample code?
If so, then you could just replace that code and create your own class with Open, Close and Execute functions (at least). You may need other methods too if they are used in your code.
That way you could effectively override the execute in lines like Set rs = conn.execute(SQL).
Eg:
Class MyDatabase
Private m_conn
Public Sub Open(connString)
Set m_conn = Server.CreateObject("ADODB.Connection")
m_conn.Open connString
End Sub
Public Sub Close()
m_conn.Close
Set m_conn = Nothing
End Sub
Public Function Execute(sql)
'Sanitize input here (sql), simple example just for this type of attack
If InStr(sql, "UnIoN AlL SeLeCt") <> 0 Then sql = ""
'return a RecordSet
Set Execute = m_conn.Execute(sql)
End Property
End Class
Then change your common conn declaration from... (eg)
Set conn = Server.CreateObject("ADODB.Connection")
...to...
Set conn = New MyDatabase
If you keep the txtval function I would also update it to escape slashes as well as single quotes, eg:
Function txtval(data)
txtval = Replace(Replace(strValue, "'", "''"), "\", "\\")
txtval = trim(txtval)
End Function
Hopefully something here might be of help.
I have a program written in VB.NET that as part of its function requires the use of a number of database classes.
At the moment the classes are programmed specifically to use objects originating from System.Data.SqlClient and classes such as SqlConnection, SqlCommand, SqlParameter and SqlDataAdapter are used.
My aim is to use the analogous classes from Mysql.Data.MySqlClient (obtained via the Connector/Net download on the MySQL site). These for example would be: MySqlConnection, MySqlCommand, MySqlParameter and MySqlDataAdapter.
Is there some way in the code that I could maybe specify an abstract version of the classes (something like AbstractSqlCommand, AbstractSqlParameter) and be able to pick the correct implementation between SqlCommand and MySqlCommand based on the use of some other config variable.
Dim command As New AbsSqlCommand(sql, connection)
For Each p As AbsSqlParameter In param
command.Parameters.Add(p)
Next
Dim timeout As Integer = 3000
command.CommandTimeout = timeout
Try
connection.Open()
Catch
Throw New Exception("Connection failed")
End Try
Dim Adapter As New AbsSqlDataAdapter(command)
Adapter.Fill(table)
Return table
So in the case above some kind of global or configuration variable could be ussed to differentiate between whether AbsSqlCommand is actually used as a MySqlCommand or a SqlCommand [MSSQL] without the need for having to recode every instantiation of these objects to suit the particular database platform.
This is really a broad question that will be best answered by a full article like this, but look at
System.Data.Common Namespace
The classes in System.Data.Common are intended to give developers a
way to write ADO.NET code that will work against all .NET Framework
data providers.
Well you could have two linq to sql instances (there is no constraint on the number of instances and calsses there in), but I have no experince of using linq2sql with MySQL so I dont know how well it works. Id be inclined to set up a test project add a linq2sql setofdatatclesses and try to connect to a MySQL database, see what happens)
I'm creating an application which allows you to manage various data. The application is designed to work in a network, and thus in multi-user. For this reason I decided to trust the Datatable.
I have a class created by me for the management of operations MYSQL Database but now I still can not create a streamlined process to send the datatable to MySQL database.
Currently I am so
Dim SQLStm As String
'variable for sql query
Dim SQLManager As New ER.DB.ERMysql
For Each Riga In Datatable.Rows
'example query
SQLStm = "INSERT INTO test(Name,Phone)VALUES(Riga("Name"),Riga("Phone"))"
Try
Dim CMD As New MySqlCommand
CMD.Connection = connection
CMD.CommandText = SQLStm
CMD.ExecuteNonQuery()
Catch ex As Exception
End Try
Next
End Sub
or skim all the rows and gradually sending to the database. There is a better way to accomplish this?
Thanks to all
I would say the best way to do this would be via XML. Convert the datatable to xml format and pass it through to a procedure which accepts an XML. This saves the process from running once per every line within a datatable, and it is all done in one go. The current way you are doing this would not scale well for large data sets, but XML would scale far better.
instead performing a db insert for each row, build the whole query string first and then perform it as one large INSERT command. It's much faster.
I want to create a Dot net application and provide an environment to a user may be Multiline Text box, where user can Paste the predefined SP and Execute. After execution this Sp should be created in DB
Any ideas are invited..
I assume you want to do thid for a internal support application or something like that, not to the end-user, right?
Anyway, you need to be more specific, but the way you would create a procedure doesnt differ the way you would run a insert statment for example.
Simple example:
SqlConnection objConnection = new SqlConnection(your_connection_string);
SqlCommand objCommand = new SqlCommand(tbProcedureCode.text, objConnection);
objCommand.CommandType = CommandType.Text;
objConnection.Open();
objCommand.ExecuteNonQuery();
Very strange situation here: I'm using L2S to populate a DataGridView.
Code follows:
private void RefreshUserGrid()
{
var UserQuery = from userRecord in this.DataContext.tblUsers
orderby userRecord.DisplayName
select userRecord;
UsersGridView.DataSource = UserQuery;
//I have also tried
//this.UserBindingSource.DataSource = UserQuery;
//UsersGridView.Datasource = UserBindingSource;
UsersGridView.Columns[0].Visible = false;
}
Whenever I use L2S to Add/Delete records from the database, the GridView refreshes perfectly well.
However, if someone is editing the grid and makes a mistake, I want them to be able to hit a refresh button and have their mistakes erased by reloading from the datasource.
For the life of me, I can't get it to work.
The code I am currently using on my refresh button is this:
private void button1_Click(object sender, EventArgs e)
{
this.DataContext.Refresh(RefreshMode.OverwriteCurrentValues);
RefreshUserGrid();
}
But the damn GridView remains unaffected. All that happens is the selected row becomes unselected.
I have tried .Refresh(), .Invalidate(), I've tried changing the DataSource to NULL and back again (all suggestions from similar posts here)....none of it works. The only time the Grid refreshes is if I restart the app.
I must be missing something fundamental, but I'm totally stumped and so are my colleagues.
Any ideas?
Thanks!
The easiest would be to use a BindingSource. Create an instance of the BindingSource to the class initialize it to the data query then assign the BindingSource to the UsersGridView.
The BindingSource will handle the updates etc. There are several events that can be caught for custom management.
This link gives an example of using a BindingSource
EDIT: My first post assumed Webforms instead of WinForms.
It seems that this is a bug in LINQ to SQL. I understand from the Janus GridEx folks that the cause of the problem is that LINQ presents a static list to the grid that is not refreshed after Refresh is called on the DataContext.
A solution is to simply re-instantiate the DataContext.
You can Refresh Grid by just re initialising DataContext object. This will relaease current DataContext object which needs to updated. but new instance will automatically have the updated values.
The previous comments are true. Re-instantiate the DataContext to solve the problem
Dim db As New dbDatosDataContext()
DataGridView1.DataSource = From l In db.Bultos Select l
After trying many methods, and almost settling for DataContext.Refresh, I found this clever trick that clears the cache, thus forcing a data update:
Public Sub ClearCache(Dcontext As DataContext)
Const FLAGS = BindingFlags.Instance + BindingFlags.Public + BindingFlags.NonPublic
Dim method = Dcontext.GetType().GetMethod("ClearCache", FLAGS)
method.Invoke(Dcontext, Nothing)
End Sub
Add this method to your module and then to update the data just do:
ClearCache(DataContext)
UsersGridView.Refresh()