Dela via


Skydda webhook-slutpunkter och WebSocket-anslutningar

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 förlitar sig på 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. Mer information finns i Leverera händelser till Microsoft Entra-skyddade slutpunkter.

Anropa händelser från Automation-webhooks

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, domännamnssystemnamn 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.

Förbättra säkerheten för återkoppling i call automation webhook

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 Standard OpenID Connect (OIDC) JWT-valideringstekniker för att säkerställa tokens integritet. Livslängden för JWT är fem minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta OpenID konfigurations-URL: <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 värdet audience såsom det visas i JWT-nyttolasten.
  4. Verifiera utfärdaren, målgruppen och JWT:
    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Information om hur du hämtar den finns i Hämta ditt Azure-resurs-ID.
    • Slutpunkten för JSON-webbnyckeluppsättningen i OpenId-konfigurationen innehåller de nycklar som används för att verifiera JWT. När signaturen är giltig och token inte har upphört att gälla (inom fem minuter efter genereringen) 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 implementation on the callback event
    return Results.Ok();
})
.RequireAuthorization()
.WithOpenApi();

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

app.Run();

Förbättra säkerheten för återkoppling i call automation webhook

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 Standard OpenID Connect (OIDC) JWT-valideringstekniker för att säkerställa tokens integritet. Livslängden för JWT är fem minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta OpenID konfigurations-URL: <https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration>

  2. I följande exempel används Spring-ramverket, som skapas med hjälp av 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>
    
  4. Konfigurera ditt program för att verifiera JWT och konfigurationen av din Azure Communication Services-resurs. Du behöver värdet audience såsom det visas i JWT-nyttolasten.

  5. Verifiera utfärdaren, målgruppen och JWT:

    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Information om hur du hämtar den finns i Hämta ditt Azure-resurs-ID.
    • Slutpunkten för JSON-webbnyckeluppsättningen i OpenID-konfigurationen innehåller de nycklar som används för att verifiera JWT. När signaturen är giltig och token inte har upphört att gälla (inom fem minuter efter genereringen) kan klienten använda token för auktorisering.

Den här exempelkoden visar hur du konfigurerar OIDC-klienten för att verifiera en webhook-nyttolast med hjälp av 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 säkerheten för återkoppling i call automation webhook

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 Standard OpenID Connect (OIDC) JWT-valideringstekniker för att säkerställa tokens integritet. Livslängden för JWT är fem minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta OpenID konfigurations-URL: <https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration>

  2. Installera följande paket:

    npm install express jwks-rsa jsonwebtoken
    
  3. Konfigurera ditt program för att verifiera JWT och konfigurationen av din Azure Communication Services-resurs. Du behöver värdet audience såsom det visas i JWT-nyttolasten.

  4. Verifiera utfärdaren, målgruppen och JWT:

    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Information om hur du hämtar den finns i Hämta ditt Azure-resurs-ID.
    • Slutpunkten för JSON-webbnyckeluppsättningen i OpenID-konfigurationen innehåller de nycklar som används för att verifiera JWT. När signaturen är giltig och token inte har upphört att gälla (inom fem minuter efter genereringen) kan klienten använda token för auktorisering.

Den här exempelkoden visar hur du konfigurerar OIDC-klienten för att verifiera en webhook-nyttolast med hjälp av 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 säkerheten för återkoppling i call automation webhook

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 Standard OpenID Connect (OIDC) JWT-valideringstekniker för att säkerställa tokens integritet. Livslängden för JWT är fem minuter och en ny token skapas för varje händelse som skickas till återanrops-URI:n.

  1. Hämta OpenID konfigurations-URL: <https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration>

  2. Installera följande paket:

    pip install flask pyjwt
    
  3. Konfigurera ditt program för att verifiera JWT och konfigurationen av din Azure Communication Services-resurs. Du behöver värdet audience såsom det visas i JWT-nyttolasten.

  4. Verifiera utfärdaren, målgruppen och JWT:

    • Målgruppen är ditt Resurs-ID för Azure Communication Services som du använde för att konfigurera din Call Automation-klient. Information om hur du hämtar den finns i Hämta ditt Azure-resurs-ID.
    • Slutpunkten för JSON-webbnyckeluppsättningen i OpenId-konfigurationen innehåller de nycklar som används för att verifiera JWT. När signaturen är giltig och token inte har upphört att gälla (inom fem minuter efter genereringen) kan klienten använda token för auktorisering.

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

Viktigt!

Vår tjänst använder JSON-standardwebbtoken i autentiseringshuvudet och stöder endast OpenID Connect-JWT-validering (OIDC).

Autentisering av frågeparametertoken

Autentisering av frågeparametertoken är en enkel metod för att skydda webhook-återanrop genom att lägga till en i förväg delad hemlig token till webhook-slutpunkts-URL:en som en frågesträngsparameter. Den här token fungerar som en enkel autentiseringsnyckel så att systemet kan verifiera att webhook-återanropshändelser kommer från Call Automation Service.

https://api.example.com/webhook?token=8f2d9c63a7b14d32b53c9e12a1f47fcb

När webhook-motringningshändelser tas emot innehåller Call Automation Service token precis som du konfigurerade (se exemplet ovan). När du tar emot begäran jämför systemet token i frågeparametern med ett lagrat, betrott värde. Begäranden utan token, eller med ett felaktigt värde, bör avvisas.

Anrop Automation-händelser för WebSocket

Autentiseringstoken i en WebSocket-rubrik

Varje WebSocket-anslutningsbegäran som görs av Samtalsautomation innehåller nu en signerad JWT i autentiseringshuvudet. Den här token verifieras med hjälp av standardmetoder för JWT-verifiering av OIDC:

  • JWT har en livslängd på 24 timmar.
  • En ny token genereras för varje anslutningsbegäran till WebSocket-servern.

WebSocket-kodexempel

Den här exempelkoden visar hur du autentiserar WebSocket-anslutningsbegäranden med hjälp av JWT-token.

// 1. Load OpenID Connect metadata
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
    builder.Configuration["OpenIdConfigUrl"],
    new OpenIdConnectConfigurationRetriever());

var openIdConfig = await configurationManager.GetConfigurationAsync();
// 2. Register JWT authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Configuration = openIdConfig;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidAudience = builder.Configuration["AllowedAudience"]
        };
    });

builder.Services.AddAuthorization();

var app = builder.Build();

// 3. Use authentication & authorization middleware
app.UseAuthentication();
app.UseAuthorization();

app.UseWebSockets();

// 4. WebSocket token validation manually in middleware
app.Use(async (context, next) =>
{
    if (context.Request.Path != "/ws")
    {
        await next(context);
        return;
    }

    if (!context.WebSockets.IsWebSocketRequest)
    {
        context.Response.StatusCode = StatusCodes.Status400BadRequest;
        await context.Response.WriteAsync("WebSocket connection expected.");
        return;
    }

    var result = await context.AuthenticateAsync();
    if (!result.Succeeded)
    {
        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        await context.Response.WriteAsync("Unauthorized WebSocket connection.");
        return;
    }

    context.User = result.Principal;

    // Optional: Log headers
    var correlationId = context.Request.Headers["x-ms-call-correlation-id"].FirstOrDefault();
    var callConnectionId = context.Request.Headers["x-ms-call-connection-id"].FirstOrDefault();

    Console.WriteLine($"Authenticated WebSocket - Correlation ID: {correlationId ?? "not provided"}");
    Console.WriteLine($"Authenticated WebSocket - CallConnection ID: {callConnectionId ?? "not provided"}");

    // Now you can safely accept the WebSocket and process the connection
    // var webSocket = await context.WebSockets.AcceptWebSocketAsync();
    // var mediaService = new AcsMediaStreamingHandler(webSocket, builder.Configuration);
    // await mediaService.ProcessWebSocketAsync();
});

WebSocket-kodexempel

Det här exemplet visar hur du konfigurerar en OIDC-klient för att verifiera WebSocket-anslutningsbegäranden med hjälp av en JWT.

const audience = "ACS resource ID";
const issuer = "https://acscallautomation.communication.azure.com";

const jwksClient = new JwksClient({
  jwksUri: "https://acscallautomation.communication.azure.com/calling/keys",
});

wss.on("connection", async (ws, req) => {
  try {
    const authHeader = req.headers?.authorization || "";
    const token = authHeader.split(" ")[1];

    if (!token) {
      ws.close(1008, "Unauthorized");
      return;
    }

    verify(
      token,
      async (header, callback) => {
        try {
          const key = await jwksClient.getSigningKey(header.kid);
          const signingKey = key.getPublicKey();
          callback(null, signingKey);
        } catch (err) {
          callback(err);
        }
      },
      {
        audience,
        issuer,
        algorithms: ["RS256"],
      },
      (err, decoded) => {
        if (err) {
          console.error("WebSocket authentication failed:", err);
          ws.close(1008, "Unauthorized");
          return;
        }

        console.log(
          "Authenticated WebSocket connection with decoded JWT payload:",
          decoded
        );

        ws.on("message", async (message) => {
          // Process message
        });

        ws.on("close", () => {
          console.log("WebSocket connection closed");
        });
      }
    );
  } catch (err) {
    console.error("Unexpected error during WebSocket setup:", err);
    ws.close(1011, "Internal Server Error"); // 1011 = internal error
  }
});

WebSocket-kodexempel

Det här exemplet visar hur du konfigurerar en OIDC-kompatibel klient för att verifiera WebSocket-anslutningsbegäranden med hjälp av en JWT.

Se till att installera det nödvändiga paketet: pip install cryptography

JWKS_URL = "https://acscallautomation.communication.azure.com/calling/keys"
ISSUER = "https://acscallautomation.communication.azure.com"
AUDIENCE = "ACS resource ID”

@app.websocket('/ws')
async def ws():
    try:
        auth_header = websocket.headers.get("Authorization")
        if not auth_header or not auth_header.startswith("Bearer "):
            await websocket.close(1008)  # Policy violation
            return

        token = auth_header.split()[1]

        jwks_client = PyJWKClient(JWKS_URL)
        signing_key = jwks_client.get_signing_key_from_jwt(token)

        decoded = jwt.decode(
            token,
            signing_key.key,
            algorithms=["RS256"],
            issuer=ISSUER,
            audience=AUDIENCE,
        )

        app.logger.info(f"Authenticated WebSocket connection with decoded JWT payload: {decoded}")
        await websocket.send("Connection authenticated.")

        while True:
            data = await websocket.receive()
            # Process incoming data

    except InvalidTokenError as e:
        app.logger.warning(f"Invalid token: {e}")
        await websocket.close(1008)
    except Exception as e:
        app.logger.error(f"Uncaught exception: {e}")
        await websocket.close(1011)  # Internal error

IP range

Ett annat sätt att skydda dina WebSocket-anslutningar är att endast tillåta Microsoft-anslutningar från vissa IP-intervall.

Category IP-intervall eller FQDN Ports
Anropa automatiseringsmedier 52.112.0.0/14, 52.122.0.0/15, 2603:1063::/38 UDP: 3478, 3479, 3480, 3481
Url:er för återanrop till Automation *.lync.com, *.teams.cloud.microsoft, *.teams.microsoft.com, teams.cloud.microsoft, teams.microsoft.com 52.112.0.0/14, 52.122.0.0/15, 2603:1027::/48, 2603:1037::/48, 2603:1047::/48, 2603:1057::/48, 2603:1063::/38, 2620:1ec:6::/48, 2620:1ec:40::/42 TCP: 443, 80 UDP: 443