Database Security and Security Patterns
“If you want total security, go to prison. There you’re fed, clothed, given medical care and so on.
The only thing lacking . . . is freedom.”
—Dwight D. Eisenhower There are so many threats to your security that it is essential to remain ever vigilant—without ending up with your server in a bunker of lead wearing a tinfoil hat protecting data by keeping it completely inaccessible to any human eyes. Business needs connectivity to customers, and customers need connectivity to their data. Security is one of the most important tasks when setting up and creating a new application, yet it is often overlooked and dealt with late in the application building process. Whether or not this is acceptable is generally up to your requirements and how your application will be built, but at one point or another, your application team must take the time to get serious about security. Over and over, stories in the news report data being stolen, and the theft is inevitably due to poor security. In the last edition of this book, I used the example of an election official’s stolen laptop in my home city of Nashville, Tennessee; names, addresses, and partial social security numbers were stolen. Since then, there has been a steady stream of such stories, and probably the most high profile has been Sony’s Playstation network getting hacked and being down for months. Hence, if you are the architect of a database system that holds personal and private information, it could be you who becomes jobless with a stain on your career the size of the Mojave desert and possibly quite unemployed if it turns out to be your fault that data leaked out into the hands of some junkie looking for stuff to hock.
Security is on the minds of almost every company today, as evidenced by the constant stream of privacy policies that we continue to come across these days. They’re everywhere and if your company does business with anyone, it likely has one too. Let’s be clear: for far too many organizations, most security is implemented by hoping average users are as observant as Dorothy and her shoes. They have a lot of power if they just were adventurous enough to open a tool like Management Studio and start clicking around (or perhaps clicking their ruby slipper heels together three times.) Of course, fear is a reasonably good motivator for sticking to the marked path, and most average users aren’t too adventurous in the first place (work the help desk for a week, and you will know exactly what I mean). If they were, they’d not only discover how to fix the same problem they had yesterday, but also may just find that they have incredible power to see more than they need to see or to get back home to Kansas in the blink of an eye.
372
In this chapter, we will be covering the following topics:
• Database security prerequisites: We will cover some of the fundamentals that you need to understand before dealing with database-level access.
• Database securables: Once you are in the context of a database, you have a lot of built-in control over what users can access. In this section, we will cover what they are.
• Controlling access to data via T-SQL coded objects: We will look beyond direct access to data, at how you can restrict access to data in more granular ways using T-SQL procedures, views, and so on.
• Crossing database lines: Databases are ideally independent containers, but on occasion, you will need to access data that is not stored within the confines of the database. In this section, we will cover some of the caveats when implementing cross database access.
• Obfuscating data: Often, you cannot prevent a user from having access to some data, but you want the program to be able to decode the data only situationally. This is particularly important for personally identifiable data or financial data, so we encrypt the data to keep eyes out except where allowable.
• Monitoring and auditing: Turning on a “security camera” and watch what people are doing is sometimes the only real way to verify that you can provide adequate security, and in many cases you will do this and the aforementioned items.
Overall, we will cover a solid sampling of what you will need to secure your data but not the complete security picture, especially if you start to use some of the features of SQL Server that we are not covering in this book (Service Broker to name one). The goal of this chapter will be to shine a light on what is available, demonstrate some of the implementation patterns you may use, and then let you dig in for your exact needs.
I should also note that not everyone will use many, if any, of the guidelines in this chapter in their security implementations. Often, the application layer is left to implement the security alone, showing or hiding functionality from the user. This approach is common, but it can leave gaps in security, especially when you have to give users ad hoc access to the data or you have multiple user interfaces that have to implement different methods of security. My advice is to make use of the permissions in the database server as much as possible.
However, having the application layer control security isn’t a tremendous hole in the security of the organization, as long as the passwords used are seriously complex, encrypted, and extremely well guarded and ideally the data is accessed using Windows Authentication from the middle tier.
Database Access Prerequisites
In this initial section of this chapter, we are going to cover a few prerequisites that we will need for the rest of this chapter on database security. As a programmer, I have generally only been an advisor on how to configure most of the server beyond the confines of the individual database. Setting up the layers of security at the SQL Server instance and Windows Server level is not tremendously difficult, but it is certainly outside of the scope of this book on database design.
As a bit of an introduction to the prerequisites, I am going to cover a few topics to get you started on your way to implementing a secure environment:
• Guidelines for server security: In this section, I will cover some of the things you can use to make sure your server is configured to protect against outside harm.
• Principals and securables: All security in SQL Server is centered around principals (loosely, logins and users) and securables (stuff that you can limit access to).
• Connecting to the server: With changes in SQL Server 2012, there are now multiple ways to access the server. We will cover these in this section.
• Impersonation: Using the EXECUTE AS statement, you can “pretend” you are a different security principal to use the other users’ security. It is a very important concept for testing security that we will use often in this chapter.
Guidelines for Server Security
Even as strictly a database architect/programmer, you may need to set up, or at least validate, the security of your SQL Server installation. The following bulleted list contains some high-level characteristics you will want to use to validate the security of the server to protect your system from malicious hackers. It is not an exhaustive list, but it is a good start nonetheless:
Strong passwords are applied to all accounts, both Windows Authentication and SQL
•
Server authentication style (server and contained database style, new to SQL Server 2012)—certainly, all universally known system accounts have very strong passwords (such as sa, if you haven’t changed the name). Certainly, there are no blank passwords for any accounts!
SQL Server isn’t sitting unguarded on the Web, with no firewall and no logging of failed
•
login attempts.
• The guest user has been removed from all databases where it isn’t necessary.
Care has been taken to guard against SQL injection by avoiding query strings whereby
•
a user could simply inject SELECT name FROM sys.sql_logins and get a list of all your logins in a text box in your application that should display something like toothpaste brands. (Chapter 13 mentions SQL injection again; there I contrast ad hoc SQL with stored procedures.)
Application passwords are secured/encrypted and put where they can be seen by only
•
necessary people (such as the DBA and the application programmers who use them in their code). The password is encrypted into application code modules when using application logins.
You’ve made certain that few people have file-level access to the server where the data is
•
stored and, probably more important, where the backups are stored. If one malicious user has access to your backup file (or tape if you are still living in the past), that person has access to your data by simply attaching that file to a different server, and you can’t stop him or her from accessing the data (even encryption isn’t 100 percent secure if the hacker has virtually unlimited time).
You have taken all necessary precautions to make sure that the physical computer where
•
the data is stored cannot be taken away as a whole. Even things like encryption aren’t completely effective if the data needed to decrypt the values is available on one of the machines that has been stolen along with the machine with the encrypted values.
Your SQL Server installation is located in a very secure location. A Windows server, just
•
like your laptop, is only as secure as the physical box. Just like on any spy TV show, if the bad guys can access your physical hardware, they could boot to a CD or USB device and have access to your hard disks (note that using transparent data encryption (TDE) can help in this case).
374
All features that you are not using are turned off. To make SQL Server as secure as
•
possible out of the box, many features are disabled by default and have to be enabled explicitly before you can use them. For example, remote administrator connections, Database Mail, CLR programming, and others are all off by default. You can enable these features and others using the sp_configure stored procedure.
Principals and Securables
At the very core of security in SQL Server are the concepts of principals and securables. Principals are those objects that may be granted permission to access particular database objects, while securables are those objects to which access can be controlled. Principals can represent a specific user, a role that may be adopted by multiple users, or an application, certificate, and more. There are three sorts of SQL Server principals that you will deal with:
• Windows principals: These represent Windows user accounts or groups, authenticated using Windows security.
• SQL Server principals: These are server-level logins or groups that are authenticated using SQL Server security.
• Database principals: These include database users, groups, and roles, as well as application roles.
Securables are the database objects to which you can control access and to which you can grant principals permissions. SQL Server distinguishes between three scopes at which different objects can be secured:
• Server scope: Server-scoped securables include logins, HTTP endpoints, event
notifications, and databases. These are objects that exist at the server level, outside of any individual database, and to which access is controlled on a server-wide basis.
• Database scope: Securables with database scope are objects such as schemas, users, roles, and CLR assemblies, DDL triggers, and so on, which exist inside a particular database but not within a schema.
• Schema scope: This group includes those objects that reside within a schema in a database, such as tables, views, and stored procedures. A SQL Server 2005 and later schema
corresponds roughly to the owner of a set of objects (such as dbo) in SQL Server 2000.
These concepts will come into play in all of the following sections as we walk through the different ways that you will need to secure the data in the database. You can then grant or deny usage of these objects to the roles that have been created. SQL Server uses three different security statements to give or take away rights from each of your roles:
• GRANT: Gives the privilege to use an object.
• DENY: Denies access to an object, regardless of whether the user has been granted the privilege from any other role.
• REVOKE: Used to remove any GRANT or DENY permissions statements that have been applied to an object. This behaves like a delete of an applied permission, one either granted or denied.
Typically, you’ll simply give permissions to a role to perform tasks that are specific to the role. DENY is then used only in “extreme” cases, because no matter how many other times the user has been granted privileges to an object, the user won’t have access to it while there’s one DENY.
For a database or server right, you will use syntax like GRANT <privilege> TO <principal> [WITH GRANT OPTION];
The WITH GRANT OPTION will allow the principal to grant the privilege to another principal.
For the most part, this book will deal primarily with database object privileges, as database and server privileges are almost always an administrative consideration. They allow you to let principals create objects, drop objects, do backups, view metadata, and so on.
For database objects, there is a minor difference in the syntax, in that the securable that you will be granting rights to will be specified. For example, to grant a privilege on a securable in a database, the command would be as follows:
GRANT <privilege> ON <securable> to <principal> [WITH GRANT OPTION];
Next, if you want to remove the privilege, you have two choices. You can either REVOKE the permission, which just deletes the granted permission, or you can DENY the permission. Execute the following:
REVOKE <privilege> FROM <securable> to <principal>;
I haven’t covered role membership yet (it’s covered later in this chapter), but if the user were a member of a role that had access to this object, the user would still have access. However, execute the following code:
DENY <privilege> ON <securable> to <principal>;
The use of DENY will prohibit the principal from using the securable, even if they have also been granted access by means of another securable. To remove DENY, you again use the REVOKE command. This will all become clearer when I cover roles later in this chapter, but in my experience, DENY isn’t a typical thing to use on a principal’s privilege set. It’s punitive in nature and is confusing to the average user. More commonly, users are given rights and not denied access.
Another bit of notation you will see quite often is to denote the type of securable before the securable where it is not the default. For objects that show up in sys.objects that have security granted to them (from Books Online, these are table, view, table-valued function, stored procedure, extended stored procedure, scalar function, aggregate function, service queue, or synonym), you can simply reference the name of the object:
GRANT <privilege> ON <securable> to <database principal>;
For other types of objects, such as schemas, assemblies, and search property lists, to name a few, you will specify the type in the name. For example, for a schema GRANT, the syntax is
GRANT <privilege> ON SCHEMA::<schema securable> to <database principal>;
Note that, for objects, you can also use the a prefix of OBJECT::, as in the following:
GRANT <privilege> ON OBJECT::<securable> to <database principal>;
Connecting to the Server
Before we finally get the database security, we need to cover accessing the server. Prior to SQL Server 2012, there was a single way to access a database. This method is still pretty much the norm and is basically as follows: A login principal is defined that allows a principal to access the server using Windows credentials, a login that is managed in the SQL Server instance (known as SQL Server authentication), or one of several other methods including a certificate or an asymmetric key. The login is then mapped to a user within the database to gain access.
376
The additional method in SQL Server 2012 uses a new concept of a contained database (CDB). I will cover the broader picture and a bit of the management of CDB as a whole later in the chapter when I cover cross-database security, but I do need to introduce the syntax and creation of the database here as it is, from a coding standpoint, largely a security question. Contained databases in SQL Server 2012 are the initial start of making databases essentially standalone containers that can be moved from server to server with little effort (and likely eventually to Azure as well).
In this section, I will provide two examples of connecting to the server:
Using the classic approach of a login and user
•
Access the database directly using the containment model
•
Using Login and User
To access the server, we will create a server principal known as a login. There are two typical methods that you will use to create almost all logins. The first method is to map a login to a Windows Authentication principal. This is done using the CREATE LOGIN statement. The following example would create the login I have on my laptop for writing content:
CREATE LOGIN [DENALI-PC\AlienDrsql] FROM WINDOWS
WITH DEFAULT_DATABASE=tempdb, DEFAULT_LANGUAGE=us_english;
The name of the login is the same as the name of the Windows principal, which is how they map together.
So on my local virtual machine named DENALI-PC, I have a user named AlienDrsql (I have an Alienware PC, hence the name; I am not a weirdo; I promise.) The Windows principal can be a single user or a Windows group. For a group, all users in the group will gain access to the server in the same way and have the exact same permission set. This is, generally speaking, the most convenient method of creating and giving users rights to SQL Server.
The second way is to create a login with a password:
CREATE LOGIN [Fred] WITH PASSWORD=N'password' MUST_CHANGE, DEFAULT_DATABASE=[tempdb], DEFAULT_LANGUAGE=[us_english], CHECK_EXPIRATION=ON, CHECK_POLICY=ON;
If you set the CHECK_POLICY setting to ON, the password will need to follow the password complexity rules of the Windows server it is created on, and CHECK_EXPIRATION, when set to ON, will require the password to be changed based on the policy of the Windows server as well. Generally speaking, the most desirable method is to use Windows Authentication for the default access to the server where possible, since keeping the number of passwords a person has to a minimum makes it less likely for them to tape the password up on the wall for all to see. Of course, using Windows Authentication can be troublesome in some cases where SQL Server are located in a DMZ with no trust between domains so you have to resort to SQL Server authentication, so use very complex passwords and (ideally) change them often.)
In both cases, I defaulted the database to tempdb, because it requires a conscious effort to go to a user database and start building, or even dropping, objects. However, any work done in tempdb is deleted when the server is stopped. This is actually one of those things that may save you more times than you might imagine.
Often, a script gets executed and the database is not specified, and a bunch of data gets created—usually in master (the default database if you haven’t set one explicitly…so the default default database.) I have built more test objects on my local SQL Server in master over the years than I can count.
Once you have created the login, you will need to do something with it. If you want to make it a system administrator–level user, you could add it to the sysadmin group, which is something that you will want to do on your local machine with your default user (though you probably already did this when you were installing the