Zabezpečení koncového bodu webhooku

Zabezpečení doručování zpráv od konce do konce je zásadní pro zajištění důvěrnosti, integrity a důvěryhodnosti citlivých informací přenášených mezi systémy. Vaše schopnost a ochota důvěřovat informacím přijatým ze vzdáleného systému závisí na tom, že odesílatel poskytne svoji identitu. Automatizace volání má dva způsoby komunikace událostí, které je možné zabezpečit; sdílenou událost IncomingCall odesílanou službou Azure Event Grid a všechny ostatní události volání uprostřed volání odesílané platformou Automatizace volání prostřednictvím webhooku.

Událost příchozího volání

Služba Azure Communication Services spoléhá na odběry služby Azure Event Grid k doručení události IncomingCall. Informace o zabezpečení předplatného webhooku najdete v týmu Azure Event Gridu.

Události webhooku služby Automation

Události automatizace volání se odesílají do identifikátoru URI zpětného volání webhooku zadaného při přijetí hovoru nebo na místo nového odchozího volání. Identifikátor URI zpětného volání musí být veřejný koncový bod s platným certifikátem HTTPS, názvem DNS a IP adresou se správnými porty brány firewall, aby se k němu mohla spojit služba Automation volání. Tento anonymní veřejný webový server může vytvořit bezpečnostní riziko, pokud neuděláte potřebné kroky k zabezpečení před neoprávněným přístupem.

Běžným způsobem, jak toto zabezpečení zlepšit, je implementace mechanismu KLÍČE rozhraní API. Váš webový server může klíč vygenerovat za běhu a poskytnout ho v identifikátoru URI zpětného volání jako parametr dotazu, když přijmete nebo vytvoříte volání. Váš webový server může před povolením přístupu ověřit klíč v zpětném volání webhooku z automatizace volání. Někteří zákazníci vyžadují větší bezpečnostní opatření. V těchto případech může hraniční síťové zařízení ověřit příchozí webhook odděleně od samotného webového serveru nebo aplikace. Samotný mechanismus klíče rozhraní API nemusí být dostatečný.

Vylepšení zabezpečení zpětného volání webhooku pro automatizaci volání

Každý zpětný volání webhooku uprostřed volání odeslaný službou Call Automation používá podepsaný webový token JSON (JWT) v hlavičce ověřování příchozího požadavku HTTPS. K zajištění integrity tokenu můžete použít standardní techniky ověřování JWT (Open ID Připojení) JWT. Životnost JWT je pět (5) minut a pro každou událost odesílanou do identifikátoru URI zpětného volání se vytvoří nový token.

  1. Získejte adresu URL konfigurace Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Nainstalujte balíček NuGet Microsoft.AspNetCore.Authentication.JwtBearer.
  3. Nakonfigurujte aplikaci tak, aby ověřila JWT pomocí balíčku NuGet a konfigurace prostředku Azure Communication Services. Potřebujete audience hodnoty, které jsou přítomné v datové části JWT.
  4. Ověřte vystavitele, cílovou skupinu a token JWT.
    • Cílová skupina je ID prostředku služby Azure Communication Services, které jste použili k nastavení klienta automatizace volání. Informace o tom, jak ho získat, najdete tady .
    • Koncový bod SADY webových klíčů JSON (JWKS) v konfiguraci OpenId obsahuje klíče použité k ověření tokenu JWT. Pokud je podpis platný a platnost tokenu nevypršela (do 5 minut od generování), klient může token použít k autorizaci.

Tento ukázkový kód ukazuje použití Microsoft.IdentityModel.Protocols.OpenIdConnect k ověření datové části webhooku.

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

Vylepšení zabezpečení zpětného volání webhooku pro automatizaci volání

Každý zpětný volání webhooku uprostřed volání odeslaný službou Call Automation používá podepsaný webový token JSON (JWT) v hlavičce ověřování příchozího požadavku HTTPS. K zajištění integrity tokenu můžete použít standardní techniky ověřování JWT (Open ID Připojení) JWT. Životnost JWT je pět (5) minut a pro každou událost odesílanou do identifikátoru URI zpětného volání se vytvoří nový token.

  1. Získejte adresu URL konfigurace Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Následující ukázka používá architekturu Spring vytvořenou pomocí spring initializru s Mavenem jako nástroj pro sestavení projektu.
  3. Do svého počítače pom.xmlpřidejte následující závislosti:
  <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. Nakonfigurujte aplikaci tak, aby ověřila JWT a konfiguraci prostředku Azure Communication Services. Potřebujete audience hodnoty, které jsou přítomné v datové části JWT.
  2. Ověřte vystavitele, cílovou skupinu a token JWT.
    • Cílová skupina je ID prostředku služby Azure Communication Services, které jste použili k nastavení klienta automatizace volání. Informace o tom, jak ho získat, najdete tady .
    • Koncový bod SADY webových klíčů JSON (JWKS) v konfiguraci OpenId obsahuje klíče použité k ověření tokenu JWT. Pokud je podpis platný a platnost tokenu nevypršela (do 5 minut od generování), klient může token použít k autorizaci.

Tento ukázkový kód ukazuje, jak nakonfigurovat klienta OIDC tak, aby ověřil datovou část webhooku pomocí 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;
    }
}

Vylepšení zabezpečení zpětného volání webhooku pro automatizaci volání

Každý zpětný volání webhooku uprostřed volání odeslaný službou Call Automation používá podepsaný webový token JSON (JWT) v hlavičce ověřování příchozího požadavku HTTPS. K zajištění integrity tokenu můžete použít standardní techniky ověřování JWT (Open ID Připojení) JWT. Životnost JWT je pět (5) minut a pro každou událost odesílanou do identifikátoru URI zpětného volání se vytvoří nový token.

  1. Získejte adresu URL konfigurace Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Nainstalujte následující balíčky:
npm install express jwks-rsa jsonwebtoken
  1. Nakonfigurujte aplikaci tak, aby ověřila JWT a konfiguraci prostředku Azure Communication Services. Potřebujete audience hodnoty, které jsou přítomné v datové části JWT.
  2. Ověřte vystavitele, cílovou skupinu a token JWT.
    • Cílová skupina je ID prostředku služby Azure Communication Services, které jste použili k nastavení klienta automatizace volání. Informace o tom, jak ho získat, najdete tady .
    • Koncový bod SADY webových klíčů JSON (JWKS) v konfiguraci OpenId obsahuje klíče použité k ověření tokenu JWT. Pokud je podpis platný a platnost tokenu nevypršela (do 5 minut od generování), klient může token použít k autorizaci.

Tento ukázkový kód ukazuje, jak nakonfigurovat klienta OIDC tak, aby ověřil datovou část webhooku pomocí 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}`);
});

Vylepšení zabezpečení zpětného volání webhooku pro automatizaci volání

Každý zpětný volání webhooku uprostřed volání odeslaný službou Call Automation používá podepsaný webový token JSON (JWT) v hlavičce ověřování příchozího požadavku HTTPS. K zajištění integrity tokenu můžete použít standardní techniky ověřování JWT (Open ID Připojení) JWT. Životnost JWT je pět (5) minut a pro každou událost odesílanou do identifikátoru URI zpětného volání se vytvoří nový token.

  1. Získejte adresu URL konfigurace Open ID: https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration
  2. Nainstalujte následující balíčky:
pip install flask pyjwt
  1. Nakonfigurujte aplikaci tak, aby ověřila JWT a konfiguraci prostředku Azure Communication Services. Potřebujete audience hodnoty, které jsou přítomné v datové části JWT.
  2. Ověřte vystavitele, cílovou skupinu a token JWT.
    • Cílová skupina je ID prostředku služby Azure Communication Services, které jste použili k nastavení klienta automatizace volání. Informace o tom, jak ho získat, najdete tady .
    • Koncový bod SADY webových klíčů JSON (JWKS) v konfiguraci OpenId obsahuje klíče použité k ověření tokenu JWT. Pokud je podpis platný a platnost tokenu nevypršela (do 5 minut od generování), klient může token použít k autorizaci.

Tento ukázkový kód ukazuje, jak nakonfigurovat klienta OIDC tak, aby ověřil datovou část webhooku pomocí 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()

Další kroky