An early step in implementing countermeasures to potential threats in SQL Server is to ensure that you have a full understanding of the security model. SQL Server 2017 provides a rich security framework, with overlapping layers of security that help database administrators (DBAs) counter risks and threats in a manageable way.
It is important for DBAs to understand the SQL Server security model so that they can implement the technologies in the way that best fits the needs of their organization and applications, while minimizing the amount of security administration that is required. This chapter discusses the implementation of the security hierarchy in SQL Server 2016.
Security Model Definitions
Entity | Definition |
---|---|
Principal | A security principal is an entity, such as a user. |
Securable | A securable is data, an artifact, or metadata. |
Permission | Permissions are rights that are granted or denied to a security principal, to define the principal’s permitted access to a securable. |
Tip
Passive security refers to auditing activity. SQL Server Audit will be discussed in Chapter 3.
When implementing security in SQL Server, it is always important to consider and apply the principal of least privilege. In other words, users and other principals must only be given access to securables that they require to perform their day-to-day operations. For example, if the security principal UserA only needs to read data from the securable TableA, then UserA should only be given read permissions to TableA, not permission to write to the table.
Security Principal Hierarchy
The diagram shows that a Login, created within the SQL Server instance, can be mapped to a local Windows user or group or to a domain user or group. Usually, in an Enterprise environment, this is a domain user or group. (A group is a collection of users that are granted permissions as a unit.) This eases the administration of security. Imagine (as described earlier in this section) that a new starter joins the sales team. When he is added to the domain group called SalesTeam, which already has all of the required permissions to file system locations, SQL Server databases, and so on, he will immediately inherit all required permissions to perform his role.
The diagram also illustrates how local server accounts or domain accounts and groups can be mapped to a user at the database level (a database user without login). This is part of the functionality of contained databases. This technology was introduced in SQL Server 2012 to support high availability with AlwaysOn Availability Groups. Contained database authentication is discussed later in this chapter.
SQL Server Logins, which are mapped to local server or domain level users or groups, are created at the SQL Server instance level. At the instance level, it is also possible to create SQL Server Logins, which use 2nd-tier authentication, if you are using mixed-mode authentication (explained in the Instance Level Security section of this chapter).
Both of these types of Login can be added to fixed server roles and user-defined server roles at the instance level. Doing this allows you to grant the principal a common set of permissions to instance-level objects, such as linked servers and endpoints. You can also map Logins to database users, which in turn can be granted permissions to database level objects.
Database users reside at the database level of the hierarchy. You can grant them permissions directly to schemas and objects within the database, or you can add them to database roles. Database roles are similar to server roles, except they are used to grant a common set of permissions at the database layer, instead of the instance layer. Database layer securables include schemas, tables, views, and stored procedures, etc.
Instance Level Security
Implementing instance level security involves creating and managing Logins, Credentials, and Server Roles. Securables at the instance level include databases, endpoints, and AlwaysOn Availability Groups. The following sections will discuss Logins, server roles, and credentials.
Tip
Cryptographic Providers and SQL Server Audits are also administered at the instance level. SQL Server Audit will be discussed in Chapter 3, and Cryptographic Providers will be discussed in Chapter 5.
Logins
Since SQL Server 2012, it has been possible for users to authenticate directly to a database, as part of contained database functionality. Generally, however, database engine users will need to authenticate at the instance level. SQL Server supports two authentication modes at the instance level: Windows Authentication and Mixed Mode Authentication.
When an instance is in Windows Authentication mode, users must authenticate to either the local server, or to the domain, before they can access the SQL Server instance. A Login is created, within the SQL Server instance, which maps to either their Windows user or a Windows group, which contains their Windows user. The SID (Security Identifier) of the Windows principal is stored in the Master database of the instance.
Tip
In addition to creating a login mapped to a Windows user or group, you can also map a login to a certificate or an asymmetric key. Doing so does not allow a user to authenticate to the instance by using a certificate, but it does allow for code signing so that permissions to procedures can be abstracted, rather than granted directly to a Login. This helps when you are using dynamic SQL, which breaks the ownership chain; in this scenario, when you run the procedure, SQL Server combines the permissions from the user who called the procedure and the user who maps to the certificate.
When an instance is configured to use Mixed Mode authentication, it is still possible to create a Login that maps to a Windows user or Windows group, but it is also possible to create 2nd-tier Logins, known as SQL Logins . These logins have their name, password, and SID stored in the Master database of the instance, and these details are verified when a user connects to the instance. A user can then authenticate to the instance using this user name and password, without the need for prior authentication to the server or domain.
Legacy applications that require a 2nd-tier Login
Access from outside of the domain (such as a Linux server)
Environments where security is implemented in the application tier and a single SQL Login connects to the database engine
Creating a Login
CREATE LOGIN Options for Windows Security Principal
Option | Description |
---|---|
DEFAULT_DATABASE | Specifies a “landing” database for the Login. This is the database, to which their context will be scoped when they initially authenticate to the instance. This scope can be overwritten in the connection string. |
DEFAULT_LANGUAGE | Specifies the default language that will be assigned to the Login. If omitted, then this is configured to be the default language of the instance. |
Create a Login from a Windows Security Principal
CREATE LOGIN Options for SQL Login
Option | Description |
---|---|
PASSWORD | Specifies the initial password that will be used by the Login, in clear text |
PASSWORD HASHED | Specifies a hashed representation of the initial password that will be used by the Login* |
SID | Specifies the SID of the Login* |
DEFAULT_DATABASE | Specifies a “landing” database for the Login. This is the database, to which their context will be scoped when they initially authenticate to the instance. This scope can be overwritten in the connection string. |
DEFAULT_LANGUAGE | Specifies the default language that will be assigned to the Login. If omitted, then this is configured to be the default language of the instance. |
CHECK_POLICY | Specifies that the Login’s password must meet the same requirements, such as length and complexity, as Windows users, as enforced by Group Policy, or Local Security Policy. |
CHECK_EXPIRATION | Specifies that the Login’s password will expire, in line with password expiration policy configured for Windows users, as enforced by Group Policy or Local Security Policy. This option is only valid if CHECK_POLICY is also specified. |
MUST_CHANGE | Specifies that the user must change their password the first time that they log in to the instance. This option can only be used if CHECK_EXPIRATION is also specified. |
Create a SQL Login
We have provided the name for the Login in the Login name field at the top of the screen and chosen the radio button that indicates that it will be an SQL Server (2nd-tier Login). We can then enter the accounts password. Because the password is not shown as we type, we must enter it twice, to ensure that we have entered it correctly.
In the next section of the General page, we can specify which (if any) Windows policies should be applied to the Login. Remember that they are cumulative, so for example, you cannot select the option to Enforce password expiration, unless you also select the Enforce password policy option. At the bottom of the screen, we can specify a default database for the login to land in and the default language that should be configured for the Login.
Tip
You will notice that there are multiple pages of the dialog box, providing other management options. These will be discussed as you move through the chapter.
Migrating Logins Between Instances
There will be occasions where you need the same Login to exist on multiple servers. This may be because of a server migration, or it may be because you are implementing DR and need users to be able to reconnect to the DR instance transparently.
For Logins that are created from Windows security principals, this poses no problem whatsoever. As discussed earlier in this chapter, the SID of the Windows security principal is stored in the Master database, but the principal itself is managed by Windows. Therefore, you can simply create a Login on the second instance and map it to the same Windows user or group.
If you are working with SQL Logins, however, this scenario is more challenging to deal with, as the SQL Server instance hosts and manages the SID and the password of the Login. This means that if you create a Login with the same name and password on a different instance, then they will still be completely different, isolated principals. Once you move, or failover your databases to the second instance, the database users will become orphaned. This means that they no longer map to a login. This is because the login on the second instance has a different SID, despite having the same name and password.
You can manage this issue at point of failover by using an ALTER USER statement WITH LOGIN option.
Tip
The ALTER USER WITH LOGIN syntax replaces the functionality of the deprecated sp_change_users_logins system stored procedure.
Remap a Database User to a Login
Tip
Database users will be discussed in the Database Level Security section of this chapter.
Naturally, it is more efficient, however, if users are already mapped to their correct Login at the point that the databases are moved or failed over. This can be achieved by creating the Login and manually assigning it the correct SID.
This is where the WITH SID option mentioned in Table 2-1 comes into play. If you script out the SQL Login on an instance and include the SID, then you can use the script to pre-create the Login on the DR instance, with the correct SID, meaning that the database users will automatically map to the correct Login, without the need to alter them. Listing 2-4 demonstrates how this can be achieved using a SQLCMD script, which will replicate a Login called Danni from ProdInstance1 to DRInstance1.
Tip
The script below assumes instances named ProdInstance1 and DRInstance1 on a server named CARTERSECURESAFE. Please update the script to use your own server configuration. The script must be running in SQLCMD mode, or it will not execute.
Migrate a Login to a New Instance
Caution
This method works perfectly well in a SQLCMD script, although it is worth noting that depending on your server and network configuration, you may be sending the password across the wire in plain text. If this is the case, then you should follow the approach discussed next to hash the password.
An issue will arise with the approach that we have discussed, in a common scenario, where you wish to script out the Logins for an environment and keep them under source control, so that you can apply them to other environments as required. In this scenario you will be storing passwords in plain text. This poses an obvious security risk and should be avoided. Instead, you should script out the SQL Logins, so that the passwords are stored in the same encrypted format that SQL Server stores them in.
This approach can be achieved by scripting the Logins with passwords based on the password_hash column of the sys.sql_logins view. Instead of this, however, we will script the Logins using the HASHBYTES() function to generate the password hash for the Login. This technique is demonstrated in Listing 2-5 and gives you an insight into how SQL Server hashes the passwords. The script will generate the DDL (Data Definition Language) statements required to script out all enabled SQL Logins from an instance with hashed passwords. The script can subsequently be placed in source control and run on other environments as required.
Tip
In normal operations, you should take the approach of taking the password hash from the sys.sql_logins view. This is preferable, as it does not require knowledge of the Login’s plain text password.
HASHBYTES() Parameters
Parameter | Description |
---|---|
Algorithm | The algorithm that the function will use to hash the data. Acceptable values are: • MD2 • MD4 • MD5 • SHA • SHA1 • SHA2_256 • SHA2_512 |
Input | The value that will be hashed by the function |
CRYP_GEN_RANDOM Parameters
Parameter | Description |
---|---|
Length | The length of the number to be generated |
Seed | An optional randomization seed |
Script Out Logins with Hashed Passwords
Server Roles
Fixed Server Roles
Role | Description |
---|---|
Sysadmin | The sysadmin role gives administrative permissions to the entire instance. A member of the sysadmin role can perform any action within the instance of the SQL Server relational engine. |
Blkadmin | In conjunction with the INSERT permission on the target table within a database, the bulkadmin role allows a user to import data from a file using the BULK INSERT statement. This role is normally given to service accounts that run ETL processes. |
Dbcreator | The dbcreator role allows its members to create new databases within the instance. Once a Login creates a database, that Login is automatically the owner of that database and is able to perform any action inside it. |
Diskadmin | The diskadmin role gives its members the permissions to manage backup devices within SQL Server. |
Processadmin | Members of the processadmin role are able to stop the instance from T-SQL or SSMS (SQL Server Management Studio). They are also able to kill running processes. |
Public | All Logins are added to the public role. Although you can assign permissions to the public role, this does not fit with the principle of least privilege. This role is normally only used for internal SQL Server operations, such as authentication to TempDB. |
Securityadmin | Members of the securityadmin role are able to manage Logins at the instance level. For example, members may add a Login to a server role (except sysadmin) or assign permissions to an instance-level resource, such as an endpoint. However, they cannot assign permissions within a database to database users. |
Serveradmin | serveradmin combines the diskadmin and processadmin roles. In addition to being able to start or stop the instance, however, members of this role can also shut down the instance using the SHUTDOWN T-SQL statement. The subtle difference here is that the SHUTDOWN command gives you the option of not running a CHECKPOINT in each database if you use it with the NOWAIT option. Additionally, members of this role can alter endpoints and view all instance metadata. |
setupadmin | Members of the setupadmin role are able to create and manage linked servers. |
Alter any availability group
Alter any endpoint
Create availability group
Create endpoint
You can then add the junior DBAs to this role, who are not authorized to be made members of the sysadmin fixed server role but who need to manage the high availability and disaster recovery of the instance. The script in Listing 2-6 demonstrates how to create the server role and grant it the relevant permissions.
GRANT
DENY
REVOKE
Create Server Role and Grant Permissions
Add Login to Server Role
Credentials
Credentials are used to provide ability to access resources that are external to the SQL Server instance. SQL Logins can use credentials to access operating system level resources; SQL Server Agent Proxy Accounts use Credentials to access SQL Server Agent Subsystems, such as PowerShell or CmdExec; and Credentials are also used when taking backups to Azure.
Create a Credential for Backups to Azure
Database-Level Security
At the database level, security is implemented by assigning permissions to security principals—namely, users and database roles. The following sections will discuss both of these types of principal.
Users
A database user will typically be created from a Login at the instance level. This means that the same instance level security principal can be granted permissions on resources in multiple databases. Since SQL Server 2012, however, it has also been possible to create a user without a Login. The following sections will describe each of these types of database user.
Users With a Login
Options When Creating a User From a Login
Option | Description |
---|---|
DEFAULT_SCHEMA | Specifies the default schema for a user. Schemas will be discussed in Chapter 4. |
ALLOW_ENCRYPTED_VALUE_MODIFICATIONS | Specifies that users will be allowed to bulk copy encrypted data, without first decrypting it. Encryption is discussed in Chapter 5. |
Create a User From a Login
The script creates a user that has the same name as the Login. Whilse this is not mandatory, it is sensible, as it aids the administration of the security principals and makes the hierarchy of principals transparent to new DBAs joining your team. The script sets the user’s default schema to be Sales. This means that the Danni user will be able to reference objects in the Sales schema using one-part names. If no default schema is specified for a user, then their default schema will be dbo.
We could also use the GUI to create this database user. One option here would be to drill through Databases | WideWorldImporters | Security in Object Explorer and then open the New User dialog box by selecting New from the context menu of users.
Another option would be to use the Login Properties dialog box, which you will already be familiar with, from the previous sections of this chapter. To create a user for the Reuben Login in the WideWorldImporters database, drill through Security | Logins in Object Explorer before double-clicking the Reuben Login and selecting the User Mapping page.
Note
Schemas will be discussed in further detail in Chapter 4.
Users Without a Login
When creating a user that is not associated with a Login, the user can either be mapped to a Windows security principal, or it can be created using SQL Server authentication.
Note
Users can only be created using SQL Server authentication if the database is configured with a containment level of Partial. Contained databases are beyond the scope of this book, but you can learn more about them at https://msdn.microsoft.com/en-us/library/ff929071.aspx .
Options When Creating a User Without a Login
Option | Description |
---|---|
DEFAULT_SCHEMA | Specifies the default schema for a user. Schemas will be discussed in Chapter 4. |
ALLOW_ENCRYPTED_VALUE_MODIFICATIONS | Specifies that users will be allowed to bulk copy encrypted data, without first decrypting it. Encryption is discussed in Chapter 5. |
DEFAULT_LANGUAGE | Specify the default language for the user. This option can be expressed as an lcid, a language name, or a language alias. |
SID | Specify the SID that will be associated with the user (SQL Server authentication only). This can be used to create users in multiple databases, that share the same SID. This can help with high availability and disaster recovery techniques, using AlwaysOn Availability Groups. |
Create a User From a Windows Security Principal
Create a User With Password in a Contained Database
- 1.
Create a user with password in the first database.
- 2.
Retrieve the user’s SID from sys.database_principals.
- 3.
Create the user in additional databases, specifically supplying the SID that you have recovered from the metadata.
sys.database_principals Columns
Column | Description |
---|---|
name | The name of the security principal |
principal_id | The ID of the security principal. This ID is only unique within the database. |
type | A single character abbreviation of the type description |
type_desc | A description of the type of security principal. Possible values are: • APPLICATION_ROLE • CERTIFICATE_MAPPED_USER • EXTERNAL_USER • WINDOWS_GROUP • ASYMMETRIC_KEY_MAPPED_USER • DATABASE_ROLE • SQL_USER • WINDOWS_USER • EXTERNAL_GROUPS |
default_schema_name | The name of the principals default schema |
create_date | The date and time that the principal was created |
modify_date | The date and time that the principal was last modified |
owning_principal_id | The ID of the security principal that is marked as the owner of the principal. This will be 1, which is the ID of dbo, for all principals except database roles. |
sid | The SID of the security principal |
is_fixed_role | Indicates if the principal is a fixed database role. • 1 indicates that the principal is a fixed database role. • 0 indicates that the principal is not a fixed database role. |
authentication_type | Indicates how the principal authenticates to the database. Possible values are listed below: • 0 indicates No authentication. • 1 indicates Instance authentication. • 2 indicates Database authentication. • 3 indicates Windows Authentication. |
authentication_type_desc | A textual description of the authentication type. Possible values are: • NONE • INSTANCE • DATABASE • WINDOWS |
default_language_name | The name of the default language assigned to the principal |
default_language_lcid | The lcid of the default language assigned to the principal |
Retrieve SIDs for Users With a Password
Database Roles
Fixed Database Roles
Role | Description |
---|---|
db_accessadmin | Members of this role can add and remove database users from the database. |
db_backupoperator | The db_backupoperator role gives users the permissions they need to back up the database, natively. It may not work for third-party backup tools, such as CommVault or Backup Exec, since these tools often demand sysadmin rights. |
db_datareader | Members of the db_datareader role can run SELECT statements against any table in the database. It is possible to override this for specific tables by explicitly denying a user, the permissions to read those tables. DENY always overrides GRANT |
db_datawriter | Members of the db_datawriter role can perform DML (Data Manipulation Language) statements against any table in the database. It is possible to override this for specific tables by specifically denying a user the permissions to write to a table. DENY will always override GRANT. |
db_denydatareader | The db_denydatareader role denies the SELECT permission against every table in the database. |
db_denydatawriter | The db_denydatawriter role denies its members the permissions to perform DLM statements against every table in the database. |
db_ddladmin | Members of this role are given the ability to run CREATE, ALTER, and DROP statements against any object in the database. This role is rarely used, but I have seen a couple of examples of poorly written applications that create database objects on the fly. If you are responsible for administering an application such as this, then the ddl_admin role may be useful. |
db_owner | Members of the db_owner role can perform any action within the database that has not been specifically denied. |
db_securityadmin | Members of this role can GRANT, DENY, and REVOKE a user’s permissions to securables. They can also modify role memberships, with the exception of the db_owner role. |
As well as the fixed roles, it is also possible to create your own user-defined database roles. This simplifies administration by allowing DBAs to create roles that map to requirements of business teams that use a specific database. For example, an administrator of the WideWorldImporters database may create a role for salespeople, a role for the procurement department and a role for the manufacturing department.
Create SalesRole
Summary
SQL Server provides a flexible hierarchy to implement security. Role-based security is available out-of-the-box, and database administrators should embrace this in order to simplify the administration of security.
The database engine supports two methods of authentication: Windows authentication and SQL Server authentication. The latter is SQL Server’s implementation of 2nd-tier authentication. Windows authentication should be used, unless there is a good reason not to, as it is more secure.
Database engine users will typically authenticate to the instance via a Login. This Login will then map to users within the databases to which they require access. It is also possible, however, for users to authenticate directly to a database. This is known as a user without a Login. When a user without a Login is implemented, they can authenticate via Windows authentication, or if the database is contained, then SQL Server authentication can alternatively be used.