Så här skyddar du webhook-slutpunkten

Att skydda leveransen av meddelanden från slutpunkt till slutpunkt är avgörande för att säkerställa konfidentialitet, integritet och pålitlighet för känslig information som överförs mellan system. Din förmåga och vilja att lita på information som tas emot från ett fjärrsystem är beroende av att avsändaren tillhandahåller sin identitet. Samtalsautomation har två sätt att kommunicera händelser som kan skyddas. den delade IncomingCall-händelsen som skickas av Azure Event Grid och alla andra mellansamtalshändelser som skickas av Call Automation-plattformen via webhook.

Inkommande samtalshändelse

Azure Communication Services förlitar sig på Azure Event Grid-prenumerationer för att leverera IncomingCall-händelsen. Du kan läsa mer i Azure Event Grid-teamet om deras dokumentation om hur du skyddar en webhook-prenumeration.

Anropa Automation webhook-händelser

Samtalsautomatiseringshändelser skickas till den webhook-återanrops-URI som anges när du svarar på ett samtal eller ringer ett nytt utgående samtal. Motringnings-URI:n måste vara en offentlig slutpunkt med ett giltigt HTTPS-certifikat, DNS-namn och IP-adress med rätt brandväggsportar öppna för att call automation ska kunna nå den. Den här anonyma offentliga webbservern kan skapa en säkerhetsrisk om du inte vidtar nödvändiga åtgärder för att skydda den från obehörig åtkomst.

Ett vanligt sätt att förbättra den här säkerheten är genom att implementera en API KEY-mekanism. Webbservern kan generera nyckeln vid körning och ange den i URI:n för återanrop som en frågeparameter när du svarar eller skapar ett anrop. Webbservern kan verifiera nyckeln i webhookens återanrop från Call Automation innan åtkomst tillåts. Vissa kunder behöver fler säkerhetsåtgärder. I dessa fall kan en perimeternätverksenhet verifiera den inkommande webhooken, separat från själva webbservern eller programmet. Enbart API-nyckelmekanismen kanske inte räcker.

Förbättra call automation webhook callback security

Varje webhook-återanrop mellan anropet som skickas av Call Automation använder en signerad JSON-webbtoken (JWT) i autentiseringshuvudet för den inkommande HTTPS-begäran. Du kan använda JWT-valideringstekniker (OIDC) (Open ID Anslut) för att säkerställa tokens integritet på följande sätt. Livslängden för JWT är fem (5) minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta konfigurations-URL:en för öppet ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Installera NuGet-paketet Microsoft.AspNetCore.Authentication.JwtBearer.
  3. Konfigurera ditt program för att verifiera JWT med hjälp av NuGet-paketet och konfigurationen av din Azure Communication Services-resurs. Du behöver audience värdena eftersom de finns i JWT-nyttolasten.
  4. Verifiera utfärdaren, målgruppen och JWT-token.
    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Läs mer här om hur du hämtar den.
    • JSON-slutpunkten (JWKS) i OpenId-konfigurationen innehåller de nycklar som används för att verifiera JWT-token. När signaturen är giltig och token inte har upphört att gälla (inom 5 minuter efter generering) kan klienten använda token för auktorisering.

Den här exempelkoden visar hur du använder Microsoft.IdentityModel.Protocols.OpenIdConnect för att verifiera webhook-nyttolasten

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

Förbättra call automation webhook callback security

Varje webhook-återanrop mellan anropet som skickas av Call Automation använder en signerad JSON-webbtoken (JWT) i autentiseringshuvudet för den inkommande HTTPS-begäran. Du kan använda JWT-valideringstekniker (OIDC) (Open ID Anslut) för att säkerställa tokens integritet på följande sätt. Livslängden för JWT är fem (5) minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta konfigurations-URL:en för öppet ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. I följande exempel används Spring Framework, som skapats med spring initializr med Maven som projektverktyg.
  3. Lägg till följande beroenden i :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. Konfigurera ditt program för att verifiera JWT och konfigurationen av din Azure Communication Services-resurs. Du behöver audience värdena eftersom de finns i JWT-nyttolasten.
  2. Verifiera utfärdaren, målgruppen och JWT-token.
    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Läs mer här om hur du hämtar den.
    • JSON-slutpunkten (JWKS) i OpenId-konfigurationen innehåller de nycklar som används för att verifiera JWT-token. När signaturen är giltig och token inte har upphört att gälla (inom 5 minuter efter generering) kan klienten använda token för auktorisering.

Den här exempelkoden visar hur du konfigurerar OIDC-klienten för att verifiera webhook-nyttolasten med 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;
    }
}

Förbättra call automation webhook callback security

Varje webhook-återanrop mellan anropet som skickas av Call Automation använder en signerad JSON-webbtoken (JWT) i autentiseringshuvudet för den inkommande HTTPS-begäran. Du kan använda JWT-valideringstekniker (OIDC) (Open ID Anslut) för att säkerställa tokens integritet på följande sätt. Livslängden för JWT är fem (5) minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta konfigurations-URL:en för öppet ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Installera följande paket:
npm install express jwks-rsa jsonwebtoken
  1. Konfigurera ditt program för att verifiera JWT och konfigurationen av din Azure Communication Services-resurs. Du behöver audience värdena eftersom de finns i JWT-nyttolasten.
  2. Verifiera utfärdaren, målgruppen och JWT-token.
    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Läs mer här om hur du hämtar den.
    • JSON-slutpunkten (JWKS) i OpenId-konfigurationen innehåller de nycklar som används för att verifiera JWT-token. När signaturen är giltig och token inte har upphört att gälla (inom 5 minuter efter generering) kan klienten använda token för auktorisering.

Den här exempelkoden visar hur du konfigurerar OIDC-klienten för att verifiera webhook-nyttolasten med 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}`);
});

Förbättra call automation webhook callback security

Varje webhook-återanrop mellan anropet som skickas av Call Automation använder en signerad JSON-webbtoken (JWT) i autentiseringshuvudet för den inkommande HTTPS-begäran. Du kan använda JWT-valideringstekniker (OIDC) (Open ID Anslut) för att säkerställa tokens integritet på följande sätt. Livslängden för JWT är fem (5) minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta konfigurations-URL:en för öppet ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Installera följande paket:
pip install flask pyjwt
  1. Konfigurera ditt program för att verifiera JWT och konfigurationen av din Azure Communication Services-resurs. Du behöver audience värdena eftersom de finns i JWT-nyttolasten.
  2. Verifiera utfärdaren, målgruppen och JWT-token.
    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Läs mer här om hur du hämtar den.
    • JSON-slutpunkten (JWKS) i OpenId-konfigurationen innehåller de nycklar som används för att verifiera JWT-token. När signaturen är giltig och token inte har upphört att gälla (inom 5 minuter efter generering) kan klienten använda token för auktorisering.

Den här exempelkoden visar hur du konfigurerar OIDC-klienten för att verifiera webhook-nyttolasten med 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()

Nästa steg