Partilhar via


Como proteger o ponto de extremidade do webhook

Proteger a entrega de mensagens de ponta a ponta é crucial para garantir a confidencialidade, integridade e confiabilidade de informações confidenciais transmitidas entre sistemas. Sua capacidade e vontade de confiar nas informações recebidas de um sistema remoto depende do remetente fornecer sua identidade. A Automação de Chamadas tem duas maneiras de comunicar eventos que podem ser protegidos; o evento IncomingCall compartilhado enviado pela Grade de Eventos do Azure e todos os outros eventos de chamada intermediária enviados pela plataforma de Automação de Chamadas via webhook.

Evento de chamada recebida

Os Serviços de Comunicação do Azure dependem de subscrições da Grelha de Eventos do Azure para fornecer o evento IncomingCall. Você pode consultar a equipe da Grade de Eventos do Azure para obter a documentação sobre como proteger uma assinatura de webhook.

Eventos webhook de automação de chamadas

Os eventos de Automação de Chamadas são enviados para o URI de retorno de chamada webhook especificado quando você atende uma chamada ou faz uma nova chamada de saída. Seu URI de retorno de chamada deve ser um ponto de extremidade público com um certificado HTTPS válido, nome DNS e endereço IP com as portas de firewall corretas abertas para permitir que a Automação de Chamadas o alcance. Este servidor web público anónimo pode criar um risco de segurança se não tomar as medidas necessárias para o proteger de acesso não autorizado.

Uma maneira comum de melhorar essa segurança é implementando um mecanismo API KEY. Seu servidor web pode gerar a chave em tempo de execução e fornecê-la no URI de retorno de chamada como um parâmetro de consulta quando você atende ou cria uma chamada. Seu servidor web pode verificar a chave no retorno de chamada webhook da Automação de chamadas antes de permitir o acesso. Alguns clientes exigem mais medidas de segurança. Nesses casos, um dispositivo de rede de perímetro pode verificar o webhook de entrada, separado do servidor web ou do próprio aplicativo. O mecanismo de chave API por si só pode não ser suficiente.

Melhorando a segurança do retorno de chamada do webhook da automação de chamadas

Cada retorno de chamada webhook mid-call enviado pela Automação de Chamadas usa um JSON Web Token (JWT) assinado no cabeçalho Authentication da solicitação HTTPS de entrada. Você pode usar técnicas de validação JWT padrão do Open ID Connect (OIDC) para garantir a integridade do token da seguinte maneira. O tempo de vida do JWT é de cinco (5) minutos e um novo token é criado para cada evento enviado para o URI de retorno de chamada.

  1. Obtenha o URL de configuração do Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Instale o pacote NuGet Microsoft.AspNetCore.Authentication.JwtBearer.
  3. Configure seu aplicativo para validar o JWT usando o pacote NuGet e a configuração do recurso dos Serviços de Comunicação do Azure. Você precisa dos audience valores como ele está presente na carga útil JWT.
  4. Valide o emissor, o público e o token JWT.
    • O público é sua ID de recurso dos Serviços de Comunicação do Azure que você usou para configurar seu cliente de Automação de Chamadas. Consulte aqui sobre como obtê-lo.
    • O ponto de extremidade JSON Web Key set (JWKS) na configuração OpenId contém as chaves usadas para validar o token JWT. Quando a assinatura é válida e o token não expirou (dentro de 5 minutos após a geração), o cliente pode usar o token para autorização.

Este código de exemplo demonstra como usar Microsoft.IdentityModel.Protocols.OpenIdConnect para validar a carga útil do webhook

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Add Azure Communication Services CallAutomation OpenID configuration
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
            builder.Configuration["OpenIdConfigUrl"],
            new OpenIdConnectConfigurationRetriever());
var configuration = configurationManager.GetConfigurationAsync().Result;

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Configuration = configuration;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidAudience = builder.Configuration["AllowedAudience"]
        };
    });

builder.Services.AddAuthorization();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapPost("/api/callback", (CloudEvent[] events) =>
{
    // Your implemenation on the callback event
    return Results.Ok();
})
.RequireAuthorization()
.WithOpenApi();

app.UseAuthentication();
app.UseAuthorization();

app.Run();

Melhorando a segurança do retorno de chamada do webhook da automação de chamadas

Cada retorno de chamada webhook mid-call enviado pela Automação de Chamadas usa um JSON Web Token (JWT) assinado no cabeçalho Authentication da solicitação HTTPS de entrada. Você pode usar técnicas de validação JWT padrão do Open ID Connect (OIDC) para garantir a integridade do token da seguinte maneira. O tempo de vida do JWT é de cinco (5) minutos e um novo token é criado para cada evento enviado para o URI de retorno de chamada.

  1. Obtenha o URL de configuração do Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. O exemplo a seguir usa a estrutura Spring, criada usando spring initializr com Maven como ferramenta de construção de projeto.
  3. Adicione as seguintes dependências ao seu pom.xml:
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-oauth2-jose</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-oauth2-resource-server</artifactId>
  </dependency>
  1. Configure seu aplicativo para validar o JWT e a configuração do recurso dos Serviços de Comunicação do Azure. Você precisa dos audience valores como ele está presente na carga útil JWT.
  2. Valide o emissor, o público e o token JWT.
    • O público é sua ID de recurso dos Serviços de Comunicação do Azure que você usou para configurar seu cliente de Automação de Chamadas. Consulte aqui sobre como obtê-lo.
    • O ponto de extremidade JSON Web Key set (JWKS) na configuração OpenId contém as chaves usadas para validar o token JWT. Quando a assinatura é válida e o token não expirou (dentro de 5 minutos após a geração), o cliente pode usar o token para autorização.

Este código de exemplo demonstra como configurar o cliente OIDC para validar a carga útil do webhook usando JWT

package callautomation.example.security;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.*;

@EnableWebSecurity
public class TokenValidationConfiguration {
    @Value("ACS resource ID")
    private String audience;

    @Value("https://acscallautomation.communication.azure.com")
    private String issuer;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/api/callbacks").permitAll()
                .anyRequest()
                .and()
                .oauth2ResourceServer()
                .jwt()
                .decoder(jwtDecoder());

        return http.build();
    }

    class AudienceValidator implements OAuth2TokenValidator<Jwt> {
        private String audience;

        OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);

        public AudienceValidator(String audience) {
            this.audience = audience;
        }

        @Override
        public OAuth2TokenValidatorResult validate(Jwt token) {
            if (token.getAudience().contains(audience)) {
                return OAuth2TokenValidatorResult.success();
            } else {
                return OAuth2TokenValidatorResult.failure(error);
            }
        }
    }

    JwtDecoder jwtDecoder() {
        OAuth2TokenValidator<Jwt> withAudience = new AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
        OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>(withAudience, withIssuer);

        NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuer);
        jwtDecoder.setJwtValidator(validator);

        return jwtDecoder;
    }
}

Melhorando a segurança do retorno de chamada do webhook da automação de chamadas

Cada retorno de chamada webhook mid-call enviado pela Automação de Chamadas usa um JSON Web Token (JWT) assinado no cabeçalho Authentication da solicitação HTTPS de entrada. Você pode usar técnicas de validação JWT padrão do Open ID Connect (OIDC) para garantir a integridade do token da seguinte maneira. O tempo de vida do JWT é de cinco (5) minutos e um novo token é criado para cada evento enviado para o URI de retorno de chamada.

  1. Obtenha o URL de configuração do Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Instale os seguintes pacotes:
npm install express jwks-rsa jsonwebtoken
  1. Configure seu aplicativo para validar o JWT e a configuração do recurso dos Serviços de Comunicação do Azure. Você precisa dos audience valores como ele está presente na carga útil JWT.
  2. Valide o emissor, o público e o token JWT.
    • O público é sua ID de recurso dos Serviços de Comunicação do Azure que você usou para configurar seu cliente de Automação de Chamadas. Consulte aqui sobre como obtê-lo.
    • O ponto de extremidade JSON Web Key set (JWKS) na configuração OpenId contém as chaves usadas para validar o token JWT. Quando a assinatura é válida e o token não expirou (dentro de 5 minutos após a geração), o cliente pode usar o token para autorização.

Este código de exemplo demonstra como configurar o cliente OIDC para validar a carga útil do webhook usando JWT

import express from "express";
import { JwksClient } from "jwks-rsa";
import { verify } from "jsonwebtoken";

const app = express();
const port = 3000;
const audience = "ACS resource ID";
const issuer = "https://acscallautomation.communication.azure.com";

app.use(express.json());

app.post("/api/callback", (req, res) => {
    const token = req?.headers?.authorization?.split(" ")[1] || "";

    if (!token) {
        res.sendStatus(401);

        return;
    }

    try {
        verify(
            token,
            (header, callback) => {
                const client = new JwksClient({
                    jwksUri: "https://acscallautomation.communication.azure.com/calling/keys",
                });

                client.getSigningKey(header.kid, (err, key) => {
                    const signingKey = key?.publicKey || key?.rsaPublicKey;

                    callback(err, signingKey);
                });
            },
            {
                audience,
                issuer,
                algorithms: ["RS256"],
            });
        // Your implementation on the callback event
        res.sendStatus(200);
    } catch (error) {
        res.sendStatus(401);
    }
});

app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

Melhorando a segurança do retorno de chamada do webhook da automação de chamadas

Cada retorno de chamada webhook mid-call enviado pela Automação de Chamadas usa um JSON Web Token (JWT) assinado no cabeçalho Authentication da solicitação HTTPS de entrada. Você pode usar técnicas de validação JWT padrão do Open ID Connect (OIDC) para garantir a integridade do token da seguinte maneira. O tempo de vida do JWT é de cinco (5) minutos e um novo token é criado para cada evento enviado para o URI de retorno de chamada.

  1. Obtenha o URL de configuração do Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Instale os seguintes pacotes:
pip install flask pyjwt
  1. Configure seu aplicativo para validar o JWT e a configuração do recurso dos Serviços de Comunicação do Azure. Você precisa dos audience valores como ele está presente na carga útil JWT.
  2. Valide o emissor, o público e o token JWT.
    • O público é sua ID de recurso dos Serviços de Comunicação do Azure que você usou para configurar seu cliente de Automação de Chamadas. Consulte aqui sobre como obtê-lo.
    • O ponto de extremidade JSON Web Key set (JWKS) na configuração OpenId contém as chaves usadas para validar o token JWT. Quando a assinatura é válida e o token não expirou (dentro de 5 minutos após a geração), o cliente pode usar o token para autorização.

Este código de exemplo demonstra como configurar o cliente OIDC para validar a carga útil do webhook usando JWT

from flask import Flask, jsonify, abort, request
import jwt

app = Flask(__name__)


@app.route("/api/callback", methods=["POST"])
def handle_callback_event():
    token = request.headers.get("authorization").split()[1]

    if not token:
        abort(401)

    try:
        jwks_client = jwt.PyJWKClient(
            "https://acscallautomation.communication.azure.com/calling/keys"
        )
        jwt.decode(
            token,
            jwks_client.get_signing_key_from_jwt(token).key,
            algorithms=["RS256"],
            issuer="https://acscallautomation.communication.azure.com",
            audience="ACS resource ID",
        )
        # Your implementation on the callback event
        return jsonify(success=True)
    except jwt.InvalidTokenError:
        print("Token is invalid")
        abort(401)
    except Exception as e:
        print("uncaught exception" + e)
        abort(500)


if __name__ == "__main__":
    app.run()

Próximos passos