Autenticación del acceso a recursos de Event Hubs mediante firmas de acceso compartido (SAS)

La firma de acceso compartido (SAS) le ofrece un control pormenorizado sobre el tipo de acceso que se concede a los clientes. Estos son algunos de los controles que puede establecer en una SAS:

  • El intervalo durante el que la SAS es válida, incluida la hora de inicio y la hora de expiración.
  • Los permisos concedidos por la SAS. Por ejemplo, una SAS para un espacio de nombres de Event Hubs podría conceder el permiso de escucha, pero no el permiso de envío.
  • Solo los clientes que tengan credenciales válidas pueden enviar datos a un centro de eventos.
  • Un cliente no puede suplantar a otro cliente.
  • Se puede bloquear el envío de datos por parte de un cliente no autorizado a un centro de eventos.

En este artículo se explica cómo autenticar el acceso a los recursos de Event Hubs mediante SAS. Para más información sobre la autorización del acceso a recursos de Event Hubs mediante SAS, consulte este artículo.

Nota:

Microsoft recomienda utilizar las credenciales de Microsoft Entra siempre que sea posible como mejor práctica de seguridad, en lugar de utilizar las firmas de acceso compartido, que pueden verse comprometidas con mayor facilidad. Si bien puede seguir utilizando firmas de acceso compartido (SAS) para conceder acceso específico a sus recursos Centros de eventos, Microsoft Entra ID ofrece funcionalidades similares sin necesidad de administrar tokens SAS o preocuparse por la revocación de un SAS comprometido.

Para obtener más información sobre la integración de Microsoft Entra en Azure Event Hubs, consulte Autorizar el acceso a Centros de eventos mediante Microsoft Entra ID.

Configuración de la autenticación de SAS

Puede configurar una regla de autorización de acceso compartido en un espacio de nombres de Event Hubs o una entidad (instancia de centro de eventos o tema de Kafka en un centro de eventos). Actualmente no se admite la configuración de una regla de autorización de acceso compartido en un grupo de consumidores, pero puede usar reglas configuradas en un espacio de nombres o una entidad para proteger el acceso a dicho grupo.

En la imagen siguiente se muestra cómo se aplican las reglas de autorización en las entidades de ejemplo.

Configuración de la regla de autorización

En este ejemplo, el espacio de nombres de Event Hubs (ExampleNamespace) tiene dos entidades: eh1 y topic1 de Kafka. Las reglas de autorización se definen en el nivel de entidad y también en el nivel de espacio de nombres.

Las reglas de autorización manageRuleNS, sendRuleNS y listenRuleNS se aplican a eh1 y t1. Las reglas de autorización listenRule-eh y sendRule-eh se aplican únicamente a eh1, y la regla de autorización sendRuleT se aplica solo a topic1.

Al usar una regla de autorización sendRuleNS, las aplicaciones cliente pueden realizar envíos a eh1 y topic1. Cuando se usa la regla de autorización sendRuleT, solo se aplica el acceso granular a topic1 y, por lo tanto, las aplicaciones cliente que usan esta regla para el acceso ahora no pueden realizar envíos a eh1, sino solo a topic1.

Generación de un token de Firmas de acceso compartido

Cualquier cliente que tenga acceso al nombre de una regla de autorización y una de sus claves de firma puede generar un token de SAS. El token se genera diseñando una cadena con el siguiente formato:

  • se: instante de expiración del token. Entero que refleja los segundos transcurridos desde la época 00:00:00 UTC el 1 de enero de 1970 (época de UNIX) cuando expira el token.
  • skn: nombre de la regla de autorización, que es el nombre de la clave SAS.
  • sr: URI del recurso al que se accede.
  • sig: firma.

La cadena de firma es el hash SHA-256 calculado sobre el URI del recurso (ámbito tal y como se describe en la sección anterior) y la representación de cadenas del instante de expiración del token, separados por CRLF. El cálculo del hash es similar al siguiente pseudocódigo y devuelve un valor de hash de 256 bits/32 bytes.

SHA-256('https://<yournamespace>.servicebus.windows.net/'+'\n'+ 1438205742)

El token contiene los valores sin el hash para que el destinatario pueda volver a calcular el hash con los mismos parámetros y verificar que el emisor posee una clave de firma válida.

El URI de recurso es el URI completo del recurso de Service Bus al que se solicita el acceso. Por ejemplo, http://<namespace>.servicebus.windows.net/<entityPath> o sb://<namespace>.servicebus.windows.net/<entityPath>; es decir, http://contoso.servicebus.windows.net/eh1.

El URI debe estar codificado por porcentaje.

La regla de autorización de acceso compartido usada para firmar debe configurarse en la entidad especificada por este URI, o uno de sus primarios jerárquicos. Por ejemplo, http://contoso.servicebus.windows.net/eh1 o http://contoso.servicebus.windows.net en el ejemplo anterior.

Un token SAS es válido en todos los recursos con el prefijo <resourceURI> usado en la cadena de firma.

Nota:

El token de acceso para Event Hubs se genera mediante la directiva de acceso compartido. Para más información, consulte Directiva de autorización de acceso compartido.

Generación de una firma (token) a partir de una directiva

En la sección siguiente se muestra cómo generar un token de SAS mediante directivas de firma de acceso compartido.

NodeJS

function createSharedAccessToken(uri, saName, saKey) { 
  if (!uri || !saName || !saKey) { 
          throw "Missing required parameter"; 
      } 
  var encoded = encodeURIComponent(uri); 
  var now = new Date(); 
  var week = 60*60*24*7;
  var ttl = Math.round(now.getTime() / 1000) + week;
  var signature = encoded + '\n' + ttl; 
  var hash = crypto.createHmac('sha256', saKey).update(signature, 'utf8').digest('base64'); 
  return 'SharedAccessSignature sr=' + encoded + '&sig=' +  
      encodeURIComponent(hash) + '&se=' + ttl + '&skn=' + saName; 
}

Para usar un nombre de directiva y un valor de clave para conectarse a un centro de eventos, use el constructor EventHubProducerClient que toma el parámetro AzureNamedKeyCredential.

const producer = new EventHubProducerClient("NAMESPACE NAME.servicebus.windows.net", eventHubName, new AzureNamedKeyCredential("POLICYNAME", "KEYVALUE"));

Debe agregar una referencia a AzureNamedKeyCredential.

const { AzureNamedKeyCredential } = require("@azure/core-auth");

Para usar un token de SAS que generó con el código, use el constructor EventHubProducerClient que toma el parámetro AzureSASCredential.

var token = createSharedAccessToken("https://NAMESPACENAME.servicebus.windows.net", "POLICYNAME", "KEYVALUE");
const producer = new EventHubProducerClient("NAMESPACENAME.servicebus.windows.net", eventHubName, new AzureSASCredential(token));

Debe agregar una referencia a AzureSASCredential.

const { AzureSASCredential } = require("@azure/core-auth");

Java

private static String GetSASToken(String resourceUri, String keyName, String key)
  {
      long epoch = System.currentTimeMillis()/1000L;
      int week = 60*60*24*7;
      String expiry = Long.toString(epoch + week);

      String sasToken = null;
      try {
          String stringToSign = URLEncoder.encode(resourceUri, "UTF-8") + "\n" + expiry;
          String signature = getHMAC256(key, stringToSign);
          sasToken = "SharedAccessSignature sr=" + URLEncoder.encode(resourceUri, "UTF-8") +"&sig=" +
                  URLEncoder.encode(signature, "UTF-8") + "&se=" + expiry + "&skn=" + keyName;
      } catch (UnsupportedEncodingException e) {

          e.printStackTrace();
      }

      return sasToken;
  }


public static String getHMAC256(String key, String input) {
    Mac sha256_HMAC = null;
    String hash = null;
    try {
        sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        Encoder encoder = Base64.getEncoder();

        hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8"))));

    } catch (InvalidKeyException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
   } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    return hash;
}

PHP

function generateSasToken($uri, $sasKeyName, $sasKeyValue) 
{ 
    $targetUri = strtolower(rawurlencode(strtolower($uri))); 
    $expires = time(); 	
    $expiresInMins = 60; 
    $week = 60*60*24*7;
    $expires = $expires + $week; 
    $toSign = $targetUri . "\n" . $expires; 
    $signature = rawurlencode(base64_encode(hash_hmac('sha256', 			
     $toSign, $sasKeyValue, TRUE))); 
    
    $token = "SharedAccessSignature sr=" . $targetUri . "&sig=" . $signature . "&se=" . $expires . 		"&skn=" . $sasKeyName; 
    return $token; 
}

C#

private static string createToken(string resourceUri, string keyName, string key)
{
    TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
    var week = 60 * 60 * 24 * 7;
    var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);
    string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
    using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
    {
        var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
        var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
        return sasToken;
    }
}

PowerShell

[Reflection.Assembly]::LoadWithPartialName("System.Web")| out-null
$URI="myNamespace.servicebus.windows.net/myEventHub/"
$Access_Policy_Name="RootManageSharedAccessKey"
$Access_Policy_Key="myPrimaryKey"
#Token expires now+300
$Expires=([DateTimeOffset]::Now.ToUnixTimeSeconds())+300
$SignatureString=[System.Web.HttpUtility]::UrlEncode($URI)+ "`n" + [string]$Expires
$HMAC = New-Object System.Security.Cryptography.HMACSHA256
$HMAC.key = [Text.Encoding]::ASCII.GetBytes($Access_Policy_Key)
$Signature = $HMAC.ComputeHash([Text.Encoding]::ASCII.GetBytes($SignatureString))
$Signature = [Convert]::ToBase64String($Signature)
$SASToken = "SharedAccessSignature sr=" + [System.Web.HttpUtility]::UrlEncode($URI) + "&sig=" + [System.Web.HttpUtility]::UrlEncode($Signature) + "&se=" + $Expires + "&skn=" + $Access_Policy_Name
$SASToken

BASH

get_sas_token() {
    local EVENTHUB_URI='EVENTHUBURI'
    local SHARED_ACCESS_KEY_NAME='SHAREDACCESSKEYNAME'
    local SHARED_ACCESS_KEY='SHAREDACCESSKEYVALUE'
    local EXPIRY=${EXPIRY:=$((60 * 60 * 24))} # Default token expiry is 1 day

    local ENCODED_URI=$(echo -n $EVENTHUB_URI | jq -s -R -r @uri)
    local TTL=$(($(date +%s) + $EXPIRY))
    local UTF8_SIGNATURE=$(printf "%s\n%s" $ENCODED_URI $TTL | iconv -t utf8)

    local HASH=$(echo -n "$UTF8_SIGNATURE" | openssl sha256 -hmac $SHARED_ACCESS_KEY -binary | base64)
    local ENCODED_HASH=$(echo -n $HASH | jq -s -R -r @uri)

    echo -n "SharedAccessSignature sr=$ENCODED_URI&sig=$ENCODED_HASH&se=$TTL&skn=$SHARED_ACCESS_KEY_NAME"
}

Autenticación de los publicadores de Event Hubs con SAS

Un publicador de eventos define un punto de conexión virtual para un centro de eventos. El publicador solo puede usarse para enviar mensajes a un centro de eventos, no para recibirlos.

Normalmente, un centro de eventos emplea a un publicador por cliente. Todos los mensajes que se envíen a cualquiera de los publicadores de un centro de eventos se ponen en cola dentro de ese centro de eventos. Los publicadores permiten control de acceso pormenorizado.

A cada cliente de Event Hubs se le asigna un token único, que se carga en el cliente. Los tokens se generan de forma que cada token único concede acceso a un publicador único diferente. Un cliente que posea un token solo puede enviar a un publicador y a ningún otro. Si varios clientes comparten el mismo token, cada uno de estos clientes comparte el publicador.

Todos los tokens se asignan con claves SAS. Normalmente, todos los tokens se firman con la misma clave. Los clientes no conocen la clave, lo que evita que los clientes produzcan tokens. Los clientes operan en los mismos tokens hasta que expiran.

Por ejemplo, para definir reglas de autorización cuyo ámbito sea solo enviar o publicar en Event Hubs, debe definir una regla de autorización de envío. Se puede hacer en un nivel de espacio de nombres o proporcionar un ámbito más específico a una entidad determinada (una instancia de Event Hubs o un tema). A un cliente o una aplicación que estén limitados con este acceso específico se les conoce como publicador de Event Hubs. Para hacerlo, siga estos pasos:

  1. Cree una clave SAS en la entidad que quiera publicar para asignarle el ámbito de envío. Para más información, consulte Directivas de autorización de acceso compartido.

  2. Genere un token de SAS con una hora de expiración para un publicador específico mediante la clave generada en paso 1. Para ver un código de ejemplo, consulte Generación de una firma (token) a partir de una directiva.

  3. Proporcione el token al cliente del publicador, que solo puede enviar a la entidad y al publicador a los que el token concede acceso.

    Una vez que el token expira, el cliente pierde su acceso para enviar o publicar en la entidad.

Nota:

Aunque no se recomienda, es posible equipar a los dispositivos con tokens que concedan acceso directo a un centro de eventos o espacio de nombres. Cualquier dispositivo que contenga un token de ese tipo puede enviar mensajes directamente a ese centro de eventos. Además, el dispositivo no puede estar en la lista de bloqueados que impide el envío a ese centro de eventos.

Siempre se recomienda proporcionar ámbitos específicos y pormenorizados.

Importante

Cuando se han creado los tokens, cada cliente se aprovisiona con su propio token único.

Cuando el cliente envía datos a un centro de eventos, la solicitud se etiqueta con el token. Para evitar que un atacante use la técnica de eavesdropping y robe el token, la comunicación entre el cliente y el centro de eventos debe realizarse a través de un canal cifrado.

Si un atacante roba un token, el atacante puede suplantar el cliente al que se ha robado el token. Al incorporar un publicador a la lista de no permitidos, ese cliente se representa como inutilizable hasta que recibe un token nuevo que usa un publicador diferente.

Autenticación de consumidores de Event Hubs con SAS

Para autenticar las aplicaciones back-end que consumen los datos generados por los productores de Event Hubs, la autenticación de tokens de Event Hubs requiere que sus clientes tengan asignados los derechos de administración o los privilegios de escucha a su espacio de nombres de Event Hubs o instancia o tema del centro de eventos. Los datos se consumen de Event Hubs mediante grupos de consumidores. Aunque la directiva SAS proporciona un ámbito granular, este ámbito solo se define en el nivel de entidad, no en el nivel de consumidor. Significa que los privilegios definidos en el nivel de espacio de nombres o en el nivel de instancia o tema de centro de eventos se aplicarán a los grupos de consumidores de esa entidad.

Deshabilitación de la autenticación de clave local o SAS

Para ciertos requisitos de seguridad de la organización, quiere desactivar completamente la autenticación de clave local/SAS y confiar en la autenticación basada en Microsoft Entra ID, que es la forma recomendada para conectarse con Azure Event Hubs. Puede deshabilitar la autenticación de clave local o SAS en el nivel de espacio de nombres de Event Hubs a través de Azure Portal o la plantilla de Azure Resource Manager.

Deshabilitación de la autenticación de clave local o SAS a través del portal

Puede deshabilitar la autenticación de clave local o SAS para un espacio de nombres de Event Hubs determinado a través de Azure Portal.

Tal como se muestra en la imagen siguiente, en la sección de información general del espacio de nombres, seleccione la autenticación local.

Información general del espacio de nombres para deshabilitar la autenticación local

Luego, seleccione la opción Deshabilitado y, después, Aceptar, tal y como se muestra en la siguiente imagen. Deshabilitación de la autenticación local

Deshabilitación de la autenticación de clave local o SAS a través de una plantilla

Puede deshabilitar la autenticación local para un espacio de nombres de Event Hubs si establece la propiedad disableLocalAuth en true, tal como se muestra en la plantilla de Azure Resource Manager (plantilla de ARM) siguiente.

"resources":[
      {
         "apiVersion":"[variables('ehVersion')]",
         "name":"[parameters('eventHubNamespaceName')]",
         "type":"Microsoft.EventHub/Namespaces",
         "location":"[variables('location')]",
         "sku":{
            "name":"Standard",
            "tier":"Standard"
         },
         "resources": [
    {
      "apiVersion": "2017-04-01",
      "name": "[parameters('eventHubNamespaceName')]",
      "type": "Microsoft.EventHub/Namespaces",
      "location": "[resourceGroup().location]",
      "sku": {
        "name": "Standard"
      },
      "properties": {
        "isAutoInflateEnabled": "true",
        "maximumThroughputUnits": "7", 
        "disableLocalAuth": false
      },
      "resources": [
        {
          "apiVersion": "2017-04-01",
          "name": "[parameters('eventHubName')]",
          "type": "EventHubs",
          "dependsOn": [
            "[concat('Microsoft.EventHub/namespaces/', parameters('eventHubNamespaceName'))]"
          ],
          "properties": {
            "messageRetentionInDays": "[parameters('messageRetentionInDays')]",
            "partitionCount": "[parameters('partitionCount')]"
          }

        }
      ]
    }
  ]

Ejemplos

  • Consulte el ejemplo 6 de .NET en esta ubicación de GitHub para saber cómo publicar eventos en un centro de eventos mediante credenciales de acceso compartido o la identidad de credencial de Azure predeterminada.
  • Consulte el ejemplo 5 de .NET en esta ubicación de GitHub para saber cómo consumir o procesar eventos mediante credenciales de acceso compartido o la identidad de credencial de Azure predeterminada.

Pasos siguientes

Vea los artículos siguientes:

Consulte los artículos relacionados siguientes: