Autenticar el acceso a los recursos de los Centros de Eventos utilizando firmas de acceso compartido (SAS)

Utilizando una firma de acceso compartido (SAS), puedes controlar el tipo de acceso que concedes a los clientes. Puedes configurar los siguientes controles en un 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, un SAS para un espacio de nombres de Event Hubs podría conceder permiso para escuchar eventos, pero no el permiso para enviar eventos.

La autenticación SAS ofrece estos beneficios:

  • Solo los clientes que tengan credenciales válidas pueden enviar datos a un centro de eventos.
  • Un cliente no puede suplantar a otro cliente.
  • Puedes bloquear que un cliente malicioso envíe datos a un hub de eventos.

Este artículo trata sobre cómo autenticar el acceso a los recursos de Event Hubs mediante SAS. Para aprender sobre cómo autorizar el acceso a recursos de Event Hubs usando SAS, consulte Autorizar acceso a recursos de Event Hubs usando firmas de acceso compartidas.

Note

Como mejor práctica de seguridad, utiliza credenciales de Microsoft Entra siempre que sea posible en lugar de usar las firmas de acceso compartidas, que pueden ser más fácilmente comprometidas. 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.

Configurar la autenticación SAS

Puede configurar una regla de autorización de acceso compartido en un espacio de nombres de Event Hubs o una entidad (centro de eventos o tema de Kafka). No se permite configurar una regla SAS en un grupo de consumidores, pero puedes usar reglas configuradas en un espacio de nombres o entidad para asegurar el acceso a un grupo de consumidores. En la imagen siguiente se muestra cómo se aplican las reglas de autorización en las entidades de ejemplo.

Diagrama que muestra centros de eventos con reglas de escucha, envío y administració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 topic1. 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. Genera el token creando una cadena en el siguiente formato:

  • se: instante de expiración del token. Entero que refleja el número de segundos transcurridos desde las 00:00:00 UTC del 1 de enero de 1970 (época UNIX) en que 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 de HMAC-SHA256 calculado sobre el URI de recurso (alcance descrito en la sección anterior) y la representación de la cadena del instante de caducidad del token, separados por un salto de línea (LF). Clave el hash con una de las claves de firma de la regla de autorización. El cálculo de hash devuelve un valor hash de 256 bits/32 bytes.

HMAC-SHA256(signingKey, 'https://<yournamespace>.servicebus.windows.net/' + '\n' + 1438205742)

El token contiene los valores no hashados para que el destinatario pueda recalcular el hash con los mismos parámetros y verificar que el emisor tiene 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>, como .http://contoso.servicebus.windows.net/eh1

Codifica el URI por porcentaje.

Configura la regla SAS utilizada para firmar en la entidad especificada por este URI o por uno de sus padres 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.

Note

Generas un token de acceso para los Event Hubs usando una política de acceso compartido. Para más información, consulte Directiva de autorización de acceso compartido.

Generar una firma (token) a partir de una póliza

La siguiente sección muestra cómo generar un token SAS utilizando políticas de firma de acceso compartida.

Node.js

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"));

Agregue una referencia a AzureNamedKeyCredential.

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

Para usar un token SAS que generas usando el código, usa el EventHubProducerClient constructor que toma el AzureSASCredential parámetro.

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

Agregue 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"
}

Autentica a los editores 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.

Se asigna un token único a cada cliente de Event Hubs, 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 limitadas únicamente al envío o la publicación en Event Hubs, debe definir una regla de autorización para envío. Puedes crear la regla a nivel de espacio de nombres o dar un alcance más detallado a una entidad concreta, como una instancia de hub de eventos o un tema. Un cliente o una aplicación que tenga este acceso granular se denomina editor de Event Hubs. Para configurar esta autenticación, sigue estos pasos:

  1. Crea una clave SAS en la entidad a la que quieres publicar y asigna el alcance de envío a ella. Para más información, consulte Directivas de autorización de acceso compartido.

  2. Genera un token SAS con un tiempo de caducidad para un editor específico usando la clave que creaste en el paso 1. Para el código de ejemplo, véase Generar una firma (token) a partir de una política.

  3. Proporciona el token al cliente editor, que solo puede enviarlo a la entidad y al editor al que el token concede acceso.

    Cuando el token expira, el cliente pierde el acceso para enviar o publicar a la entidad.

Note

Aunque no lo recomendamos, puedes equipar dispositivos con tokens que concedan acceso a un hub de eventos o a un espacio de nombres. Cualquier dispositivo que contenga un token de ese tipo puede enviar mensajes directamente a ese centro de eventos. No puedes bloquear la lista de envío del dispositivo a ese hub de eventos.

Se recomienda proporcionar ámbitos específicos y granulares.

Important

Cuando creas los tokens, cada cliente recibe su propio token único.

Cuando el cliente envía datos a un hub de eventos, etiqueta su solicitud 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, puede hacerse pasar por el cliente cuyo token fue robado. Si no permites un editor, ese cliente se vuelve inutilizable hasta que reciba un nuevo token que use otro editor.

Autentica a los 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 el centro de eventos o el nivel de tema se aplican a los grupos de consumidores de esa entidad.

Desactivar la autenticación por 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.

Desactiva la autenticación por clave local o SAS usando el 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.

  1. Vaya a su espacio de nombres de Event Hubs en Azure Portal.

  2. En la página Información general, seleccione Habilitado para Autenticación local, como se muestra en la imagen siguiente.

    Captura de pantalla que muestra la autenticación local seleccionada.

  3. En el menú emergente Autenticación local, seleccione Deshabilitado y seleccione Aceptar.

    Captura de pantalla que muestra el menú emergente Autenticación local con la opción Deshabilitado seleccionada.

Deshabilitar la autenticación con clave local o SAS mediante una plantilla

Es posible deshabilitar la autenticación local para un espacio de nombres de Event Hubs al establecer la propiedad disableLocalAuth en true, tal y como se muestra en la plantilla de Azure Resource Manager (plantilla de ARM) siguiente.

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

Samples

  • 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.

Ahora que entiendes la autenticación SAS, explora estos artículos relacionados: