SOAP 메시지 암호화

 

롭 하워드
Microsoft Corporation

2001년 9월 27일

XML Web Services는 HTML의 범위에서 정보를 해제하기 위한 인에이블러로 업계로부터 환영받고 있습니다. W3C에 제출된 프로토콜인 SOAP를 사용하여 데이터를 XML로 인코딩하고 여러 인터넷 프로토콜을 사용하여 전송할 수 있습니다. HTTP가 가장 일반적입니다. 발신자와 수신자 모두 메시지 형식, 즉 SOAP가 정의하는 프로토콜에 동의할 수 있는 한 플랫폼 독립적 방식으로 정보를 쉽게 교환할 수 있습니다.

Microsoft®는 비전을 제시하는 것뿐만 아니라 기술과 제품을 구현하여 XML Web Services의 성공을 보장하는 데 있어 리더십 역할을 수행하고 있습니다. 물론 XML Web Services를 지원하는 가장 기대되는 제품 중 하나는 Microsoft® ASP.NET.

ASP.NET 사용하여 XML Web Services 빌드

ASP.NET 웹 서비스를 간단하게 빌드할 수 있습니다. ASP가 초기 웹 개발자를 위한 웹 개발에 혁명을 일으키고 여기에 삽입된 PERL/CGI의 추억처럼 ASP.NET HTML 또는 XML을 내보내는 웹 애플리케이션 빌드에 혁명을 일으킬 것입니다(SOAP 프로토콜 준수).

언급했듯이 ASP.NET 사용하여 XML Web Services를 빌드하는 것은 간단합니다. 개발자는 확장명 .asmx를 사용하여 파일 내에 존재하는 사용자 지정 특성(사용자 지정 특성은 .NET의 고유한 기능)으로 표시된 메서드 정의를 사용하여 클래스를 작성합니다. 예를 들어 다음은 간단한 웹 서비스입니다.

<%@ WebService Language="C#" Class="SimpleMath" %>

using System.Web.Services;

public class SimpleMath {

  [WebMethod]
  public int Add(int a, int b) {
    return a+b;
  }
}

SOAP 메시지를 이 서비스로 보낼 수 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <Add xmlns="http://tempuri.org/" />
      <a>5</a>
      <b>8</b>
    </Add>
  </soap:Body>
</soap:Envelope>

그러면 ASP.NET XML 웹 서비스가 그에 따라 응답합니다.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <AddResponse xmlns="http://tempuri.org/" />
      <AddResult>13</AddResult>
    </AddResponse>
  </soap:Body>
</soap:Envelope>

개발자의 이점은 분명합니다. XML, HTTP, SOAP 또는 XML Web Services를 빌드하는 데 사용되는 다른 프로토콜을 이해할 필요가 없습니다. 대신 개발자는 솔루션 빌드에 중점을 둡니다.

보안은 어떻습니까?

개발자의 주요 관심사 중 하나는 보안입니다. 위의 예제에서는 두 XML Web Services 간에 'the wire'로 전송되는 내용을 보여 줍니다. 참고로 교환은 일반 텍스트로 수행됩니다. 데이터는 일반 텍스트로 전송되고 최종 대상에 도달하기 위해 인터넷을 통해 라우팅되므로 누구나 메시지 교환을 볼 수 있습니다. 위의 예제의 경우 문제가 되지 않을 수 있습니다. 그러나 교환이 은행에 대한 요청으로 구성되고 응답에 계좌 번호 및 잔액 목록이 포함되어 있거나 신용 카드 번호와 같이 더 흥미로운 것을 반환한 요청이 포함되어 있다면 어떨까요? 분명히 이것은 우리가 누군가가보고 싶은 것이 아닙니다!

암호화 지원

교환할 데이터가 있고 눈을 돌리지 않으려는 경우 암호화를 통해 통신을 보호해야 합니다. SOAP 사양은 XML Web Services에 대한 암호화를 정의하지 않습니다. 대신 SOAP 프로토콜의 구현자에 남아 있으며, 대부분의 경우 제안된 보안 조치는 HTTPS(보안 HTTP)입니다.

HTTPS를 통한 SOAP는 안전합니다. HTTP 메시지의 헤더와 본문을 포함한 전체 HTTP 메시지는 공용 비대칭 암호화 알고리즘을 사용하여 암호화됩니다. 그러나 이러한 유형의 암호화는 전송 프로토콜 HTTP에 종속된 반면 SOAP는 전송 독립적 프로토콜입니다. SOAP 메시지는 HTTP, SMTP, TCP, UDP 등을 사용하여 보낼 수 있습니다. SOAP 메시지를 라우팅할 수도 있습니다. 즉, 서버가 메시지를 수신하고 최종 대상이 아닌지 확인하고 메시지를 다른 곳으로 라우팅할 수 있습니다. 라우팅 중에 HTTP - UDP - SMTP> ->> HTTP와 같은 다양한 전송 프로토콜을 사용할 수 있습니다. 따라서 보안을 위한 전송 프로토콜에 대한 종속성에는 라우팅 서버에서 메시지를 읽기 위해 암호를 해독한 다음 다른 전송 프로토콜에 대해 다시 암호화할 수 있어야 하므로 라우팅 서버에 얼마나 많은 트러스트를 배치할 수 있는지에 대한 본질적인 문제가 있습니다. 발생하는 다른 질문은 다음과 같습니다.

  • 전송 프로토콜이 보안 통신을 지원하나요?
  • 모든 데이터와 데이터의 일부를 암호화하는 데 드는 비용은 얼마인가요?

위의 단락은 완전히 유효하지만 현재 거의 모든 XML Web Services가 전송 프로토콜에 HTTP를 사용하고 현재 메시지 라우팅을 지원하는 XML Web Services가 거의 없으므로 대부분의 경우 HTTPS가 SOAP 메시지 교환을 보호하는 데 권장되는 솔루션이라는 점에 유의해야 합니다. 그러나 SOAP 메시지의 암호화를 지원하는 ASP.NET Web Services를 쉽게 빌드할 수 있습니다. 전송 프로토콜에서 종속성을 제거하고 메시지의 일부만 암호화할 수 있도록 하는 공용 암호화 알고리즘을 사용하여 암호화를 수행할 수 있습니다. 예를 들어 아래 솔루션은 DES(단일 패스 데이터 암호화 표준) 암호화(두 당사자가 공통 키를 공유해야 하는 공용 대칭 암호화 알고리즘)를 사용하고 메시지 본문만 암호화됩니다(라우팅 서버가 헤더를 읽을 수 있지만 본문은 읽을 수 없음).

SOAP 확장

ASP.NET Web Services에 대해 빌드할 암호화 지원은 SOAP 확장 형식으로 제공됩니다. SOAP 확장을 사용하면 ASP.NET 웹 서비스를 구현하는 개발자가 메서드에 다른 특성을 추가하고(WebMethod 특성과 함께) 메시지를 암호화하거나 암호 해독할 수 있습니다. 다음은 이 새 확장을 사용하여 ASP.NET Web Service의 코드가 어떻게 표시되는지 에 대한 샘플입니다.

<%@ WebService Language="C#" Class="CreditCardService" %>

using System.Web.Services;

public class CreditCardService {

  [WebMethod]
  [EncryptionExtension(Encrypt=EncryptMode.Response)]
  public string GetCreditCardNumber() {
    return "MC: 4111-1111-1111-1111";
  }
}

여기서는 상당히 간단한 예제를 사용하지만 GetCreditCardNumber 메서드에 단일 사용자 지정 특성을 추가했습니다.

  [EncryptionExtension(Encrypt=EncryptMode.Response)]

이 특성은 순간적으로 검사할 원본인 사용자 지정 SOAP 확장 특성입니다. 이 문서를 작성할 때 구현은 Encrypt=EncryptMode.Response 및 Decrypt=DecryptMode=Request 및 물론 EncryptMode.None 및 Decrypt.None 값에 대해서만 작동합니다.

일반 텍스트로 웹 서비스에 요청을 보낼 수 있으며 응답이 암호화됩니다. 일반적인 교환은 다음과 같습니다. 먼저 요청이 제공됩니다.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <GetCreditCardNumber xmlns="http://tempuri.org/" />
  </soap:Body>
</soap:Envelope>

다음으로 응답이 제공됩니다.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <GetCreditCardNumber xmlns="http://tempuri.org/">
      <GetCreditCardNumberResult>83 151 243 32 53 95 86 13 190 134 188 241 
198 209 72 114 122 38 180 34 194 138 16 97 221 195 239 86 26 152 94 27
      </GetCreditCardNumberResult>
    </GetCreditCardNumber>
  </soap:Body>
</soap:Envelope>

분명히 알 수 있듯이 응답은 여전히 유효한 SOAP 메시지입니다. 그러나 크레딧 카드 번호를 일반 텍스트로 전송하는 대신 암호화된 바이트 배열이 교환됩니다.

여기에 다른 보안 문제가 있습니다. instance 경우 이 메서드를 GetCreditCardNumber라고 하지 않아야 합니다. 이는 숙련된 해커에게 팁을줄 수 있기 때문일 수 있습니다. 그럼에도 불구하고 XML 웹 서비스에서 보낸 데이터는 올바른 키를 소유한 최종 클라이언트만 메시지의 암호를 해독할 수 있으므로 메시지의 보안을 손상시키지 않고 HTTP, SMTP 또는 기타 전송 프로토콜을 통해 라우팅할 수 있는 유효한 SOAP 메시지로 계속 전송된다는 점입니다. .NET 클라이언트의 경우 wsdl.exe 또는 Visual Studio .NET에서 생성된 프록시 클래스에 특성을 추가하는 문제일 뿐입니다.

...
[SoapDocumentMethodAttribute("http://tempuri.org/GetCreditCardNumber", 
                             Use= SoapBindingUse.Literal, 
                             ParameterStyle= SoapParameterStyle.Wrapped)]
[EncryptionExtension(Decrypt=DecryptMode.Request)]
public string GetCreditCardNumber() {
    object[] results = this.Invoke("GetCreditCardNumber", new object[0]);
    return ((string)(results[0]));
}
...

위의 두 경우 모두 SOAP 확장을 사용하고 있습니다. SOAP 확장은 SOAP serialization 프로세스와 하위 수준에서 상호 작용할 수 있도록 하는 SoapExtension에서 파생된 클래스입니다. 아래 다이어그램에서 빨간색 별표가 있는 점은 SOAP 확장을 통한 상호 작용의 기회를 나타냅니다.

그림 1. SOAP serialization 및 deserialization

SOAP EncryptionExtension 뒤에 있는 코드를 살펴보겠습니다. 모든 원본을 살펴보지 않고 작은 섹션만 살펴보겠습니다. 전체 원본은 를 참조 https://www.gotdotnet.com/team/rhoward하세요.

EncryptionExtension 클래스는 SoapExtension에서 파생됩니다. System.Web.Services.Protocols 네임스페이스에 있습니다. SoapExtension에서 상속되는 클래스는 여러 메서드 및 속성에 대한 구현을 제공해야 합니다. 가장 중요한 것은 아래에 표시된 ProcessMessage(SoapMessage 메시지) 메서드입니다.

public class EncryptionExtension : SoapExtension { 
   
  public override void ProcessMessage(SoapMessage message) {
    switch (message.Stage) {

      case SoapMessageStage.BeforeSerialize:
        break;

      case SoapMessageStage.AfterSerialize:
        Encrypt();
        break;

      case SoapMessageStage.BeforeDeserialize:
        Decrypt();
        break;

      case SoapMessageStage.AfterDeserialize:
        break;

      default:
        throw new Exception("invalid stage");
      }
   }
...
}

이 재정의된 메서드 내에서 위에 다이어그램된 4단계에서 SOAP 메시지의 serialization과 상호 작용할 수 있습니다. EncryptionExtension의 경우 암호화 및 암호 해독을 위한 AfterSerializeBeforeDeserialize 단계에만 참여합니다. 예를 들어 응답이 serialize된 후 GetCreditCardNumber 웹 서비스에 요청이 들어오는 경우 EncryptionExtensionProcessMessage()에서 Encrypt()를 호출합니다.

  private void Encrypt() {
    newStream.Position = 0;

    if ((encryptMode == EncryptMode.Request) || 
        (encryptMode == EncryptMode.Response)) {
      newStream = EncryptSoap(newStream);
    }

    Copy(newStream, oldStream);
  }

Encrypt() 메서드 내에서는 EncryptionExtension 특성의 EncryptMode 속성이 설정되어 있는지 확인하기 위해 검사. EncryptMode.Request 또는 EncryptMode.Response로 설정된 경우 내부 메서드 EncryptSoap()이 호출됩니다. EncryptSoap() 은 SOAP 메시지를 나타내는 스트림을 수락하고 암호화된 값이 있는 새 스트림을 반환합니다.

  public MemoryStream EncryptSoap(Stream streamToEncrypt) {
    streamToEncrypt.Position = 0;
    XmlTextReader reader = new XmlTextReader(streamToEncrypt);
    XmlDocument dom = new XmlDocument();
    dom.Load(reader);

    XmlNamespaceManager nsmgr = new XmlNamespaceManager(dom.NameTable);
    nsmgr.AddNamespace("soap", 
                       "https://schemas.xmlsoap.org/soap/envelope/");
    XmlNode node = dom.SelectSingleNode("//soap:Body", nsmgr);
    node = node.FirstChild.FirstChild;

    byte[] outData = Encrypt(node.InnerText);

    StringBuilder s = new StringBuilder();

    for(int i=0; i<outData.Length; i++) {
      if(i==(outData.Length-1))
        s.Append(outData[i]);
      else
        s.Append(outData[i] + " ");
    }

    node.InnerText = s.ToString();

    MemoryStream ms = new MemoryStream();
    dom.Save(ms);
    ms.Position = 0;

    return ms;
  }

EncryptSoap() 내에서 soap:Body의 첫 번째 노드를 암호화하기 위해 하드 와이어됩니다. 적절한 XmlNode가 선택되고 노드 내의 텍스트가 문자열을 수락하고 바이트 배열을 반환하는 다른 Encrypt() 메서드로 전달됩니다. .NET DES 클래스를 사용하여 문자열을 암호화하고 암호화된 값의 바이트 배열을 반환하는 최종 Encrypt() 메서드입니다.

  private byte[] Encrypt(string stringToEncrypt) {
    DESCryptoServiceProvider des = new DESCryptoServiceProvider();

    byte[] inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);

    MemoryStream ms = new MemoryStream();
    CryptoStream cs = new CryptoStream(ms, 
                                       des.CreateEncryptor( key, IV ), 
                                       CryptoStreamMode.Write);

    cs.Write(inputByteArray, 0, inputByteArray.Length);
    cs.FlushFinalBlock();

    return ms.ToArray();
  }

많은 사람들이 이 솔루션이 .NET 전용인지 물었습니다. 그것은 가장 확실히하지 않습니다. 그러나 두 당사자가 DES 암호화가 사용된다는 것을 알고 있어야 하며, non-.NET 호출자가 DES 알고리즘을 사용해야 합니다(DES는 공개적으로 사용할 수 있는 알고리즘임).

위의 예제의 개선 사항에 관해서는 다음과 같은 여러 가지가 있을 수 있습니다.

  • 비대칭 암호화 지원: 호출자는 서버의 공개 키를 사용하여 암호화하고 서버는 클라이언트의 키를 사용하여 응답을 암호화합니다.
  • 현재 이 예제는 일반 텍스트 요청 및 암호화된 응답만 지원합니다.
  • 인증 정보를 전송하기 위한 SOAP 헤더 암호화 및 해당 세부 정보의 암호화만 지원합니다.

요약

보듯이 ASP.NET 웹 서비스는 매우 강력합니다. 대부분의 개발자는 SOAP 확장의 기능과 Web Services에 대한 모든 종류의 새롭고 흥미로운 작업을 수행하는 데 사용할 수 있는 방법을 전혀 인식하지 못합니다. 예를 들어 ProcessMessage()에서 제공하는 기능 때문에 XML 압축, 라우팅, 암호화, 로깅, 결제 처리 등과 같은 ASP.NET Web Services 옵션으로 빌드하기가 매우 쉽습니다. 이와 같은 솔루션을 사용하면 SOAP를 사용하는 동안 인터넷을 통해 안전하게 통신할 수 있는 XML Web Services를 빌드할 수 있습니다.

Rob Howard 는 .NET Frameworks 팀의 ASP.NET 프로그램 관리자입니다. 그는 가족과 함께 여가 시간을 보내거나 워싱턴 동부에서 낚시를 합니다.