다음을 통해 공유


웹후크 엔드포인트를 보호하는 방법

메시지 전달을 처음부터 끝까지 보호하는 것은 시스템 간에 전송되는 중요한 정보의 기밀성, 무결성 및 신뢰성을 보장하는 데 중요합니다. 원격 시스템에서 수신된 정보를 신뢰할 수 있는 능력과 의지는 보낸 사람이 자신의 ID를 제공하는 데에 따릅니다. 통화 자동화에는 통신 이벤트를 보호할 수 있는 두 가지 방법이 있습니다. 하나는 Azure Event Grid에서 보낸 공유 IncomingCall 이벤트이고 다른 하나는 웹후크를 통해 통화 자동화 플랫폼에서 보낸 다른 모든 중간 통화 이벤트입니다.

수신 통화 이벤트

Azure Communication Services는 Azure Event Grid 구독을 사용하여 IncomingCall 이벤트를 전달합니다. 웹후크 구독 보호 방법에 대한 문서를 위해 Azure Event Grid 팀을 참조할 수 있습니다.

Call Automation 웹후크 이벤트

통화 자동화 이벤트는 사용자가 통화에 응답하거나 새 아웃바운드 통화를 걸면 지정된 웹후크 콜백 URI로 전송됩니다. 콜백 URI는 통화 자동화에서 연결할 수 있도록 올바른 방화벽 포트가 열려 있는 퍼블릭 엔드포인트이어야 하며 이 엔드포인트에는 유효한 HTTPS 인증서, DNS 이름 및 IP 주소가 있어야 합니다. 무단 액세스로부터 보호하는 데 필요한 단계를 수행하지 않으면 이 익명 공용 웹 서버로 인해 보안 위험이 발생할 수 있습니다.

이 보안을 개선할 수 있는 일반적인 방법은 API KEY 메커니즘을 구현하는 것입니다. 웹 서버는 런타임에 키를 생성하고 통화에 응답하거나 통화를 만들 때 콜백 URI에서 쿼리 매개 변수로 키를 제공할 수 있습니다. 웹 서버는 액세스를 허용하기 전에 통화 자동화에서 웹후크 콜백의 키를 확인할 수 있습니다. 일부 고객은 더 많은 보안 조치를 요구합니다. 이러한 경우 경계 네트워크 디바이스는 웹 서버나 애플리케이션 자체와 별도로 인바운드 웹후크를 확인할 수 있습니다. API 키 메커니즘만으로는 충분하지 않을 수 있습니다.

통화 자동화 웹후크 콜백 보안 강화

통화 자동화에서 보낸 각 중간 통화 웹후크 콜백은 인바운드 HTTPS 요청의 인증 헤더에서 서명된 JWT(JSON 웹 토큰)를 사용합니다. 표준 OIDC(Open ID Connect) JWT 유효성 검사 기술을 사용하여 다음과 같이 토큰 무결성을 보장할 수 있습니다. JWT 수명은 5분이며 콜백 URI로 전송되는 모든 이벤트에 새 토큰이 생성됩니다.

  1. Open ID 구성 URL(https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration)을 가져옵니다.
  2. Microsoft.AspNetCore.Authentication.JwtBearer NuGet 패키지를 설치합니다.
  3. NuGet 패키지와 Azure Communication Services 리소스 구성을 사용하여 JWT 유효성을 검사하도록 애플리케이션을 구성합니다. JWT 페이로드에 있는 audience 값이 필요합니다.
  4. 발급자, 대상 그룹 및 JWT 토큰의 유효성을 검사합니다.
    • 대상 그룹은 통화 자동화 클라이언트를 설정하는 데 사용한 Azure Communication Services 리소스 ID입니다. 가져오는 방법은 여기를 참조하세요.
    • OpenId 구성의 JWKS(JSON 웹 키 집합) 엔드포인트에는 JWT 토큰 유효성을 검사하는 데 사용되는 키가 포함되어 있습니다. 서명이 유효하고 토큰이 만료되지 않은 경우(생성 후 5분 이내) 클라이언트는 토큰을 권한 부여에 사용할 수 있습니다.

이 샘플 코드에서는 Microsoft.IdentityModel.Protocols.OpenIdConnect를 사용하여 웹후크 페이로드 유효성을 검사하는 방법을 보여줍니다.

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

통화 자동화 웹후크 콜백 보안 강화

통화 자동화에서 보낸 각 중간 통화 웹후크 콜백은 인바운드 HTTPS 요청의 인증 헤더에서 서명된 JWT(JSON 웹 토큰)를 사용합니다. 표준 OIDC(Open ID Connect) JWT 유효성 검사 기술을 사용하여 다음과 같이 토큰 무결성을 보장할 수 있습니다. JWT 수명은 5분이며 콜백 URI로 전송되는 모든 이벤트에 새 토큰이 생성됩니다.

  1. Open ID 구성 URL(https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration)을 가져옵니다.
  2. 다음 샘플에서는 Maven을 프로젝트 빌드 도구로 사용하여 spring initializr를 사용해 만든 Spring 프레임워크를 사용합니다.
  3. 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. JWT와 Azure Communication Services 리소스 구성의 유효성을 검사하도록 애플리케이션을 구성합니다. JWT 페이로드에 있는 audience 값이 필요합니다.
  2. 발급자, 대상 그룹 및 JWT 토큰의 유효성을 검사합니다.
    • 대상 그룹은 통화 자동화 클라이언트를 설정하는 데 사용한 Azure Communication Services 리소스 ID입니다. 가져오는 방법은 여기를 참조하세요.
    • OpenId 구성의 JWKS(JSON 웹 키 집합) 엔드포인트에는 JWT 토큰 유효성을 검사하는 데 사용되는 키가 포함되어 있습니다. 서명이 유효하고 토큰이 만료되지 않은 경우(생성 후 5분 이내) 클라이언트는 토큰을 권한 부여에 사용할 수 있습니다.

이 샘플 코드에서는 JWT를 사용하여 웹후크 페이로드 유효성을 검사하도록 OIDC 클라이언트를 구성하는 방법을 보여줍니다.

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;
    }
}

통화 자동화 웹후크 콜백 보안 강화

통화 자동화에서 보낸 각 중간 통화 웹후크 콜백은 인바운드 HTTPS 요청의 인증 헤더에서 서명된 JWT(JSON 웹 토큰)를 사용합니다. 표준 OIDC(Open ID Connect) JWT 유효성 검사 기술을 사용하여 다음과 같이 토큰 무결성을 보장할 수 있습니다. JWT 수명은 5분이며 콜백 URI로 전송되는 모든 이벤트에 새 토큰이 생성됩니다.

  1. Open ID 구성 URL(https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration)을 가져옵니다.
  2. 다음 패키지를 설치합니다.
npm install express jwks-rsa jsonwebtoken
  1. JWT와 Azure Communication Services 리소스 구성의 유효성을 검사하도록 애플리케이션을 구성합니다. JWT 페이로드에 있는 audience 값이 필요합니다.
  2. 발급자, 대상 그룹 및 JWT 토큰의 유효성을 검사합니다.
    • 대상 그룹은 통화 자동화 클라이언트를 설정하는 데 사용한 Azure Communication Services 리소스 ID입니다. 가져오는 방법은 여기를 참조하세요.
    • OpenId 구성의 JWKS(JSON 웹 키 집합) 엔드포인트에는 JWT 토큰 유효성을 검사하는 데 사용되는 키가 포함되어 있습니다. 서명이 유효하고 토큰이 만료되지 않은 경우(생성 후 5분 이내) 클라이언트는 토큰을 권한 부여에 사용할 수 있습니다.

이 샘플 코드에서는 JWT를 사용하여 웹후크 페이로드 유효성을 검사하도록 OIDC 클라이언트를 구성하는 방법을 보여줍니다.

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}`);
});

통화 자동화 웹후크 콜백 보안 강화

통화 자동화에서 보낸 각 중간 통화 웹후크 콜백은 인바운드 HTTPS 요청의 인증 헤더에서 서명된 JWT(JSON 웹 토큰)를 사용합니다. 표준 OIDC(Open ID Connect) JWT 유효성 검사 기술을 사용하여 다음과 같이 토큰 무결성을 보장할 수 있습니다. JWT 수명은 5분이며 콜백 URI로 전송되는 모든 이벤트에 새 토큰이 생성됩니다.

  1. Open ID 구성 URL(https://acscallautomation.communication.azure.com/calling/.well-known/acsopenidconfiguration)을 가져옵니다.
  2. 다음 패키지를 설치합니다.
pip install flask pyjwt
  1. JWT와 Azure Communication Services 리소스 구성의 유효성을 검사하도록 애플리케이션을 구성합니다. JWT 페이로드에 있는 audience 값이 필요합니다.
  2. 발급자, 대상 그룹 및 JWT 토큰의 유효성을 검사합니다.
    • 대상 그룹은 통화 자동화 클라이언트를 설정하는 데 사용한 Azure Communication Services 리소스 ID입니다. 가져오는 방법은 여기를 참조하세요.
    • OpenId 구성의 JWKS(JSON 웹 키 집합) 엔드포인트에는 JWT 토큰 유효성을 검사하는 데 사용되는 키가 포함되어 있습니다. 서명이 유효하고 토큰이 만료되지 않은 경우(생성 후 5분 이내) 클라이언트는 토큰을 권한 부여에 사용할 수 있습니다.

이 샘플 코드에서는 JWT를 사용하여 웹후크 페이로드 유효성을 검사하도록 OIDC 클라이언트를 구성하는 방법을 보여줍니다.

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

다음 단계