March 11, 2010

"For successful technology, reality must take precedence over public relations, for nature cannot be fooled."    --  Richard Feynman
Moving Sql Server Code
Author:RBarryYoungCreated:8/29/2008 12:21 PM
SQL Server security issues

An interesting question came up on the MSDN SQL Forums yesterday about best practices for applications to access their databases on SQL Server:

In what situations will application roles suffice, I have read about how it works, but in applications that I have seen, e.g. an exe which expects a servername as a parameter, a username and a password. How will an application role work here.

In what applications/scenarios have other SQL server experts used application roles.

If SQL server login is to be used, what is the best practice approach of how this should be managed?
In order to answer this question, it is necessary to first understand that a "SQL Server Login"and an "Application Role" are two different things.  

A SQL Server Login, is a SQL Server-only account (i.e., not a Windows Login/account) that is used by supplying a username and password  to SQL Server when connecting.  Because such a SQL Server Login is created just for this application's use, and is used only by this application, they are typically called "Application Logins".  They are popular with applications because 1) they provide a way to authenticate and authorize just the application, irrespective of the user, so that the user cannot access the database on their own without the application.  And 2) they can be externally parametrized and thus managed and modified externally.  

Its downsides are that gaining access to the application is tantamount to gaining access to the applications data, and even worse, gaining access to the executable image and/or executable environment gives hackers the ability to strip the application login's username and password out and use it on their own (because the Window account using it is never checked by SQL Server). 

This is all in contrast to a Windows or Domain Login that uses a Trusted connection that needs no validation on connection.  Typically in Client-Server architectures, this is the Users domain account that the client application is piggy-backing on to connect to the database.  Authorization in the database is implemented by adding the Windows domain account or a Windows group that the application user are in (usually a group created solely for this purpose) as a Windows Login in SQL Server and then as a User in the application database.  User/Group authorization of windows logins for applications access to SQL Server database is popular because 1) it takes virtually nothing in the application code to implement it, its all up to Windows and SQL Server, and 2) access to the application, or its image/environment will not give you access to the data because you have to be in the right Windows Group before SQL Server will give you that access.  

Its disadvantages are that the users can access the database (and therefore usually, the data) all on their own without ever having to use the application (because SQL Server is *only* checking the Windows Login/Group and requires nothing else).

 An Application Role, on the other hand is a way to "sort of" combine the security advantages of both of these approaches.  An Application Role is a database-only principal (that is, non-server level, so it cannot normally cross databases) that is intended to be used by an application AFTER it has successfully connected to the database.  The Application Role is invoked by using the command: EXEC sp_setapprole "rolename", "password", which causes SQL Server to drop the connections current security context and assume the Application Role's.  The idea here is that the application's users' Windows Logins (or Windows Group) is authorized by SQL Server to connect to the database, but *nothing else*, it has no access to anything else in the database (tables, views, application stored procedures, etc.).  This allows the application to use Trusted connections to the database based on the authorization of the user.  At that point the application runs sp_setapprole to switch to the Application Role in the database, which is authorized to do everything that the database needs to do. 

The advantages of this approach are that 1) Authorized users cannot access the application data on their because they do not have the approle's password.  And, 2) access to the application, image or environment, does not enable a hacker to gain access to the SQL Server or the application's database, because you must still be in the right Windows Group to connect to the database.  

The disadvantages are that this approach is complicated and harder to implement (both in code and in the DB) than the other two.  Even worse, because it can only be implemented explicitly in code, it cannot be retrofitted in the field onto an existing product, application or other executable through external configuration, the access code has to be rewritten.

Here's a good article on SQL Injection by one of my favorite authors, Michael Coles: http://www.sqlservercentral.com/articles/Security/updatedsqlinjection/2065/.

Note that most of the SQL Injection articles will be filed under the "Dynamic SQL" heading.

I have implemented large shared schema, multi-tenant applications & databases before and it is suprisingly straight-forward as long as you follow certain rules:

1) All application users must be easily distinguishable by tenant to SQL. 

 Practically speaking this means that either each tenant has a separate Login or each user has a sepearate Login to SQL Server and a Tenant_Users table to map the user back to their tenant association.

2) Every table that the application can write to must have a tenant_id column that identifies the tenant-owner of the data.

3) Every such table must have a corresponding "security" view that insure that any user can only access rows in that table from the same tenant., like so:

CREATE VIEW Secure_TABLE as
Select *
  From Physical_TABLE T
   Inner Join Tenant_Users U
    ON U.tenant_id = T.tenant_id
     And U.UserName = sUser_sName()


4) No application code, including client code, application stored procedures, views, etc., is permitted to directly access the physical tables.  All such data access is only permitted through the Security views.  (use database roles, schemas and permissions to implement this, do not rely on code).

If you notice, this approach insures that, except for the user Logon's, the application has no knowledge of the multi-tenancy, and no application changes should normally be necessary.  This means that you can move a tenants data into or out of this approach without any changes to most applications, other than changing the Server.Database address. (note that to be truly transparent you would have to remove the tenant_id from the output of the security views).

The tenant_id should be made the first field of the primary key and probably also the Clustered Index (if different).

I've been invovled in some recent discussion on Multi-Tenenacy databases and in particular, the Shared Schema implementation:

http://www.sqlservercentral.com/Forums/FindPost610335.aspx
http://www.sqlservercentral.com/Forums/Topic529170-361-1.aspx
http://www.sqlservercentral.com/Forums/Topic569567-149-1.aspx

Multi-Tenancy databases are databases with multiple customer organizations ("tenants") all running the same application and all using the same database server.  The three stadard approaches to this are:

  1. Seperate Databases
  2. Same Database, Seperate Schemas
  3. Shared Schemas

In the last one, all of the tenants use the same tables, but a tenant_id is added to each table to distinguish them.  The is an MSDN article that explains this (http://msdn.microsoft.com/en-us/library/aa479086.aspx)

As it happens I designed and implemented a large Shared Schema Multi-Tenant DB several years ago, and  learned many insights from that.

Way back in March I answered question from two posters about the notorious Two-Hop Rule here: www.sqlservercentral.com/Forums/Topic471172-359-1.aspx#bm473248.  The thread has a good summary of the problem and cause by me, and an excellent post by Brian Kelley on how to use Kerberos to fix it.

A helpful tip from Microsoft MVP Gail Shaw here on how to bypass a bad Logon Trigger:

Connections via the DAC don't fire login triggers. From management studio, open a new query and specift admin: before the server name (admin:MyServerName) and use windows authentication. You must be sysadmin.

If remote DAC hasn't been enabled (the default), you will have to do that from a querying tool on the server itself. If the server doesn't have management studio, you can use SQLCMD. Specify the -A switch

Once in, you can disable the trigger.

DISABLE TRIGGER MyBrokenLoginTrigger ON ALL SERVER
Be very, very careful when writing login triggers. If, for any reason, the trigger fires an error of Sev 16 or higher (object does not exist, database not found, permission denied) the trigger fails and rolls back the login.
I've had a couple panicked, late-night phone calls because of these.

Something that I have wanted to do for a while is to write a Logon Trigger for SQL Server.  This bit of code from BOL demonstrates how:

CREATE TRIGGER connection_limit_trigger
ON ALL SERVER WITH EXECUTE AS 'login_test'
FOR LOGON
AS
BEGIN
IF ORIGINAL_LOGIN()= 'login_test'
AND (SELECT COUNT(*) FROM sys.dm_exec_sessions
WHERE is_user_process = 1
AND original_login_name = 'login_test') > 3
ROLLBACK;
END;

Next up: how to bypass a bad Logon Triger with the DAC (Dedicated Adminstrator Connection).

 

Copyright 2008 by R. Barry Young
 RBarryYoung.net  |  Terms Of Use  |  Privacy Statement