{"id":83682,"date":"2019-03-26T18:33:35","date_gmt":"2019-03-26T18:33:35","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=83682"},"modified":"2021-04-27T14:00:52","modified_gmt":"2021-04-27T14:00:52","slug":"introduction-to-sql-server-security-part-4","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/devops\/data-privacy-and-protection\/introduction-to-sql-server-security-part-4\/","title":{"rendered":"Introduction to SQL Server Security \u2014 Part 4"},"content":{"rendered":"<h4>The series so far:<\/h4>\n<ol>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/data-protection-and-privacy\/introduction-to-sql-server-security-part-1\/\">Introduction to SQL Server Security \u2014 Part 1<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/data-protection-and-privacy\/introduction-to-sql-server-security-part-2\/\">Introduction to SQL Server Security \u2014 Part 2<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/data-protection-and-privacy\/introduction-to-sql-server-security-part-3\/\">Introduction to SQL Server Security \u2014 Part 3<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/data-protection-and-privacy\/introduction-to-sql-server-security-part-4\">Introduction to SQL Server Security \u2014 Part 4<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/data-protection-and-privacy\/introduction-to-sql-server-security-part-5\">Introduction to SQL Server Security \u2014 Part 5<\/a><\/li>\n<li><a href=\"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/data-protection-and-privacy\/introduction-to-sql-server-security-part-6\/\">Introduction to SQL Server Security\u00a0<span>\u2014 <\/span>Part 6<\/a><\/li>\n<li style=\"list-style-type: none;\">\u00a0<\/li>\n<\/ol>\n\n<p>In the previous articles in this series, I introduced you to SQL Server security principals and the part they play in authentication and authorization. At their most basic, principals are server and database entities that can request access to SQL Server resources. The most common principals are server logins, server roles, database users, and database roles.<\/p>\n<p>SQL Server provides a number of built-in principals that are added automatically when you install a SQL Server instance or create a database. In some cases, it\u2019s not always clear when and how to use these principals. For example, SQL Server automatically adds the <code>public<\/code> server and database roles. Unlike other fixed roles, you can modify their permissions. However, this can impact common database operations, so you must proceed cautiously if changing the default configuration.<\/p>\n<p>In this article, I dig into some of the more confusing built-in principals to help you better understand how they fit into the larger authentication picture. I created the examples for this article on an instance of SQL Server 2017, but much of the information applies to SQL Server editions going back to 2014 or earlier. Regardless of which edition you\u2019re using, you should understand how these built-in principals work before enabling them or modifying them in your production environments.<\/p>\n<h2>The sa Login<\/h2>\n<p>The <code>sa<\/code> login, short for <em>system administrator,<\/em> is one of the riskiest server-level principals in SQL Server. It\u2019s automatically added as a member of the <code>sysadmin<\/code> fixed server role and, as such, has all permissions on that instance and can perform any activity. If the login were hacked, the attacker could do unlimited damage.<\/p>\n<p>You cannot drop the <code>sa<\/code> login, but you can disable it. If you select Windows Authentication when installing SQL Server, the database engine assigns a random password to the account and automatically disables it. If you then switch to SQL Server Authentication, the login remains disabled, and you must manually enable it.<\/p>\n<p>If you select SQL Server Authentication during installation, the account will be enabled, but you must provide a password, so make sure it\u2019s a strong one. Even if the login is enabled, you should avoid using it for your applications. In fact, unless a connecting system absolutely requires the <code>sa<\/code> login, it\u2019s best that the account remains disabled.<\/p>\n<p>You can verify whether the <code>sa<\/code> login is disabled by querying the <code>sys.server_principals<\/code> system view, using a <code>SELECT<\/code> statement similar to the following:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE master;\r\nGO\r\nSELECT principal_id, type_desc, is_disabled\r\nFROM sys.server_principals\r\nWHERE name = 'sa';<\/pre>\n<p>If the login is disabled, the <code>is_disabled<\/code> value will be <code>1<\/code>, as shown in Figure 1.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"288\" height=\"97\" class=\"wp-image-83683\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-52.png\" \/><\/p>\n<p class=\"caption\">Figure 1. The sa login on a SQL Server instance<\/p>\n<p>You cannot remove the <code>sa<\/code> login from the <code>sysadmin<\/code> server role, but you can verify its membership:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT member.name\r\nFROM sys.server_role_members rm\r\nJOIN sys.server_principals role  \r\n  ON rm.role_principal_id = role.principal_id  \r\nJOIN sys.server_principals member  \r\n  ON rm.member_principal_id = member.principal_id\r\nWHERE role.name = 'sysadmin';  <\/pre>\n<p>The statement uses the <code>sys.server_role<\/code> system view and <code>sys.server_principals<\/code> system view to return the members of the <code>sysadmin<\/code> role, as shown in Figure 2.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"281\" height=\"199\" class=\"wp-image-83684\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-53.png\" \/><\/p>\n<p class=\"caption\">Figure 2. Principals assigned to the sysadmin server role<\/p>\n<p>If you find yourself in a situation that requires the <code>sa<\/code> login, you must enable the account before it can be used. To do so, run an <code>ALTER<\/code> <code>LOGIN<\/code> statement that sets the <code>ENABLE<\/code> property and then run the statement again to assign a password to the login, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE master;\r\nGO\r\nALTER LOGIN sa ENABLE;  \r\nGO  \r\nALTER LOGIN sa WITH PASSWORD = 'tempPW@56789';\r\nGO <\/pre>\n<p>Of course, if you were to enable the login, you would need to assign a much stronger password than the one shown here. Moreover, if you\u2019re just trying all this out, be sure to perform your modifications in a non-production environment.<\/p>\n<p>You can test the <code>sa<\/code> login in SQL Server Management Studio (SSMS) by launching a new query and changing the connection. The following steps describe how to change that connection:<\/p>\n<ol>\n<li>In SSMS, launch a new query.<\/li>\n<li>On the new query tab, right-click a blank area in the query editor, point to <em>Connection<\/em> and click <em>Change Connection<\/em>.<\/li>\n<li>In the <em>Connect to Database Engine<\/em> dialog box, select <em>SQL Server Connection<\/em> from the <em>Authentication<\/em> drop-down list. The form will be updated to include login and password text boxes.<\/li>\n<li>Type <code>sa<\/code> for the login and the password you provided when you enabled the login, and then click <em>Connect<\/em>.<\/li>\n<\/ol>\n<p>After you\u2019ve logged in as <code>sa<\/code>, try running a query such as the following:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT * FROM sys.server_principals;<\/pre>\n<p>The query should run with no problem because the <code>sa<\/code> login is permitted to do anything on the server. On my test system, the <code>SELECT<\/code> statement returns 32 rows.<\/p>\n<p>After you\u2019ve verified the login, run the following <code>ALTER<\/code> <code>LOGIN<\/code> statement to disable it:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">ALTER LOGIN sa DISABLE; \r\nGO<\/pre>\n<p>Next, launch a new query and again try connecting with the <code>sa<\/code> login, following the same steps outlined above. This time, you should receive an error similar to the one shown in Figure 3.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"624\" height=\"174\" class=\"wp-image-83685\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-54.png\" \/><\/p>\n<p class=\"caption\">Figure 3. The error generated when trying to connect with the disabled sa login<\/p>\n<p>Whenever possible, you should ensure that the <code>sa<\/code> login remains disabled. Also, consider renaming the login to provide another line of defense. Because the login is so well known, it\u2019s a frequent target for cybercriminals who have lots of tools at their disposal for cracking passwords. Once they\u2019ve gotten into your databases, you might not be able to stop them until it\u2019s too late.<\/p>\n<h2>Certificate-Based Logins<\/h2>\n<p>When poking around SQL Server, you\u2019re likely to notice a set of server logins whose names start and end with double hash marks, as in <code>##MS_PolicySigningCertificate##<\/code>. The logins are certificate-mapped accounts used by the database engine for internal purposes. You should not delete these or mess with them in any way.<\/p>\n<p>To retrieve a list of certificate-based logins, run the following query:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT name, type_desc, is_disabled\r\nFROM sys.server_principals\r\nWHERE name LIKE '##%'\r\nORDER BY name;<\/pre>\n<p>Figure 4 shows the results that the statement returns on my system.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"535\" height=\"226\" class=\"wp-image-83686\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-55.png\" \/><\/p>\n<p class=\"caption\">Figure 4. Certificate-based logins on a SQL Server instance<\/p>\n<p>Notice that two of the logins are marked as disabled and their type listed as <code>SQL_LOGIN<\/code>, rather than <code>CERTIFICATE_MAPPED_LOGIN<\/code>. Despite these differences, Microsoft documentation specifically states that these two are also considered to be certificate-mapped logins.<\/p>\n<p>You can view the certificates associated with the enabled certificate-based logins by running the following <code>SELECT<\/code> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT name FROM sys.certificates\r\nWHERE name LIKE '##%'\r\nORDER BY name;<\/pre>\n<p>On my system, the statement returns the results shown in Figure 5, confirming that there\u2019s a certificate for each enabled login.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"394\" height=\"212\" class=\"wp-image-83687\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-56.png\" \/><\/p>\n<p class=\"caption\">Figure 5. Certificates associated with certificate-mapped logins<\/p>\n<p>For the most part, you don\u2019t have to worry about the logins, unless you\u2019re involved in an audit or performing a security scan, in which case you might need to explain to the powers-that-be why these logins are showing up in your reports.<\/p>\n<h2>The public Server and Database Roles<\/h2>\n<p>Each SQL Server instance contains the <code>public<\/code> fixed server role, and each database (including system databases) contains the <code>public<\/code> fixed database role. All logins belong to the <code>public<\/code> server role, and all database users belong to the <code>public<\/code> database role. You cannot drop either role, and you cannot add members to or remove members from either role.<\/p>\n<p>The database engine assigns a set of permissions to the roles by default. Logins inherit all permissions granted to the <code>public<\/code> server role unless a login has been specifically granted or denied those permissions. The same goes for the <code>public<\/code> database role. Users inherit all permissions unless they\u2019ve been specifically granted or denied permissions.<\/p>\n<p>To view the permissions assigned to the <code>public<\/code> server role, run the following <code>SELECT<\/code> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT pm.state_desc, \r\n  pm.permission_name,\r\n  pm.class_desc,\r\n  pm.major_id,\r\n  ep.name\r\nFROM sys.server_permissions pm\r\nJOIN sys.server_principals pr \r\n  ON pm.grantee_principal_id = pr.principal_id\r\nLEFT JOIN sys.endpoints ep\r\n  ON pm.major_id = ep.endpoint_id\r\nWHERE pr.name = 'public';<\/pre>\n<p>The statement joins the <code>sys.server_permissions<\/code>, <code>sys.server_principals<\/code>, and <code>sys.endpoints<\/code> system views to retrieve the relevant information. On my system, I retained the default permissions, which are shown in Figure 6<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"531\" height=\"193\" class=\"wp-image-83688\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-57.png\" \/><\/p>\n<p class=\"caption\">Figure 6. Default permissions assigned to the public server role<\/p>\n<p>You can take a similar approach to retrieve the permissions assigned to the <code>public<\/code> database role:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE ImportSales1;\r\nGO\r\nSELECT pm.state_desc, \r\n  pm.permission_name, \r\n  pm.class_desc,\r\n  pm.major_id,\r\n  OBJECT_NAME(pm.major_id) obj_name\r\nFROM sys.database_permissions pm\r\nJOIN sys.database_principals pr \r\n  ON pm.grantee_principal_id = pr.principal_id\r\nWHERE pr.name = 'public';<\/pre>\n<p>In this case, the <code>public<\/code> database role is specific to the <code>ImportSales1<\/code> database, which was created as part of the examples in the previous article in this series. The database is a simple, contained database that includes only the <code>Sales<\/code> schema and the <code>Customers<\/code> table. You can refer back to that article for details about the database, or you can run this statement against another database.<\/p>\n<p>Figure 7 shows part of the results returned by the <code>SELECT<\/code> statement on my system. As with the <code>public<\/code> server role, I made no modifications to the <code>public<\/code> database role. By default, the role has been granted 176 permissions, the majority of which are the <code>SELECT<\/code> permission granted to system views.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"296\" class=\"wp-image-83689\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-58.png\" \/><\/p>\n<p class=\"caption\">Figure 7. Partial view of the default permissions assigned to the public database role<\/p>\n<p>If you were to run the same query against the <code>master<\/code> database, it would return a much larger result set (2,254 rows on my system). In this case, the results would also include the <code>EXECUTE<\/code> permission, granted on system functions and stored procedures.<\/p>\n<p><span style=\"display: inline !important; float: none; background-color: transparent; color: #333333; cursor: text; font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;\">One way to check effective permissions without writing scripts yourself is to use Redgate\u2019s <\/span><a href=\"https:\/\/www.redgatefoundry.com\/apps\/sql-census\">SQL Census<\/a><span style=\"display: inline !important; float: none; background-color: transparent; color: #333333; cursor: text; font-family: Georgia,'Times New Roman','Bitstream Charter',Times,serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;\">. It creates a report of who has access to what on your SQL Servers and makes best practice recommendations, like disabling sa accounts. It\u2019s still in development but it\u2019s a good starting point to check on your SQL Server permissions and undertake any necessary cleaning tasks.<\/span><\/p>\n<p>The <code>public<\/code> roles are different from other fixed server and database roles because you can grant, deny, and revoke permissions. Even so, you should avoid modifying the <code>public<\/code> roles and instead create one or more additional roles. However, if you work someplace that insists upon using the <code>public<\/code> roles, you should grant permissions to the roles only on securables that you want to make available to all users. In addition, you should avoid denying permissions because they can override the permissions granted to individual users or logins.<\/p>\n<p>You can also revoke permissions on the <code>public<\/code> roles, but be careful when doing so. SQL Server assigns a number of permissions to these roles by default (as you saw in the preceding examples), and many of those permissions are used for routine database operations. Revoking permissions on these roles can impact all logins or users.<\/p>\n<h2>The dbo Database User and Schema<\/h2>\n<p>Every database contains a <code>dbo<\/code> user and <code>dbo<\/code> schema. Although the two are related, they serve very different purposes. (The term <em>dbo<\/em> stands for <em>database owner<\/em>.)<\/p>\n<p>The <code>dbo<\/code> user is added as a member of the <code>db_owner<\/code> fixed database role. By default, the user is granted all permissions in a database and can perform all activities within the scope of that database. You cannot limit the <code>dbo<\/code> user or drop the user from the database.<\/p>\n<p>SQL Server automatically maps the <code>sa<\/code> login, database owner, and members of the <code>sysadmin<\/code> server role to the <code>dbo<\/code> user account in each database. To verify this, connect to a SQL Server instance as one of these users and query the <code>CURRENT_USER<\/code> system function, as in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT CURRENT_USER;<\/pre>\n<p>The <code>SELECT<\/code> statement should return <code>dbo<\/code> as the current user.<\/p>\n<p>The <code>dbo<\/code> user also owns the <code>dbo<\/code> schema, which is the default schema for all newly created databases and users, unless a different schema is specified. As with the <code>dbo<\/code> user, you cannot drop the <code>dbo<\/code> schema.<\/p>\n<p>To verify the login name and default database associated with the <code>dbo<\/code> user, run the following query against one of your databases:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE ImportSales1;\r\nGO\r\nSELECT SUSER_SNAME(sid) login_name, default_schema_name\r\nFROM sys.database_principals\r\nWHERE name = 'dbo';<\/pre>\n<p>In this case, the query is specific to the <code>ImportSales1<\/code> database created for the previous article, but you can use any database. It should return the login for your current connection, as well as the <code>dbo<\/code> schema.<\/p>\n<p>You can also verify that the <code>dbo<\/code> user has been added to the <code>db_owner<\/code> database role:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT member.name\r\nFROM sys.database_role_members rm\r\nJOIN sys.database_principals role  \r\n  ON rm.role_principal_id = role.principal_id  \r\nJOIN sys.database_principals member  \r\n  ON rm.member_principal_id = member.principal_id\r\nWHERE role.name = 'db_owner'; <\/pre>\n<p>The statement should return <code>dbo<\/code>, along with any other role members. When I ran the query on my system, I was still working within the context of the <code>ImportSales1<\/code> database, so the query returned only <code>dbo<\/code>, which is tied to my login.<\/p>\n<p>However, look what happens with the <code>WideWorldImporters<\/code> database, which I attached to my SQL Server instance from the backup file I downloaded from GitHub:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE WideWorldImporters;\r\nGO\r\nSELECT SUSER_SNAME(sid) login_name, default_schema_name\r\nFROM sys.database_principals\r\nWHERE name = 'dbo';<\/pre>\n<p>On my system, the results indicate that <code>sa<\/code> is the associated login, along with <code>dbo<\/code> as the default schema, as shown in Figure 8.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"290\" height=\"118\" class=\"wp-image-83690\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-59.png\" \/><\/p>\n<p class=\"caption\">Figure 8. Login associated with the dbo user<\/p>\n<p>In addition, when I retrieve the members of the <code>db_owner<\/code> database role, the results include both the <code>dbo<\/code> user and my login account:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT member.name\r\nFROM sys.database_role_members rm\r\nJOIN sys.database_principals role  \r\n  ON rm.role_principal_id = role.principal_id  \r\nJOIN sys.database_principals member  \r\n  ON rm.member_principal_id = member.principal_id\r\nWHERE role.name = 'db_owner'; <\/pre>\n<p>Because the <code>dbo<\/code> user is associated with the <code>sa<\/code> account, my login is added as a separate member to the <code>db_owner<\/code> database role. In this way, both my login and the <code>sa<\/code> login have full control over the database (assuming the <code>sa<\/code> login is enabled).<\/p>\n<p>Be aware, however, you might not see the same results on your system as I see mine. It\u2019s possible that only the <code>dbo<\/code> user will be added to the role. It will depend on how you\u2019ve configured your system and added the <code>WideWorldImporters<\/code> database.<\/p>\n<p>On my system, my login is considered the owner for both the <code>ImportSales1<\/code> database and <code>WideWorldImporters<\/code> database, even though my login is associated only with the <code>dbo<\/code> user in the <code>ImportSales1<\/code> database. To confirm the database owners, I ran the following query:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT name, SUSER_SNAME(owner_sid) \r\nFROM sys.databases\r\nWHERE name = 'ImportSales1' \r\n  OR name = 'WideWorldImporters';<\/pre>\n<p>The query returned my login for both databases, as shown in Figure 9.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"359\" height=\"157\" class=\"wp-image-83691\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-60.png\" \/><\/p>\n<p class=\"caption\">Figure 9. Login associated with WideWorldImporters and ImportSales1 databases<\/p>\n<p>You should also be aware of the query engine\u2019s behavior when it comes to the <code>dbo<\/code> schema. When you query a database object without specifying a schema, the query engine first looks in your default schema if it\u2019s other than <code>dbo<\/code>. If the object is not there, the query engine looks in the <code>dbo<\/code> schema. If the object is not in that schema, an error is returned.<\/p>\n<p>When you create a user, you can specify a default schema. If you do not, the <code>dbo<\/code> schema is assumed. Users with <code>dbo<\/code> as their default schema do not inherit the permissions granted to the <code>dbo<\/code> user.<\/p>\n<p>In addition, if you specify a default schema other than <code>dbo<\/code> and the user is a member of the <code>sysadmin<\/code> server role, the specified schema is ignored and the <code>dbo<\/code> schema is used. The default schema for all members of <code>sysadmin<\/code> is <code>dbo<\/code>.<\/p>\n<h2>The guest Database User and Schema<\/h2>\n<p>As with <code>dbo<\/code>, every database contains a <code>guest<\/code> user and a <code>guest<\/code> schema. You can use the <code>guest<\/code> user to grant database access to logins that are not associated with user accounts in that database, a strategy that should not be implemented lightly.<\/p>\n<p>Although the <code>guest<\/code> user cannot be dropped, it is disabled by default and assigned no permissions. Microsoft recommends that you keep it that way. If enabled, logins that should not be able to get into a database will have access. Do not enable the account unless you have a compelling reason to so.<\/p>\n<p>The <code>guest<\/code> user owns the <code>guest<\/code> schema. Like the user, the schema cannot be dropped. However, it contains no objects and has been granted no permissions. In fact, the <code>guest<\/code> schema is seldom used, if at all.<\/p>\n<p>To verify whether the <code>guest<\/code> user is enabled, run the following query:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE ImportSales1;\r\nGO\r\nSELECT u.hasdbaccess, p.default_schema_name\r\nFROM sys.database_principals p\r\nJOIN sys.sysusers u\r\n  ON p.principal_id = u.uid\r\nWHERE p.name = 'guest';<\/pre>\n<p>The query should return a <code>hasdbaccess<\/code> value of <code>0<\/code>, indicating that the user account is disabled, as shown in Figure 10. The query should also return <code>guest<\/code> as the default schema.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"322\" height=\"128\" class=\"wp-image-83692\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-61.png\" \/><\/p>\n<p class=\"caption\">Figure 10. Enabled status of the guest user account<\/p>\n<p>If against all advice you decide to enable the <code>guest<\/code> account, you must grant the <code>CONNECT<\/code> permission to the user, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">GRANT CONNECT TO guest;\r\nGO<\/pre>\n<p>Granting the <code>CONNECT<\/code> permission is all it takes to enable the <code>guest<\/code> user. If you rerun the previous <code>SELECT<\/code> statement, the <code>hasdbaccess<\/code> value should now be <code>1<\/code>.<\/p>\n<p>At any time (the sooner, the better), you can disable the guest account by revoking the <code>CONNECT<\/code> permission:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">REVOKE CONNECT FROM guest;\r\nGO<\/pre>\n<p>To verify whether the <code>guest<\/code> schema contains any objects, run the following query, which will normally return zero rows:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT o.name\r\nFROM sys.all_objects o\r\nJOIN sys.schemas s\r\n  ON o.schema_id = s.schema_id\r\nWHERE s.name = 'guest';<\/pre>\n<p>If you were to enable the <code>guest<\/code> user, you might want to add objects to the <code>guest<\/code> schema specifically for that user to access, but chances are, you won\u2019t be touching either one.<\/p>\n<h2>The sys Database User and Schema<\/h2>\n<p>Every database includes the <code>sys<\/code> user and <code>sys<\/code> schema. The <code>sys<\/code> user owns the <code>sys<\/code> schema. The user serves no other purpose. It\u2019s associated with no logins and is disabled by default. In all likelihood, you\u2019ll never need to interact with this user account.<\/p>\n<p>The <code>sys<\/code> schema is another story. The database engine requires the schema for internal use. You cannot modify or drop the schema. It contains a number of important system objects, such as system tables, catalog views, dynamic management views, and built-in functions. You\u2019ve already seen several of the catalog views in action in the previous examples. The <code>sys<\/code> schema is particularly handy for accessing SQL Server metadata.<\/p>\n<p>You can view the objects in the <code>sys<\/code> schema by running the following query:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">USE ImportSales1;\r\nGO\r\nSELECT o.type_desc, o.name\r\nFROM sys.all_objects o\r\nJOIN sys.schemas s\r\n  ON o.schema_id = s.schema_id\r\nWHERE s.name = 'sys'\r\nORDER BY o.type_desc, o.name;<\/pre>\n<p>In this case, the <code>SELECT<\/code> statement is specific to the <code>ImportSales1<\/code> database. On my system, the statement returns 2,273 rows, broken into 12 object types. To view a list of the object types, run the following query:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT distinct o.type_desc\r\nFROM sys.all_objects o\r\nJOIN sys.schemas s\r\n  ON o.schema_id = s.schema_id\r\nWHERE s.name = 'sys'\r\nORDER BY o.type_desc<\/pre>\n<p>Figure 11 shows the results I received when I ran the <code>SELECT<\/code> statement.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"313\" height=\"310\" class=\"wp-image-83693\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-62.png\" \/><\/p>\n<p class=\"caption\">Figure 11. The types of objects in the sys schema<\/p>\n<p>You can also retrieve a list of objects based on type, as shown in the following example:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT o.name\r\nFROM sys.all_objects o\r\nJOIN sys.schemas s\r\n  ON o.schema_id = s.schema_id\r\nWHERE s.name = 'sys' \r\n  AND o.type_desc = 'VIEW'\r\nORDER BY o.name;<\/pre>\n<p>The statement returns only the objects of type <code>VIEW<\/code>. Figure 12 shows part of the results I received. There were 473 rows in all.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"286\" height=\"297\" class=\"wp-image-83694\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-63.png\" \/><\/p>\n<p class=\"caption\">Figure 12. A partial list of the views in the sys schema<\/p>\n<p>For more information about catalog views and dynamic management views, check out my Simple Talk article <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/learn-sql-server\/sql-server-system-views-the-basics\/\">SQL Server System Views: The Basics<\/a>.<\/p>\n<h2>The INFORMATION_SCHEMA Database User and Schema<\/h2>\n<p>Like <code>sys<\/code>, every database also includes the <code>INFORMATION_SCHEMA<\/code> user and <code>INFORMATION_SCHEMA<\/code> schema. Again, the user is there only to support the schema. You cannot drop the user, but it is disabled by default.<\/p>\n<p>Unlike <code>sys<\/code>, the <code>INFORMATION_SCHEMA<\/code> schema contains only a small number of views and no other object types. You can confirm this by running the following <code>SELECT<\/code> statement:<\/p>\n<pre class=\"lang:tsql theme:ssms2012-simple-talk\">SELECT o.name\r\nFROM sys.all_objects o\r\nJOIN sys.schemas s\r\n  ON o.schema_id = s.schema_id\r\nWHERE s.name = 'INFORMATION_SCHEMA'\r\nORDER BY o.name;<\/pre>\n<p>Figure 13 shows part of the results that I received. In all, there are 21 <code>INFORMATION_SCHEMA<\/code> views for the <code>ImportSales1<\/code> database.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"262\" height=\"293\" class=\"wp-image-83695\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2019\/03\/word-image-64.png\" \/><\/p>\n<p class=\"caption\">Figure 13. A partial list of the views in the INFORMATION_SCHEMA schema<\/p>\n<p>For more information about <code>INFORMATION_SCHEMA<\/code> views, refer to the same Simple Talk article, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/sql\/learn-sql-server\/sql-server-system-views-the-basics\/\">SQL Server System Views: The Basics<\/a>.<\/p>\n<h2>SQL Server\u2019s Odd Collection of Predefined Principals<\/h2>\n<p>When working with SQL Server and its databases, it\u2019s important that you understand how the built-in principals work, especially the ones I\u2019ve covered here. For the most part, SQL Server attempts to configure these principals in a way that best protects your data, such as disabling the <code>sa<\/code> server login or <code>guest<\/code> database user, but this doesn\u2019t prevent you from taking steps that can disrupt operations or, worse still, open your data up to security risks. The better you understand how to work with the server and database principals, the better you can protect your SQL Server instance and its databases.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The sa account is the most powerful account in a SQL Server instance, and most DBAs disable it. There are several other built-in accounts that you may not think about that often.  Robert Sheldon continues his SQL Server security series with an article about built-in accounts.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143514,53],"tags":[68855],"coauthors":[6779],"class_list":["post-83682","post","type-post","status-publish","format-standard","hentry","category-data-privacy-and-protection","category-featured","tag-sql-provision"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/83682","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=83682"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/83682\/revisions"}],"predecessor-version":[{"id":83725,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/83682\/revisions\/83725"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=83682"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=83682"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=83682"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=83682"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}