다음을 통해 공유


시나리오: 서명된 SHR(HTTP 요청)

AgentID용 Microsoft Entra SDK와 함께 서명된 SHR(HTTP 요청)을 사용하여 PoP(소유 증명) 토큰 보안을 구현합니다. PoP 토큰은 토큰을 공개 키에 암호화적으로 바인딩하여 다운스트림 API를 호출할 때 토큰 도난 및 재생 공격을 방지합니다.

필수 조건

  • 활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
  • 소유 증명 지원을 사용하도록 설정된 상태로 배포되고 실행되는 AgentID용 Microsoft Entra SDK입니다. 설치 지침은 설치 가이드 를 참조하세요.
  • RSA 키 쌍 - 암호화 서명을 위한 퍼블릭/프라이빗 키 쌍을 생성합니다. 공개 키는 SDK에서 구성되지만 프라이빗 키는 애플리케이션에서 안전하게 유지됩니다.
  • PoP 토큰을 지원하는 다운스트림 API - 대상 API는 소유 증명 토큰의 유효성을 검사하고 공개 키를 사용하여 서명을 확인해야 합니다.
  • Microsoft Entra ID의 적절한 권한 - 계정에 애플리케이션을 등록하고 PoP 설정을 구성할 수 있는 권한이 있어야 합니다.

키 쌍 생성

PoP 토큰을 구현하기 전에 RSA 키 쌍을 생성합니다. 프라이빗 키는 서명 요청을 위해 애플리케이션에 남아 있지만 공개 키는 AgentID용 Microsoft Entra SDK에서 구성됩니다.

# Generate RSA private key
openssl genrsa -out private.pem 2048

# Extract public key
openssl rsa -in private.pem -pubout -out public.pem

# Base64 encode public key for configuration
base64 -w 0 public.pem > public.pem.b64

# View base64-encoded key
cat public.pem.b64

구성 / 설정

RSA 공개 키 및 다운스트림 API 설정을 사용하여 AgentID용 Microsoft Entra SDK를 구성합니다. 보안 구성 저장소에 중요한 키를 저장합니다.

SDK 구성

apiVersion: v1
kind: Secret
metadata:
  name: shr-keys
type: Opaque
data:
  public-key: <base64-encoded-public-key>

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: sidecar-config
data:
  # ... other configuration ...
  DownstreamApis__SecureApi__BaseUrl: "https://api.contoso.com"
  DownstreamApis__SecureApi__Scopes: "api://secureapi/.default"
  DownstreamApis__SecureApi__AcquireTokenOptions__PopPublicKey: "<base64-public-key>"

사용 예제

애플리케이션에서 PoP 토큰을 사용하려면 공개 키를 지정하여 AgentID용 Microsoft Entra SDK에서 PoP 토큰을 요청한 다음 API 요청에 포함합니다.

TypeScript

// Request PoP token
async function getPopToken(incomingToken: string, publicKey: string): Promise<string> {
  const sidecarUrl = process.env.SIDECAR_URL!;
  
  const response = await fetch(
    `${sidecarUrl}/AuthorizationHeader/SecureApi?` +
    `optionsOverride.AcquireTokenOptions.PopPublicKey=${encodeURIComponent(publicKey)}`,
    {
      headers: {
        'Authorization': incomingToken
      }
    }
  );
  
  const data = await response.json();
  return data.authorizationHeader; // Returns "PoP <pop-token>"
}

// Use PoP token with signed request
async function callSecureApi(incomingToken: string, publicKey: string, privateKey: string) {
  // Get PoP token from the SDK
  const popToken = await getPopToken(incomingToken, publicKey);
  
  // Make request to API with PoP token
  const response = await fetch('https://api.contoso.com/secure/data', {
    headers: {
      'Authorization': popToken
    }
  });
  
  return await response.json();
}

파이썬

import base64
import requests
import os

def get_pop_token(incoming_token: str, public_key: str) -> str:
    """Get a PoP token from the SDK."""
    sidecar_url = os.getenv('SIDECAR_URL', 'http://localhost:5000')
    
    response = requests.get(
        f"{sidecar_url}/AuthorizationHeader/SecureApi",
        params={
            'optionsOverride.AcquireTokenOptions.PopPublicKey': public_key
        },
        headers={'Authorization': incoming_token}
    )
    
    response.raise_for_status()
    data = response.json()
    return data['authorizationHeader']

def call_secure_api(incoming_token: str, public_key_b64: str):
    """Call API with PoP token."""
    pop_token = get_pop_token(incoming_token, public_key_b64)
    
    response = requests.get(
        'https://api.contoso.com/secure/data',
        headers={'Authorization': pop_token}
    )
    
    return response.json()

요청당 SHR

다른 API 또는 범위에 대해 다른 공개 키를 지정하여 요청별로 PoP 설정을 재정의할 수 있습니다.

// Enable SHR for specific request
const response = await fetch(
  `${sidecarUrl}/AuthorizationHeader/Graph?` +
  `optionsOverride.AcquireTokenOptions.PopPublicKey=${encodeURIComponent(publicKey)}`,
  {
    headers: { 'Authorization': incomingToken }
  }
);

암호 키 관리

RSA 키를 보호하고 필요한 경우 키 회전을 사용하도록 설정하는 보안 키 관리 방법을 구현합니다.

키 스토리지 보호

Kubernetes 비밀을 사용하여 RSA 키를 안전하게 저장합니다.

# Store keys in Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
  name: shr-keys
type: Opaque
data:
  public-key: <base64-encoded-public-key>
  private-key: <base64-encoded-private-key>

---
# Mount keys in application
volumes:
- name: shr-keys
  secret:
    secretName: shr-keys
    defaultMode: 0400

containers:
- name: app
  volumeMounts:
  - name: shr-keys
    mountPath: /keys
    readOnly: true

키 회전

OpenSSL을 사용하여 주기적으로 서명 키를 회전하고 구성을 업데이트합니다.

#!/bin/bash
# Script to rotate SHR keys

# Generate new key pair
openssl genrsa -out private-new.pem 2048
openssl rsa -in private-new.pem -pubout -out public-new.pem
base64 -w 0 public-new.pem > public-new.pem.b64

# Update Kubernetes secret
kubectl create secret generic shr-keys-new \
  --from-file=public-key=public-new.pem.b64 \
  --from-file=private-key=private-new.pem \
  --dry-run=client -o yaml | kubectl apply -f -

# Update deployment to use new keys
kubectl rollout restart deployment myapp

PoP 토큰 유효성 검사

다운스트림 API는 PoP 토큰의 유효성을 검사하여 올바르게 서명되고 요청에 바인딩되었는지 확인해야 합니다.

  1. 토큰에서 추출된 공개 키를 사용하여 JWT 서명 확인
  2. 표준 JWT 클레임 유효성 검사(발급자, 대상 그룹, 만료)
  3. 클레임에 cnf 예상된 공개 키가 포함되어 있는지 확인합니다.
  4. 클레임의 키를 사용하여 HTTP 요청 서명이 일치하는지 확인합니다.cnf

혜택

소유 증명 토큰을 사용하여 서명된 HTTP 요청을 구현하면 다음과 같은 몇 가지 보안 이점이 있습니다.

  • 토큰 바인딩: 각 토큰은 특정 공개 키에 암호화적으로 바인딩되어 가로채더라도 무단 사용을 방지합니다.
  • 재생 방지: 공격자는 해당 프라이빗 키를 보유하지 않고 캡처된 토큰을 재생할 수 없습니다.
  • 향상된 보안: 토큰 도난에 대한 보호를 제공하며, 특히 중요한 작업 및 높은 보안 환경에 중요합니다.
  • 소유 증명: 클라이언트가 토큰에 해당하는 프라이빗 키를 보유한다는 것을 암호화적으로 증명합니다.

모범 사례

서명된 HTTP 요청을 구현할 때 보안 및 운영 안정성을 유지하려면 다음 방법을 따르세요.

  • 프라이빗 키 보호: 로그, 구성 파일 또는 코드 리포지토리에 프라이빗 키를 노출하지 않습니다. 키 볼트 또는 구성 관리 시스템을 사용하여 안전하게 저장합니다.
  • 정기적으로 키 회전: 키 회전 일정을 구현하여 잠재적인 키 손상의 영향을 최소화합니다. 회전하는 동안 SDK 및 다운스트림 API를 모두 업데이트합니다.
  • Per-API 키 사용: 다른 API 또는 보안 영역에 서로 다른 키 쌍을 사용하여 하나의 키가 손상된 경우 영향을 제한합니다.
  • 사용량 모니터링: PoP 토큰 사용량을 감사하고 모니터링하여 의심스러운 패턴 또는 무단 액세스 시도를 검색합니다.
  • 철저히 테스트: PoP 토큰 유효성 검사가 프로덕션에 배포하기 전에 올바르게 작동하는지 확인하여 서명 유효성 검사와 요청 바인딩 검사가 모두 통과했는지 확인합니다.

다음 단계

PoP 토큰을 구현한 후: