{"id":91389,"date":"2021-06-21T17:00:10","date_gmt":"2021-06-21T17:00:10","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=91389"},"modified":"2021-06-18T22:16:12","modified_gmt":"2021-06-18T22:16:12","slug":"azure-sql-tightening-the-security-using-integrated-authentication-only","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/blogs\/azure-sql-tightening-the-security-using-integrated-authentication-only\/","title":{"rendered":"Azure SQL: Tightening the Security using Integrated Authentication only"},"content":{"rendered":"<p>This new feature was already being expected for a while. Out of the blue, while I was delivering an online class, there it was, looking to me. Waiting for me to figure out how it works and explain it to my students.<\/p>\n<p>Luckily, the feature is very simple: You click a checkbox and you will entirely block any <strong>SQL User<\/strong> from login to your <strong>Azure SQL Database<\/strong>. Only integrated authentication will be accepted. Only <strong>AD<\/strong> users will be accepted on <strong>Azure SQL DB<\/strong> if you enable this feature.<\/p>\n<h2>Enabling Azure Active Directory Authentication<\/h2>\n<p>First, we need to enable <strong>Azure Active Directory Authentication<\/strong>. This usually is one of the first demonstrations I make in the technical sessions, when I talk about <strong>Azure SQL<\/strong>. To enable <strong>AAD authentication<\/strong>\u00a0we need to set an <strong>AAD<\/strong> admin for our <strong>Azure SQL Server<\/strong>.<\/p>\n<p>The new feature appeared in the same place we need to enable the integrated authentication.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"354\" class=\"wp-image-91390\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-51.png\" \/><\/p>\n<p>Let&#8217;s make some tests to identify all the challenges we may face if we enable this feature.<\/p>\n<ol>\n<li>Let&#8217;s set the administrator of our Azure SQL. I have a group for SQL Admins, that\u2019s the best way to manage the administrators.<\/li>\n<\/ol>\n<p style=\"padding-left: 60px\">A) Click the button <strong>Set Admin<\/strong><\/p>\n<p style=\"padding-left: 60px\">B) Select the administrator. It may be a user or a group.<\/p>\n<p style=\"padding-left: 60px\">C) Click the <strong>Save<\/strong> button<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"702\" height=\"925\" class=\"wp-image-91391\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-52.png\" \/><\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1198\" height=\"359\" class=\"wp-image-91392\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-53.png\" \/><\/p>\n<h2>Creating some SQL Users<\/h2>\n<p>Creating SQL users with <strong>DB_Owner<\/strong> permissions will lead to some challenging scenarios. Let&#8217;s explore these situations to fully understand our challenges.<\/p>\n<p>2) First, execute the script below to create some users<\/p>\n<div style=\"padding-left: 30px\"><span style=\"font-family: Courier New;font-size: 10pt\"> <span style=\"color: blue\">CREATE<\/span>\u00a0<span style=\"color: blue\">USER<\/span>\u00a0<span style=\"color: maroon\">jonh<\/span>\u00a0<span style=\"color: blue\">WITH<\/span>\u00a0<span style=\"color: maroon\">password<\/span><span style=\"color: silver\">=<\/span><span style=\"color: red\">&#8216;6964xpahmW&#8217;<\/span> <\/p>\n<p><span style=\"color: blue\">CREATE<\/span>\u00a0<span style=\"color: blue\">USER<\/span>\u00a0<span style=\"color: maroon\">jane<\/span>\u00a0<span style=\"color: blue\">WITH<\/span>\u00a0<span style=\"color: maroon\">password<\/span><span style=\"color: silver\">=<\/span><span style=\"color: red\">&#8216;6964xpahmW&#8217;<\/span> <\/p>\n<p><span style=\"color: blue\">ALTER<\/span>\u00a0<span style=\"color: maroon\">role<\/span>\u00a0<span style=\"color: maroon\">db_owner<\/span>\u00a0<span style=\"color: blue\">ADD<\/span>\u00a0<span style=\"color: maroon\">member<\/span>\u00a0<span style=\"color: maroon\">jonh<\/span> <\/p>\n<p><span style=\"color: blue\">ALTER<\/span>\u00a0<span style=\"color: maroon\">role<\/span>\u00a0<span style=\"color: maroon\">db_owner<\/span>\u00a0<span style=\"color: blue\">ADD<\/span>\u00a0<span style=\"color: maroon\">member<\/span>\u00a0<span style=\"color: maroon\">jane<\/span>\u00a0 <\/span><\/div>\n<p>&nbsp;<\/p>\n<p>3) You can already login as Jonh. It\u2019s important to mind this is a <a href=\"https:\/\/docs.microsoft.com\/en-us\/sql\/relational-databases\/databases\/contained-databases?view=sql-server-ver15\">contained database<\/a>, so we need to specify the database name when login in.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"633\" height=\"387\" class=\"wp-image-91393\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-54.png\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"633\" height=\"232\" class=\"wp-image-91394\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-55.png\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>4) Execute the script below to create some objects<\/p>\n<div style=\"padding-left: 30px\"><span style=\"font-family: Courier New;font-size: 10pt\"> <span style=\"color: blue\">CREATE<\/span>\u00a0<span style=\"color: blue\">SCHEMA<\/span>\u00a0<span style=\"color: maroon\">jschema<\/span><br \/>\n<span style=\"color: maroon\">go<\/span> <\/p>\n<p><span style=\"color: blue\">CREATE<\/span>\u00a0<span style=\"color: blue\">TABLE<\/span>\u00a0<span style=\"color: maroon\">jschema<\/span><span style=\"color: silver\">.<\/span><span style=\"color: maroon\">jtesttable<\/span> <br \/>\n\u00a0\u00a0<span style=\"color: maroon\">(<\/span> <br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: maroon\">id<\/span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: black;font-style: italic\">INT<\/span>\u00a0<span style=\"color: blue\">IDENTITY<\/span><span style=\"color: maroon\">(<\/span><span style=\"color: black\">1<\/span><span style=\"color: silver\">,<\/span>\u00a0<span style=\"color: black\">1<\/span><span style=\"color: maroon\">)<\/span>\u00a0<span style=\"color: blue\">NOT<\/span>\u00a0<span style=\"color: blue\">NULL<\/span>\u00a0<span style=\"color: blue\">PRIMARY<\/span>\u00a0<span style=\"color: blue\">KEY<\/span><span style=\"color: silver\">,<\/span> <br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: maroon\">[values]<\/span>\u00a0<span style=\"color: black;font-style: italic\">NUMERIC<\/span>\u00a0<span style=\"color: maroon\">(<\/span><span style=\"color: black\">15<\/span><span style=\"color: silver\">,<\/span>\u00a0<span style=\"color: black\">2<\/span><span style=\"color: maroon\">)<\/span> <br \/>\n\u00a0\u00a0<span style=\"color: maroon\">)<\/span><br \/>\n<span style=\"color: maroon\">go<\/span>\u00a0 <\/span><\/div>\n<p>&nbsp;<\/p>\n<p>5) Enable <strong>AD Authentication<\/strong>\u00a0<strong>Only<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"737\" height=\"340\" class=\"wp-image-91395\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-56.png\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>6) Try to login as Jonh. It will fail, because now we can only login using Azure <strong>AD users<\/strong><\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"833\" height=\"396\" class=\"wp-image-91396\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-57.png\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>7) Login using an Azure AD user<\/p>\n<p>8) Execute the following script to identify the owner of the <strong>jsschema<\/strong><\/p>\n<div><span style=\"font-family: Courier New;font-size: 10pt\"> <span style=\"color: blue\">SELECT<\/span>\u00a0<span style=\"color: maroon\">ss<\/span><span style=\"color: silver\">.<\/span><span style=\"color: blue\">NAME<\/span>\u00a0\u00a0<span style=\"color: maroon\">[schema]<\/span><span style=\"color: silver\">,<\/span> <br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: maroon\">sdp<\/span><span style=\"color: silver\">.<\/span><span style=\"color: blue\">NAME<\/span>\u00a0<span style=\"color: maroon\">[user]<\/span> <br \/>\n<span style=\"color: blue\">FROM<\/span>\u00a0\u00a0\u00a0<span style=\"color: maroon\">sys<\/span><span style=\"color: silver\">.<\/span><span style=\"color: maroon\">schemas<\/span>\u00a0<span style=\"color: maroon\">ss<\/span> <br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: blue\">INNER<\/span>\u00a0<span style=\"color: blue\">JOIN<\/span>\u00a0<span style=\"color: maroon\">sys<\/span><span style=\"color: silver\">.<\/span><span style=\"color: maroon\">database_principals<\/span>\u00a0<span style=\"color: maroon\">sdp<\/span> <br \/>\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: blue\">ON<\/span>\u00a0<span style=\"color: maroon\">ss<\/span><span style=\"color: silver\">.<\/span><span style=\"color: maroon\">principal_id<\/span>\u00a0<span style=\"color: silver\">=<\/span>\u00a0<span style=\"color: maroon\">sdp<\/span><span style=\"color: silver\">.<\/span><span style=\"color: maroon\">principal_id<\/span>\u00a0 <\/span><\/div>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"366\" height=\"184\" class=\"wp-image-91397\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-58.png\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>Jonh can\u2019t login anymore, but he is still the owner of a schema inside the database. I recommend this feature to everyone but take care to not break anything.<\/p>\n<p>&nbsp;<\/p>\n<h2>Auditing this configuration<\/h2>\n<p>&nbsp;<\/p>\n<p>This configuration is exactly the kind of configuration we may would like to keep enabled in all our Azure SQLs for security purposes. As a result, we need to identify all servers with this configuration enabled or not, ensuring the safety of our cloud environment.\u00a0<\/p>\n<p>We can audit this configuration on the entire company, or only parts of the company. Of course, in a big corporation, there may be areas, projects, that can use this configuration and others can\u2019t. We can solve this problem by breaking down the services in different subscriptions and breaking down the subscriptions in different management groups. By doing so, we can use Azure Policies on some management groups or subscriptions and not others.<\/p>\n<p>My <a href=\"https:\/\/www.red-gate.com\/simple-talk\/blogs\/how-essential-are-azure-policies\/\">blog about policies<\/a> provide a more complete explanation about this.<\/p>\n<p>&nbsp;<\/p>\n<h2>Creating the Policy<\/h2>\n<p>Our challenge\u00a0is to correctly build the policy to check this new feature.<\/p>\n<p>First, we need to understand how this option is implemented on the <strong>SQL Server<\/strong> object. We can generate the a template of the object by clicking on <strong>Export Template<\/strong> option under <strong>Automation<\/strong> on the left side blade of the <strong>Azure<\/strong>\u00a0<strong>SQL Server<\/strong>.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"203\" height=\"121\" class=\"wp-image-91398\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-59.png\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>On this template we will find the information we need to build a policy. The first important information we discover, is the <strong>Type<\/strong> of the <strong>SQL Server<\/strong>, which is <strong>Microsoft.Sql\/servers<\/strong>. In fact, this would not be so difficult to discover on the documentation as well.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"193\" class=\"wp-image-91399\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/graphical-user-interface-text-application-descr.png\" alt=\"Graphical user interface, text, application\n\nDescription automatically generated\" \/><\/p>\n<p>Another important information is how this configuration is deployed. You may notice this configuration has its own object of <strong>Type<\/strong> <strong>Microsoft.Sql\/azureADonlyAuthentications<\/strong> . This object has a collection of properties and one of them is <strong>azureADonlyAuthentication<\/strong> . This is the property our policy will need to check.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1428\" height=\"559\" class=\"wp-image-91400\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/word-image-60.png\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>This will be our policy:<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0&#8220;policyRule&#8221;:\u00a0{<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;if&#8221;:\u00a0{<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;field&#8221;:\u00a0&#8220;type&#8221;,<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;equals&#8221;:\u00a0&#8220;Microsoft.Sql\/servers&#8221;<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;then&#8221;:\u00a0{<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;effect&#8221;:\u00a0&#8220;auditIfNotExists&#8221;,<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;details&#8221;:\u00a0{<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;type&#8221;:\u00a0&#8220;Microsoft.Sql\/servers\/azureADOnlyAuthentications&#8221;,<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;existenceCondition&#8221;:\u00a0{<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;field&#8221;:\u00a0&#8220;Microsoft.Sql\/servers\/azureADOnlyAuthentications\/azureADOnlyAuthentication&#8221;,<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&#8220;equals&#8221;:\u00a0true<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/p>\n<p>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}<\/p>\n<p>Some details about how the policy is built:<\/p>\n<ul>\n<li>The main policy criteria is the type, <strong>Microsoft.Sql\/servers<\/strong> .<\/li>\n<li>The effect is <strong>auditIfNotExists<\/strong>. It will check the existence of the type <strong>Microsoft.Sql\/servers\/azureADOnlyAuthentications<\/strong>.<\/li>\n<li>The existence condition will check if the property is enabled, ensuring only <strong>Azure AD Authentication <\/strong>is accepted by <strong>SQL Server<\/strong>. If the property is not enabled, the <strong>auditIfNotExists<\/strong> will not find the object. The policy will report the <strong>SQL Server<\/strong> as non-compliant.<\/li>\n<\/ul>\n<h2>Policy Result<\/h2>\n<p>After creating and assigning a policy, I enabled this option in a single <strong>SQL Server<\/strong> in my subscription and checked the policy compliance result.<\/p>\n<p>What\u2019s important is how we are able to easily identify all <strong>SQL Servers<\/strong> non-compliant with the policy across the entire company. We could be talking about a worldwide company.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1392\" height=\"389\" class=\"wp-image-91401\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2021\/06\/graphical-user-interface-text-application-email-1.png\" alt=\"Graphical user interface, text, application, email\n\nDescription automatically generated\" \/><\/p>\n<h2>Conclusion<\/h2>\n<p>This is an important new feature on <strong>Azure SQL<\/strong> and together with the policies, we can tighten the security on the entire company environment.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This new feature was already being expected for a while. Out of the blue, while I was delivering an online class, there it was, looking to me. Waiting for me to figure out how it works and explain it to my students. Luckily, the feature is very simple: You click a checkbox and you will&#8230;&hellip;<\/p>\n","protected":false},"author":50808,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"tags":[143575,143573,136322,143574],"coauthors":[6810],"class_list":["post-91389","post","type-post","status-publish","format-standard","hentry","category-blogs","tag-azure-ad","tag-azure-policies","tag-azure-sql","tag-governance"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/91389","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\/50808"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=91389"}],"version-history":[{"count":1,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/91389\/revisions"}],"predecessor-version":[{"id":91402,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/91389\/revisions\/91402"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=91389"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=91389"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=91389"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=91389"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}