Introduction

Most Storage Accounts I encounter are accessible from all networks and a lot of times with Anonymous Access enabled allowing attackers to access your Storage Accounts with minimal effort. It goes without saying that it’s very important to secure them correctly.

In this blog I will walk you through the most important security settings, share an easy script to detect insecure Storage Accounts and I will share a “Secure-by-default” ARM template for Storage deployment.

Storage Authentication Methods

Blob Public Access

Anonymous Access makes it possible to connect to a Storage Account without any form of authentication. All you need is the URL of the Storage Account and you’re in. You should disable this unless you absolutely need this.

Access Keys

You can authenticate to your Storage Account by using access keys which is a combination of a connection string and a secret. Access keys will give you full access to the Storage Account. You can implement these in your code or applications. I don’t recommend sharing these with users since these keys should be considered highly sensitive.

Shared Access Signature

A shared access signature works almost the same way as an access key but it will give you a lot of freedom when it comes to scope of access, permissions and expiration

  • Specify the services that can be accessed
  • Specify permissions on the services that can be accessed
  • Set up expiration dates for temporarily access
  • Whitelist source IP’s
  • HTTP/HTTPS or HTTPS only

After selecting your preferred settings you can generate and share your connection string and token.


Role-based Access Control (RBAC)

You can use Azure Active Directory to authenticate against your Storage Account. This works for User Accounts, Security Groups, Application Service Principals or Managed Identities and could eliminate your need for key-based authentication. You can assign roles in the Access Control (IAM) blade on the Storage Account or a specific sub-resource. The following roles are available. You can enable this feature in the Configuration blade

Networking

Firewalls and virtual networks

By default a storage account is accessible from all networks. To change this, navigate to your Storage Account and click on Networking.
To limit network access select Selected Networks. Here you can add your Azure Virtual Networks or any IP ranges from internal or external sources to allow access to your Storage Account and you can create a simple exception for trusted Azure Services like Azure Backup or Azure Monitor. Full list of trusted services here.

Secure Transfer and TLS

  • Secure Transfer Required – If enabled only HTTPS traffic will be allowed. HTTP traffic will be rejected
  • Minimum TLS Version – Transport Layer Security encrypts data in transit. TLS 1.2 is the newest version. Any older TLS versions will be rejected in this example.

Private Endpoint

A private endpoint allows you to integrate your Storage Account into your own Azure Virtual Network. When you’re using a private endpoint your Storage Account will be assigned it’s own private IP within your preferred subnet. Data will travel directly to the Storage Account instead of over the internet. If you have an Express Route or S2S set up to your Azure Network you can also connect to your Storage using the private endpoint.

Configuring a private endpoint is quite easy

  • Go to your storage account and click on the Networking blade
  • Select the Private Endpoint tab and click on + Private Endpoint
  • Select your Resource Group, choose a name and select the region you want your endpoint to be hosted in.
  • Select your target sub-resource. If you want to target multiple Storage Services you can create multiple private endpoints.
  • Select your VNET and Subnet where you want to deploy your Private Endpoint
  • Deploy 🙂

You can access your Private Endpoint details in the Private Link Center

Discovering insecure Storage Accounts

Azure Security Center and Advisor will show you insecure Storage Accounts based on Microsoft best practices. You can also use this one-liner in Azure Powershell. It will show you a list of Storage Accounts where the minimum TLS version is lower than 1.2, Public Blob Access is set to Enabled, Key Access is enabled or where HTTP traffic is allowed.

Get-AzStorageAccount | Where-Object {$_.MinimumTlsVersion -ne "TLS1_2" -or $_.AllowBlobPublicAccess -eq "True" -or $_.AllowSharedKeyAccess -eq "True" -or $_.EnableHttpsTrafficOnly -ne "True"} | Select StorageAccountName, MinimumTlsVersion, AllowBlobPublicAccess, AllowSharedKeyAccess, EnableHttpsTrafficOnly

Secure-by-default template

You can use this ARM template to deploy your Storage Accounts secure-by-default with a Private Endpoint

  • Blob Public Access: Disabled
  • Minimum TLS Version: 1.2
  • HTTPS Only
  • Azure AD Authentication: Enabled
  • Access Keys: Disabled
  • Trusted Azure Services: Enabled
  • Allow Access from Selected Networks Only
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.4.1124.51302",
      "templateHash": "16766334222894722560"
    }
  },
  "parameters": {
    "Storagename": {
      "type": "string"
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    },
    "accesstier": {
      "type": "string"
    },
    "subnetid": {
      "type": "string"
    },
    "SKU": {
      "type": "string"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2021-06-01",
      "name": "[parameters('Storagename')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "[parameters('SKU')]"
      },
      "kind": "StorageV2",
      "properties": {
        "allowBlobPublicAccess": false,
        "minimumTlsVersion": "TLS1_2",
        "supportsHttpsTrafficOnly": true,
        "defaultToOAuthAuthentication": true,
        "allowSharedKeyAccess": false,
        "accessTier": "[parameters('accesstier')]",
        "networkAcls": {
          "defaultAction": "Deny",
          "bypass": "AzureServices"
        }
      }
    },
    {
      "type": "Microsoft.Network/privateEndpoints",
      "apiVersion": "2021-05-01",
      "name": "[format('{0}-plink', parameters('Storagename'))]",
      "location": "[parameters('location')]",
      "properties": {
        "subnet": {
          "id": "[parameters('subnetid')]"
        },
        "privateLinkServiceConnections": [
          {
            "name": "[format('{0}-plink', parameters('Storagename'))]",
            "properties": {
              "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', parameters('Storagename'))]",
              "groupIds": [
                "Blob"
              ]
            }
          }
        ]
      },
      "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts', parameters('Storagename'))]"
      ]
    }
  ]
}

Related Posts

One thought on “Securing Storage Accounts

  1. The problem I have when I set the storage account to Selected Networks is I can’t seem to get my appservice to talk to it anymore unless I pay extra for a VNET. The firewall IPs don’t seem to help unless I’m doing something wrong..

Leave a Reply

Your email address will not be published. Required fields are marked *