I need to know if there is a safe way to run a SQL query that comes in directly from the user. We have an admin tool where the admins can create Key Performance Indicators by giving a SQL query to be run against the database every hour. The problem is this process is obviously susceptible to bad things happening by user error or malicious users (for example, a user could easily delete data from a table in this query). I'm wondering if there is a safe way to execute these statements or know what they are going to do beforehand so if they are doing anything other than a select we can block it.
One suggestion is running the SQL in a transaction and then just rolling back the transaction every time. Is this a decent solution?
A few methods...
Find a BI tool that gives them some ability to design their own report. Build a cube to support it and let em go nuts (BI tool would prevent the more harmful queries, and also ensure it's assembling queries according to how your DB is setup)
You can save their query as a string and do some substring functions to look for key words like drop, delete, update, or anything else you might think is malicious. This would appear to be the hard work method for you as string searching is never that fun.
Finally...permissions work, just get them into something read only. I have the strange feeling thats a no go by the way you've worded this though.
As a last comment...as dangerous as deletes and updates could be, you'll also have to be vary wary of horridly designed select statements. Select * from tbl1,tbl2,tbl3,tbl4,tbl5,etc... Could lock up your database and consume every resource it's got.
Few more suggestions
1.Search for DDL Commands in query and reject query if they exists
2.Try limiting Permissions by granting only select if possible
3.Log query and user who gave the query ,so it will be helpfull for forensic analysis
Sounds like your best bet may be to allow the queries to run by a user with limited permissions (i.e. a read-only user). Something similar to this answer: How to grant users read-only access to all databases
Related
Folks - apologies for rehashing this topic as I see even here on Stack, there are so many questions on the topic already.
But I find myself in an interesting place and I'm hoping you can help.
High level Question: can SQL SERVER have the leeway to decide that a view should be wrapped in a ISOLATION LEVEL SNAPSHOT?
I know that sounds like a crazy question but I'm trying to exhaust all avenues per an issue I'm encountering.
I'm working with an application that runs 35 queries to retrieve data from another database via Link Server. The queries are simple selects against one table respectively. All DB operations are carried out against SQL SERVER, and retrieval code is ADO.NET/c#, etc.
34 of the queries work flawlessly - but there's this one bad apple, and for it, I get the transaction isolation level snapshot issue.
I've also tested data retrieval outside of the application and when I implement the below snippet on the "problem" query, I also get the issue:
using (var trans = conn.BeginTransaction(IsolationLevel.Snapshot))
However, when I do NOT implement it on said query, all is well.
I've also tested this against the other queries - with and without "Shapshot" - and my results are predictable... With "Shapshot" in place, no queries process... When not implemented, all queries process...
My results suggest that the application is responsible for changing up the data retrieval strategy.
Per their knowledge base, I found this: Locking is handled by the database level (MS SQL Server/Oracle) and not by "us". Generally, locking is row level but the optimizer may choose something different
Unfortunately I don't have native access to the boiler-plate application code responsible for data retrieval. I suspect that this particular query/table has one or more key words - either in the column or query/table naming - that trigger the application to use an alternate retrieval strategy. Per the developer forums, I've asked of this is the case and I'm awaiting a reply...
Anyway back to their mention of the optimizer may choose something different- their optimizer, or perhaps the database optimizer? Can SQL SERVER be set up to make a "judgement call" ? Is the statement unclear or do I just not enough of SQL SERVER and its capabilities?
I know it seems like a crazy question but I really want to knock out all possible avenues here.
Thank you kindly for suspending your disbelieve and humoring that crazy post :)
Apparently objects with the word "valuation" (perhaps because of the sensitive nature implied) cause the application to build the transaction. Once I changed the view name, the data returned to the client successfully.
So yes the application was/is the issue.
In my program I have a query that views Contacts information to the user. This table normally shows all the rows with no filter. I would like to add an option for the user to write their own condition at the end of the SELECT statement to view the information that they need. So if my main query is something like this:
SELECT * FROM CONTACTS
The user can write the condition
WHERE FirstName LIKE '%Michael%'
In a textbox.
However, I am aware that this is not very safe and is prone to SQL Injection. But how can I prevent the user from entering malicious commands such as
WHERE 1=1; DROP TABLE Contacts
In the text box? For now I am using a check against some keywords e.g. if the filter contains DELETE, DROP, UPDATE, etc the query will not run. But I don't think this is a very safe solution.
Allowing a user to write "their own condition at the end of a {SQL} query", can create security holes in your application which will make it prone to things like a SQL injection attack.
If you still wish to proceed, here are a couple of things to consider:
use regular expressions to limit input to its most basic form (e.g. phone number only allows 0-9 and hyphens)
implement your protection mechanism at the lowest level (i.e. stored procedure)
for dynamic queries in stored procedures... never pass in field names into the stored procedure
Never run with more privileges than necessary.
Users that log into an application with their own login should normally only have EXEC permissions on stored procedures.
If you use dynamic SQL, it should be confined to reading operations so that users only need SELECT permissions.
A web site that logs into a database should not have any elevated privileges, preferably only EXEC and (maybe) SELECT permissions.
Never let the web site log in as administrator!
Always used parameterized statements.
Do a code review to check for the possibility of second-order attacks.
Ensure that error messages give nothing away about the internal architecture of the application or the database.
Again... be very, VERY careful when you implement this!
ADDITIONAL READING
Wikipedia: SQL injection attack
The Curse and Blessings of Dynamic SQL
SQL Injection Attacks and Some Tips on How to Prevent Them
Dynamic Search Conditions in T‑SQL
Top 10 tricks to exploit SQL server systems
You're right that this isn't safe. There is no safe way of doing exactly what you want. Arbitrary SQL filters means the user could equally use a WHERE id IN (SELECT x FROM OPENROWSET(...)) selection, for instance, still allowing DROP TABLE executions.
What you can do is provide your own filter syntax, and using your own parser for that syntax, translate it to SQL. You can make sure only to allow features that are safe to use from SQL. Some ORMs may provide such a feature out of the box, otherwise you'll have to create something yourself.
I'd consider this feature a no-go as it is designed: Not only is the user expected to have knowledge about how the database works, he also needs to know both the correct syntax and the data model.
Try to mask this by providing predefined conditions, such as "where the user name contains..." or "the last name is".
On the C# side use parameterized queries to make sure the user provided input is sanitized.
This is impossible to answer. You want a search engine that will allow SQL conditions and this is the definition of SQL injection.
Here are a few workaround ideas :
Use a grid control : You can display the query results inside a grid and allow the user to
use the grid to filter the results. This is easy to implement (you don't reinvent the wheel) and it is user friendly. Moreover, some grids offer very powerful filtering options. The only drawback I can see is that you'll often retrieve much more results from the database that what you actually needs.
Create your own filter syntax : You can code your own filter syntax (hvd solution). This is going to be a lot of work and if you miss something you might still end up with a security hole.
Code a condition build-up tool : You can provide a condition build up tool. This is very user friendly but in the end it may not be flexible enough.
Export to CSV : You can offer a tool to export the query results to CSV for easy exploitation in Excel or Calc. This would be very user friendly for experienced users with spreadsheet applications.
I'm writing an ASP.NET MVC4 application which ultimately builds a SQL SELECT statement dynamically to be stored and executed at a later time. The structure of the dynamic SQL is determined by user configuration in a user-friendly manner, with standard checkboxes, dropdowns, and freeform-entry textboxes. It would be simple enough to validate the input and build the SQL string using parameterized queries, except that I need to allow advanced users the ability to enter custom SQL to be injected directly into the SELECT and WHERE clauses. So what techniques can I use to cleanse the custom SQL expressions or otherwise guard against unwanted input from a clever user? I can easily enough parse the string for suspect keywords and blacklist insert/update/delete/etc., but something tells me that's not going to protect me 100%.
I'm happy to provide more details about exactly what I'm doing here, but I'm not sure what other details would be helpful, since I feel like my problem, while probably not common, is pretty generic.
Contrary to other views, it is possible to do this safely (just look at Data Explorer). Here are the four things you can do to make it happen:
Account Security
Sql Server will let you restrict permissions available to the account used to make the connection to the database. Only grant read permissions, and only to the appropriate tables. Then someone could inject malicious code 'til they're blue in the face, but it will fail at the compile step because they don't have enough permissions. That may mean using a different connection string for this access than for other parts of the application.
Note that this is good for sql code, but there are other kinds of injection as well. If you ever show the query on the page (in full or part) back to the user before running it, you should also look out for javascript injection attacks such as cross site scripting.
Query Governor
You also want to defend against denial of service attacks. Sql databases make it easy for these to happen even by accident, simply from constructing an inefficient query. To combat this threat, you should take a look at the query governor feature in Sql Server. Note that tuning this thing is tricky.
Quarantine
The safest course is to also use a dedicated, sanitized reporting database, hosted on a dedicated server. This ensures that no query can impact production, either in terms of performance or a breached server or account. There are features in sql server such as SSIS that you can use to automate populating your reporting DB from production.
Use a Product
One of the things about security is that you never want to find yourself building your own secured system. It's easy to create something that seems to work, passes tests, but is flawed in subtle ways that lead to breaches later. You want to rely on a product from a vendor that does this kind of thing as a core competency. This means it's battle tested (inevitable bugs are found and fixed) and if there is a flaw, that flaw might end up exposed for someone else first. It also means the vendor can provide support and a level of indemnification. Normally I talk about this in terms of authentication systems, but this rule can apply to sql injection defense as well.
In this case, it might be worth checking out Stack Overflow's Data Explorer. This is a tool to allow the construction of arbitrary queries by untrusted users. The project is open source, so you can see for yourself what they've done to ensure safety, or even just fork that project for your own use. It's worth mentioning again that a big part of this tool's safety is that it is intended for use on a dedicated, sanitized database, so it does not exempt you from the other items.
When all is said and done, though, I think the comment to set up some views and provide access via reporting services is probably your best bet.
I think you can only achieve what you need by not providing a direct way to specify SQL.
That doesn't have to be a problem however. Basically you only need a subset of SQL for defining expressions (computed solumns in the SELECT) and predicates (for filtering in the WHERE). This can be handled with a self-made parser which has just enough power to do everything needed, but then "compiles" the expressions into real SQL code, without actually injecting anything directly.
This then validates the syntax and makes sure that no constructs can be fabricated which would allow for any injection. Also, it allows you to properly let the code generator parametrize the queries you generate as needed (for instance, all literals could be passed in as query parameters by your code generator).
I would never ever let users to run custom SQL statements against production server.
Which bad guys directly can attempt to read/write/modify on tables which they are not supposed to or other bad guys can compromise weak accounts and run SQL statements against to your server.
If you have pre-built system which creates sql statement according to html inputs then you can generate script on the back-end which will not hurt your database. Otherwise if users want something very custom, then let them upload their script, review it with your SQL Administrator and make sure it is "safe to execute" and run it to be scheduled.
If script is denied then let user know about it, and explain why their custom script is denied.
Use stored procedures, so any thing that comes from any user will be inserted as a parameter and never be executed.
Small background: I'm the only programmer for this company. I'm working with pre-existing frameworks.
That said, the company has a dll(Database.dll) which contains "all the database interactions I need". As in, it has a Query(), Update(), Insert(), etc. Now, the project I'm writing sets a reference to Database.dll. My project accepts zero user input. The closest thing to user input is a dropdown box that the user can select a date from. Not having much experience with it, I'm curious if I still need to worry about SQL injections? And if so, would a query written like
var query = string.Format("SELECT timestamp FROM table1 WHERE date = \"{0}\"
AND measured_dist = bit_loc AND rop > 0" , Date))
be sufficient as a parameterized query? Keep in mind, all of the query execution is handled by the pre-existing Query() that I'm told I have to use, and can't edit.
EDIT
This program is a WinForm application.
As noted in comments, the answer is "always". Since it would be so easy to add a parameter to that and do it properly, rather than concatenation: just do it right first time. Also: have you considered that injection is not the only problem in the code you've shown? That code is also susceptible to localisation / internationalisation. What happens for a user who has their PC configured in a different culture? The dates and numbers will get rendered differently - and will often break. That doesn't happen with parameters. Also: names often have apostrophes in :)
Do extend on #KirkWoll's very valid comment, any time you incorporate any user input (or input from automated sources for that matter) in a SQL statement, you place your program at risk of SQL injection.
As a matter of policy, you should never, ever build your own SQL statement using any such input.
Always sanitize input and always use parameterized queries as a first line of defense against SQL injection.
In case you have not seen it before, there's a great illustration on xkcd
http://xkcd.com/327/
Given that this is a WinForms program the only safe way to access the database is to use Stored Procedures that take parameters. Then create a user that only has access to those SPs. Anything else is not secure.
While queries with parameters work as a security measure when used with web applications which can have "attack" input, they fail when used with a local application which can be dis-assembled and re-written to anything. If you don't provide SP security you are lost.
Even though the user interaction may be a drop-down, it's possible for a sophisticated attacker to insert a value that is not in the list of selections. So YES, you should still be wary of SQL injection.
I would use prepared statements even if there was no such thing as SQL injection. They are just easier to use and in some cases they allow a database to cache the statement and not have to compile it the next time you use it. Oracle does that, I think SQL Server does, I don't know if MySQL does.
You should always assume that there are hackers, even on internal intranet projects, I use prepared statements and I use nonces to prevent CSRF.
I have a project (private, ASP.net website, password protected with https) where one of the requirements is that the user be able to enter Sql queries that will directly query the database. I need to be able to allow these queries, while preventing them from doing damage to the database itself, and from accessing or updating data that they shouldn't be able to access/update.
I have come up with the following rules for implementation:
Use a db user that only has permission for Select Table/View and Update Table (thus any other commands like drop/alter/truncate/insert/delete will just not run).
Verify that the statement begins with the words "Select" or "Update"
Verify (using Regex) that there are no instances of semi-colons in the statement that are not surrounded by single-quotes, white space and letters. (The thought here is that the only way that they could include a second query would be to end the first with a semi-colon that is not part of an input string).
Verify (using Regex) that the user has permission to access the tables being queried/updated, included in joins, etc. This includes any subqueries. (Part of the way that this will be accomplished is that the user will be using a set of table names that do not actually exist in the database, part of the query parsing will be to substitute in the correct corresponding table names into the query).
Am I missing anything?
The goal is that the users be able to query/update tables to which they have access in any way that they see fit, and to prevent any accidental or malicious attempts to damage the db. (And since a requirement is that the user generate the sql, I have no way to parametrize the query or sanitize it using any built-in tools that I know of).
This is a bad idea, and not just from an injection-prevention perspective. It's really easy for a user that doesn't know any better to accidentally run a query that will hog all your database resources (memory/cpu), effectively resulting in a denial of service attack.
If you must allow this, it's best to keep a completely separate server for these queries, and use replication to keep it pretty close to an exact mirror of your production system. Of course, that won't work with your UPDATE requirement.
But I want to say again: this just won't work. You can't protect your database if users can run ad hoc queries.
what about this stuff, just imagine the select is an EXEC
select convert(varchar(50),0x64726F70207461626C652061)
My gut reaction is that you should focus on setting the account privileges and grants as tightly as possible. Look at your RDBMS security documentation thoroughly, there may well be features you are not familiar with that would prove helpful (e.g. Oracle's Virtual Private Database, I believe, may be useful in this kind of scenario).
In particular, your idea to "Verify (using Regex) that the user has permission to access the tables being queried/updated, included in joins, etc." sounds like you would be trying to re-implement security functionality already built into the database.
Well, you already have enough people telling you "dont' do this", so if they aren't able to dissuade you, here are some ideas:
INCLUDE the Good, Don't try to EXCLUDE the bad
(I think the proper terminology is Whitelisting vs Blacklisting )
By that, I mean don't look for evil or invalid stuff to toss out (there are too many ways it could be written or disguised), instead look for valid stuff to include and toss out everything else.
You already mentioned in another comment that you are looking for a list of user-friendly table names, and substituting the actual schema table names. This is what I'm talking about--if you are going to do this, then do it with field names, too.
I'm still leaning toward a graphical UI of some sort, though: select tables to view here, select fields you want to see here, use some drop-downs to build a where clause, etc. A pain, but still probably easier.
What you're missing is the ingenuity of an attacker finding holes in your application.
I can virtually guarantee you that you won't be able to close all the holes if you allow this. There might even be bugs in the database engine you don't know about but they do that allows an SQL statement you deem safe to wreck havoc in your system.
In short: This is a monumentally bad idea!
As the others indicate, letting end-users do this is not a good idea. I suspect the requirement isn't really that the user really needs ad-hoc SQL, but rather a way to get and update data in ways not initially forseen. To allow queries, do as Joel suggests and keep a "read only" database, but use a reporting application such as Microsoft Reporting Services or Data Dynamics Active reports to allow users to design and run ad-hoc reports. Both I believe have ways to present users with a filtered view on "their" data.
For the updates, it is more tricky- I don't know of existing tools to do this. One option may be to design your application so that developers can quickly write plugins to expose new forms for updating data. The plugin would need to expose a UI form, code for checking that the current user can execute it, and code for executing it. Your application would load all plugins and expose the forms that a user has access to.
Event seemingly secure technology like Dynamic LINQ, is not safe from code injection issues and you are talking about providing low-level access.
No matter how hard you sanitize queries and tune permissions, it probably will still be possible to freeze your DB by sending over some CPU-intensive query.
So one of the "protection options" is to show up a message box telling that all queries accessing restricted objects or causing bad side-effects will be logged against user's account and reported to the admins immediately.
Another option - just try to look for a better alternative (i.e. if you really need to process & update data, why not expose API to do this safely?)
One (maybe overkill) option could be use a compiler for a reduced SQL language. Something like using JavaCC with a modified SQL grammar that only allows SELECT statements, then you might receive the query, compile it and if it compiles you can run it.
For C# i know Irony but never used it.
You can do a huge amount of damage with an update statement.
I had a project similar to this, and our solution was to walk the user through a very annoying wizard allowing them to make the choices, but the query itself is constructed behind the scenes by the application code. Very laborious to create, but at least we were in control of the code that finally executed.
The question is, do you trust your users? If your users have had to log into the system, you are using HTTPS & taken precautions against XSS attacks then SQL Injection is a smaller issue. Running the queries under a restricted account ought to be enough if you trust the legitimate users. For years I've been running MyLittleAdmin on the web and have yet to have a problem.
If you run under a properly restricted SQL Account select convert(varchar(50),0x64726F70207461626C652061) won't get very far and you can defend against resource hogging queries by setting a short timeout on your database requests. People could still do incorrect updates, but then that just comes back to do you trust your users?
You are always taking a managed risk attaching any database to the web, but then that's what backups are for.
If they don't have to perform really advanced queries you could provide a ui that only allows certain choices, like a drop down list with "update,delete,select" then the next ddl would automatically populate with a list of available tables etc.. similar to query builder in sql management studio.
Then in your server side code you would convert these groups of ui elements into sql statements and use a parametrized query to stop malicious content
This is a terribly bad practice. I would create a handful of stored procedures to handle everything you'd want to do, even the more advanced queries. Present them to the user, let them pick the one they want, and pass your parameters.
The answer above mine is also extremely good.
Although I agree with Joel Coehoorn and SQLMenace, some of us do have "requirements". Instead of having them send ad Hoc queries, why not create a visual query builder, like the ones found in the MS sample applications found at asp.net, or try this link.
I am not against the points made by Joel. He is correct. Having users (remember we are talking users here, they could care less about what you want to enforce) throw queries is like an app without a "Business Logic Layer", not to mention the additional questions to be answered when certain results does not match other supporting application results.
here is another example
the hacker doesn't need to know the real table name, he/she can run undocumented procs like this
sp_msforeachtable 'print ''?'''
just instead of print it will be drop
Plenty of answers saying that it's a bad idea but somethimes that's what the requirements insist on. There is one gotcha that I haven't spotted mentioned in the "If you have to do it anyway" suggestions though:
Make sure that any update statements include a WHERE clause. It's all too easy to run
UPDATE ImportantTable
SET VitalColumn = NULL
and miss out the important
WHERE UserID = #USER_NAME
If an update is required across the whole table then it's easy enough to add
WHERE 1 = 1
Requiring the where clause doesn't stop a malicious user from doing bad things but it should reduce accidental whole table changes.