Speed up strategy for SQL Server when EXECUTE AS is used for security - sql

I've just started looking at a system that implements security a little differently to the norm.
They create a new SQL user for each user of the system (of which there are about 32K now). Each query is sent via a connection that is initially using the SA account (lets not get bogged down on this), then after we know who the user is, the EXECUTE AS USER is used each query.
Now that there are so many users, creating new users and switching has a noticeable performance hit and the company is looking at improving the situation.
A few points:
- SQL Code is dynamic sql (not stored procedures)
- The original idea was to alleviate the need for the developers of the company to worry about writing SQL worrying about permissions - and let another layer worry about it.
How does one try and improve the query execution time and avoid the EXECUTE AS USER code and still get the same security scrutiny?
Does SQL Server support session variables to store a user account?

It's difficult to know whether this is helpful without a bit more detail on how your application security model and how it controls and/or pools database connections. Is it a fat client, n-tier or something else?
SQL connections can support a single "session variable" using CONTEXT_INFO - a user-configurable binary(128) field which persists for the duration of the connection. This may meet your requirements, but you need to beware that if you use it to store security information it will be accessible to the end user - you should therefore probably encrypt or salt and hash any security information in the CONTEXT_INFO to prevent users tampering with their permissions; this may have performance implications.
It may not be applicable depending on your application architecture, but have you considered switching to Windows authorisation and organising permissions though Active Directory users and groups?

Related

Webserver with database : one connection per user

Our aim is to implement the principle of least privilege with a defense in depth approach. In this particular case, this means that a query sent by an unprivileged user should not have admin rights on the database side. RDBMS such as PostgreSQL provide very powerful, expressive and well-tested access control mechanisms : RBAC, row-level security, parametrized views, etc. These controls, indeed, are usually totally ignored in web applications which use the paradigm "1 application == 1 user", this user has thus admin role. But heavy clients often use several different users on the database side (either one per final user or one per specific role) and thus benefit from the access control of the database.
Access control from the DB is an addition to access control in the web application. AC in the webapp will be more precise but may probably suffer from some bugs ; AC in the DB will be a bit more laxist but better enforced, limiting damages in case of an application bug.
So in our case, we want to create a DB user for every application user. Then, the connection to the database belongs to this specific user and the database can thus enforce that a simple user cannot execute admin operations. An intermediate possibility would be to drop some privileges before executing a query, but our preferred way is to connect to the database as the currently logged-in user. The login-password is sent by the user when he authenticates and we just pass it to the DBMS. Scalability is not (yet) an issue for our application, we can sacrifice some scalability for this type of security.
Would you have any hints to help us achieve this ?

Why are database links bad for security?

I heard database links are bad for organizations to use. Why is it bad for security?
Where did you hear this?
Database links, like any tool, have their uses and misuses. There is nothing inherently insecure about using a database link. But there are certainly plenty of ways to architect a system using database links that is insecure.
A database link lets you connect one database to another. Broadly speaking, you can define the database link so that it connects to the remote database as a specific fixed user on the remote database or you can define the database link so that it connects to the remote database as the current user. Those configurations have different issues.
If you use a fixed user, you have to be careful that the users that can access the local database link ought to have access to whatever privileges the remote database user has. If you use a relatively powerful account to create the database link but then give access to that link to relatively low-privilege users, that can certainly be a security issue. It can also be challenging to identify situations of concern where this has taken place because no single database has the whole picture. If user Bob on database A has read-only access to a couple of tables but there is a public database link on A that connects to database B as a highly privileged user, someone that compromises Bob's account the ability to execute commands on B as that highly privileged user. Of course, you can mitigate these issues by not creating database links as highly privileged users, taking care when creating public database links, creating private database links when the fixed user is going to have privileges that you don't want to grant to everyone, etc.
If you use current user database links, then the user Bob on database A connects to database B as Bob and has whatever privileges Bob does on database B. In general, that is likely to be easier to secure. It's at least much harder to unintentionally do something stupid. The downside to this approach, however, is that Bob would need to keep his password synchronized on both databases or the database link won't work. That generally involves developing a bit of infrastructure to allow Bob to reset his password on all databases (or use some sort of external authentication) which is a bit of work to set up and maintain. Occasionally, it will also limit what security measures the DBA can configure when you have a mixed environment. When you upgraded database A to 11.2, for example, you probably wouldn't want to enable case-sensitive passwords until database B was similarly upgraded. If you have lots of database links between lots of systems on very different upgrade schedules, this sort of thing might be concerning.
Some years back there was a significant bug where the "System Change Number" could be pushed ahead on a database and this would follow through to any database connected via a database link, resulting in a cascade of failures. Depending on how risk averse the organisation is, it can be a sensible precaution to keep databases isolated from each othe and reduce the effect of any 'outbreak'.
"Where this vulnerability gets interesting is that the SCN is synchronized to the highest SCN when two databases are connected via a database link. Therefore, it is possible to increase a database to the near maximum SCN through a database link, which will cascade through to all other interconnected databases. The result can be ORA-600 errors and potentially database crashes on the database with the lower SCN."
https://www.integrigy.com/oracle-security-blog/critical-oracle-database-bug-system-change-number-scn-cve-2012-0082

SQL Access for web apps

Background:
Our team is building an inhouse Intranet web application. We are using a standard three layer approach. Presentation layer (mvc web app), Business layer and data access layer.
Sql database is used for persistence.
Web app / iis handles user authentication (windows authentication). Logging is done in business and data access layer.
Question service account vs user specific Sql accounts:
Use service / app account:
Dev team is proposing to set up service account (set up for application only). This service account needs write & read access to db.
Vs
Pass on user credentials to SQL
IT ops is saying that using a service account (specifically created for app only) for db access is not deemed best practice. Set up Kerberos delegation configured from the web server to the SQL server so that you can pass on the Windows credentials of the end users & create a database role that grants the appropriate data access levels for end users
What is the best practice for setting up accounts in sql where all request to db will come through the front end client (ie via bus layer and then data layer)
The Best Practice here is to let the person/team responsible for the database make the decision. It sounds like the dev team wants to forward (or impersonate) some credentials to the DB which I know that some small teams like doing, but yes that can leave things a bit too open. The app can do whatever it likes to the database, which is not much of a separation if you're into that kind of thing.
Personally, if I understand what you're saying above, I do more of what the IT team is thinking about (I use Postgres). In other words my app deploys over SSH using a given account (let's say it's the AppName account). That means I need to have my SSH keys lined up for secure deployment (using a PEM or known_keys or whatever).
In the home root for AppName I have a file called .pgpass which has pretty specific security on it (0600). This means that my AppName account will use local security to get in rather than a username/password.
I do this because otherwise I'd need to store that information in a file somewhere - and those things get treated poorly pushed to github, for instance.
Ultimately, think 5 years from now and what your project and team will look like. Be optimistic - maybe it will be a smashing success! What will maintenance look like? What kinds of mistakes will your team make? Flexibility now is nice, but make sure that whomever will get in trouble if your database has a security problem is the one who gets to make the decision.
The best practice is to have individual accounts. This allows you to use database facilities for identifying who is accessing the database.
This is particularly important if the data is being modified. You can log who is modifying what data -- generally a hard-core requirement in any system where users have this ability.
You may find that, for some reason, you do not want to use your database's built-in authentication mechanisms. In that case, you are probably going to build a layer on top of the database, replicating much of the built-in functionality. There are situations where this might be necessary. In general, this would be a dangerous approach (the database security mechanisms probably undergo much more testing than bespoke code).
Finally, if you are building an in-house application with just a handful of users who have read-only access to the database, it might be simpler to have only a single login account. Normally, you would still like to know who is doing what, but for simplicity, you might forego that functionality. However, knowing who is doing what is usually very useful knowledge for maintaining and enhancing the application.

Creating a (temp?) table that is accessible only in the current connection

I want to enforce row-level security for any client connecting to a particular SQL Server database (just one database). I don't want to impose any particular set up required to happen on the client side (because this would mean that a client can set up itself so that it would have access to anything - which of course would be bad; BTW, the client is a WinApp that connects using either Windows Auth or SQL Auth). I basically want this to be transparent to any client. The client should not even know this is happening.
The enforcement of the row level security will be performed in views inside the database that are layered above the tables (in essence: no one will have the ability to perform DML against the tables directly; instead, all operations shall be performed against the views that lay on top of the tables. These views will have instead-of triggers running under a particular 'execute as', to ensure the DML operations can be correctly executed).
So basically, I want to remove the potential of the client to circumvent this security model by baking it into the database itself.
I also want to separate out the permissions granted to the user from the effective permissions that are applied to the current connection (think of it this way: if you are connected to the DB, you have a Security Context associated with your connection - maintained in the DB, mind you - this Security Context contains the information about which items you have access to. Upon establishing the connection, this Security Context is created and populated with information based of the permissions assigned to you and when the connection is closed, the information in this Security Context is removed; in fact, the entire Security Context should be removed). Of course, the Security Context should only be available within a given connection, connections should not have the ability to even see the existence of a security context for other connections.
(EDIT: one of the scenarios explicitly targeted, which explains why the Security Context is separated from the 'defined permissions' is as follows: if you establish a connection to the DB, you get a SecContext; now while your connection is active/not closed, the admin assigns new permissions to you, these new permissions will not be applied to this currently open connection. You must close the connection and re-establish a connection to have these changes reflected in your SecContext)
I know how I can enforce that the views will only return information that the user has access to. That's the easy part... This question is about creation and deletion of the Security Context I am talking about.
The Security Context should be created on establishing the connection.
It should also reside in an artifact that is accessible only to the current connection.
It must be queryable during the lifespan of the connection by the connecting user.
Finally, the Security Context must be dropped/removed/deleted when the connection is closed.
Would anyone have an idea about how this can be achieved.
Thanks
A SQL Server temp table (one with a name starting with #) is only available to the current connection, and dropped at the end. You only have to deal with establishing it on creating a new connection.
However it sounds like you are re-implementing a lot of what the DBMS already does for you. I would recommend reading up more on SQL Server's built-in security mechanisms, in particular login/user/schema separation.
I don't really understand your application in the abstract, so I don't entirely understand where you're coming from here, but here's a question for you: it sounds like you're giving your users a direct connection to your database. Do you need to do that?
Perhaps I'm missing the point, but could not all of this row-level security be done away with entirely if you built into your application an API, rather than provided your users with a direct database connection? If you set things up that way, your API could be the gatekeeper which prevents users from making changes to rows to which they should not be given access. Perhaps that would be simpler than working directly at the database level?
I hope that's helpful.

Best practice on users/roles on SQL Server for a web application

I searched online a bit and couldn't find anything that really nailed the spot or covered the bases how to go about setting up users/roles on a database.
Basically, there would be a user that would be used to access the database from the application (web application in this case) that will need access to database for the regular database operations (select, insert, update, delete) and executing stored procedures (with exec to run stored procedures within other stored procedures/UDFs).
Then, we would also have a user that would be main admin (this is simple enough).
I currently have a development environment where we don't really manage the security too well in my opinion (application uses a user with db_owner role, though it is an intranet application). Even though it is an intranet application, we still have security in mind and would like to see what are some of the ways developers set up the users/roles for this type of environment.
EDIT: Web application and SQL Server reside on separate machines.
EDIT: Forgot to mention that an ORM is used that would need direct read/write access.
Question:
What are the "best practices" on setting up the user for application access? What roles would apply and what are some of the catches?
First, I tend to encapsulate permissions in database roles rather than attach them to single user principals. The big win here is roles are part of your database, so you can completely script security then tell the deployment types to "add a user and add him to this role" and they aren't fighting SQL permission boogeymen. Furthermore, this keeps things clean enough that you can avoid developing in db_owner mode and feel alot better about yourself--as well as practice like you play and generally avoid any issues.
Insofar as applying permissions for that role, I tend to cast the net wider these days, especially if one is using ORMs and handling security through the application. In T-SQL terms, it looks like this:
GRANT SELECT, UPDATE, INSERT, DELETE, EXECUTE on SCHEMA::DBO to [My DB Role]
This might seem a bit scary at first, but it really isn't -- that role can't do anything other than manipulate data. No access to extended procs or system procs or granting user access, etc. The other big advantage is that changing the schema--like adding a table or a procedure--requires no further security work so long as you remain within that schema.
Another thing to take into consideration for SQL 2005+ is to use database schemas to secure groups of objects. Now, the big trick here is that many ORMs and migration tools don't like them, but if you render the default schema [dbo] to the app, you can use alternative schemas for special secured stuff. Eg--create an ADMIN schema for special, brutal database cleanup procedures that should be manually run by admins. Or even a separate schema for a special, highly secured part of the application that needs more granular DB permissions.
Insofar as wiring in users where you have separate boxes, even without a domain you can use Windows authentication (in Sql Server terms integrated authentication). Just make a user with the same credentials (user/pass combo) on both boxes. Setup an app domain to run as that user on the web box and setup a Sql Server user backed by that principal on the sql box and profit. That said, using the database roles can pretty much divorce you from this decision as the deployment types should be able to handle creating sql users and modifying connection strings as required.
For a long time the SQL Server guidelines for application access to the database were to isolate access to data into stored procedures, group procedures into a schema and grant execute on the schema to the principal used by the application. Ownership chaining would guarantee data access to the procedure callers. The access can be reviewed by inspecting the stored procedures. This is a simple model, easy to understand, design, deploy and manage. Use of stored procedure can leverage code signing, the most granular and powerfull access control method, and the only one that is tamper evident (signature is lost if procedure is altered).
The problem is that every bit of technology comming out from the Visual Studio designers flies in the face of this recommendation. Developers are presented with models that are just hard to use exclusively with stored procedures. Developers love to design their class models first and generate the table structure from the logical model. The procedure based guidelines reuire the procedures to exists first, before the first line of the application is written, and this is actually problematic in development due to the iterative way of modern development. This is not unsolvable, as long as the team leadership is aware of the issue and addresses it (ie. have the procedures ready, even as mocks, when the dev cycle starts).
Create a user 'webuser' that the web application uses.
Only grant stored proc execute permissions to this user. Do not allow direct table read/write. If you need to read something from a table, write a proc. If you need to write data, write another proc.
This way everything is kept nice and simple. One app user, with only the relevant permissions. If security is compromised, then all the intruder can do is run the procs.