Catch-22는 WIF로 보안 가능한 스트리밍 TCP WCF 서비스를 방지합니다. 내 크리스마스를 망치고 정신 건강


181

내가 할 수있는 요구 사항이 A가 WIF를 사용하여 WCF net.tcp 서비스 엔드 포인트를 스트리밍 보안을 . 토큰 서버에 대한 수신 전화를 인증해야합니다. 서비스는 많은 양의 데이터를 전송하도록 설계되었으므로 스트리밍됩니다.

불가능한 것 같습니다. 그리고 내가 잡을 수 없다면 크리스마스가 망가질 것이고 메리 쇼핑객들이 천천히 차가워지는 몸을 넘어가는 동안 홈통에서 죽을 것입니다. 진지한 토트.

왜 이것이 불가능합니까? 여기 캐치 -22가 있습니다.

클라이언트에서는 토큰 서버에서 얻은 GenericXmlSecurityToken 으로 채널을 만들어야 합니다. 문제 없습니다.

// people around here hate the Framework Design Guidelines.
var token = Authentication.Current._Token;
var service = base.ChannelFactory.CreateChannelWithIssuedToken(token);
return service.Derp();

"문제 없음"이라고 했습니까? 문제. 사실, NullReferenceException스타일 문제.

"브로,"나는 프레임 워크에 물었다. "널 체크도하지?" 프레임 워크는 침묵했기 때문에 분해하고

((IChannel)(object)tChannel).
    GetProperty<ChannelParameterCollection>().
    Add(federatedClientCredentialsParameter);

예외의 원인이었으며 GetProperty호출이를 반환했습니다 null. 그래서 WTF? 메시지 보안을 설정하고 클라이언트 자격 증명 유형을 IssuedToken로 설정하면 이 속성이 이제 존재합니다 ClientFactory.

<binding name="OMGWTFLOL22" transferMode="Streamed" >
    <security mode="Message">
        <message clientCredentialType="IssuedToken"/>
    </security>
</binding>

단. 더 이상 NRE가 없습니다. 그러나 이제 내 의뢰인은 태어날 때 잘못이되었다 (아직도 그를 사랑하라). WCF 진단을 통해 발굴 (팁 : 최악의 적들이 그들을 분쇄하고 당신 앞에서 운전하지만 여성과 아이들의 애가를 즐기기 직전 에이 작업을 수행하도록하십시오), 나는 그것이 서버와 클라이언트 사이의 보안 불일치 때문이라고 생각합니다.

요청한 업그레이드는 'net.tcp : // localhost : 49627 / MyService'에서 지원되지 않습니다. 바인딩이 일치하지 않기 때문일 수 있습니다 (예 : 서버가 아닌 클라이언트에서 사용 가능한 보안).

호스트의 진드기 점검 (다시 : 호감, 운전, 로그 읽기, 애도를 즐기십시오), 이것이 사실임을 알았습니다.

프로토콜 유형 application / ssl-tls가 해당 유형의 업그레이드를 지원하지 않는 서비스로 전송되었습니다.

"자기야, 나는 호스트에서 메시지 보안을 켜겠다!" 그리고 나도 그래 어떻게 보이는지 알고 싶다면 클라이언트 구성의 정확한 사본입니다. 찾다.

결과 : Kaboom.

바인딩 ( 'NetTcpBinding', ' http://tempuri.org/ ')은 메시지 레벨 보안과 함께 구성 할 수없는 스트리밍을 지원합니다. 다른 전송 모드를 선택하거나 전송 수준 보안을 선택하십시오.

따라서 내 호스트는 tokens를 통해 스트리밍 및 보안 될 수 없습니다 . 캐치 -22.

tl; dr : WIF를 사용하여 스트리밍 된 net.tcp WCF 엔드 포인트를 어떻게 보호 할 수 있습니까 ???


3
좋아, 아마도 무지한 질문이지만 WIF에는 실제로 메시지 모드가 필요합니까? 그것은 분명히 안된처럼 뭔가를 스트리밍으로 잘 작동하는 것처럼 전송 모드 소리<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
요아킴 이삭손

3
TransportWithMessageCredentialmode는 다른 옵션 일 수 있습니다.
Joachim Isaksson '12

3
TMLK, MessageSecurity는 버퍼링 된 페이로드에 서명하고 암호화 할 수 있지만 스트림을 처리 할 때는 오류가 발생합니다. authenticationMode = IssuedTokenOverTransport 사용을 고려 했습니까?
오노 센다이

7
휴일을 구할 수 있도록 과거의 유령을 소환 할 수 있는지 봅시다. 여기에 몇 가지 힌트가 있습니다 : social.msdn.microsoft.com/Forums/vstudio/en-US/…
OnoSendai

2
다른 사람들이 실험 할 수있는 테스트 사례 프로젝트를 게시 할 수 있습니까?
antiduh

답변:


41

WCF는 대부분의 사람들이 작동한다고 생각하는 방식으로 사전 인증을 수행하지 못하는 방식의 근본적인 문제로 인해 스트리밍 (MTOM 1 ) 을 통해 일부 영역에서 문제를 겪었습니다 (해당 채널에 대한 후속 요청에만 영향을 미침) , 첫 번째 요청이 아님) 좋아요, 이것은 정확히 당신의 문제는 아니지만 마지막에 당신에게 갈 것입니다. 일반적으로 HTTP 챌린지는 다음과 같이 작동합니다.

  1. 클라이언트가 서버를 익명으로 공격 함
  2. 서버에 죄송합니다. 401, 인증이 필요합니다
  3. 클라이언트가 인증 토큰으로 서버에 충돌
  4. 서버가 동의합니다.

이제 서버의 WCF 엔드 포인트에서 MTOM 스트리밍을 사용하려고 시도하면 불평하지 않습니다. 그러나 클라이언트 프록시에서 구성하면 바인딩과 일치해야하며 불의의 죽음으로 폭발합니다. 그 이유는 WCF가 방지하려고하는 위의 이벤트 순서는 다음과 같습니다.

  1. 클라이언트가 단일 POST에서 익명으로 100MB 파일을 서버로 스트리밍
  2. 서버에서 죄송합니다. 401, 인증이 필요합니다
  3. 클라이언트는 다시 인증 헤더를 사용하여 100MB 파일을 서버로 스트리밍합니다
  4. 서버가 동의합니다.

100MB 만 보내야 할 때 서버에 200MB를 보냈습니다. 음, 이것이 문제입니다. 답은 첫 번째 시도에서 인증을 보내는 것이지만 사용자 지정 동작을 작성하지 않으면 WCF에서 불가능합니다. 어쨌든 나는 떠난다.

너의 문제

먼저, 당신이 시도하는 것이 불가능하다는 것을 말씀 드리겠습니다 2 . 이제 바퀴 회전을 멈추기 위해 이유를 알려 드리겠습니다.

당신이 지금 비슷한 종류의 문제에서 방황하고 있다는 것이 저를 놀라게합니다. 메시지 레벨 보안을 사용하는 경우 클라이언트는 ws-security에 필요한 일반 해시 함수 및 xml 서명으로 메시지를 실제로 닫을 수 있기 전에 전체 데이터 스트림을 메모리에로드해야합니다. 단일 메시지 (실제로 메시지는 아니지만 단일 연속 스트림)에 서명하기 위해 전체 스트림을 읽어야하는 경우 여기에서 문제를 볼 수 있습니다. WCF는 메시지 보안을 계산하기 위해 "로컬"로 한 번 스트리밍 한 다음 다시 스트리밍하여 서버로 보냅니다. 이것은 명백히 어리석은 일이므로 WCF는 스트리밍 데이터에 대한 메시지 수준 보안을 허용하지 않습니다.

따라서 간단한 대답은 토큰을 초기 웹 서비스의 매개 변수 또는 SOAP 헤더로 보내고 사용자 지정 동작을 사용하여 토큰을 확인해야한다는 것입니다. 이를 위해 WS-Security를 ​​사용할 수 없습니다. 솔직히 이것은 WCF 문제가 아닙니다. 다른 스택에서 실제로 어떻게 작동하는지 알 수 없습니다.

MTOM 문제 해결

이것은 기본 인증을 위해 MTOM 스트리밍 문제를 해결 한 방법에 대한 예일 뿐이므로이 문제를 해결하고 비슷한 문제를 구현할 수 있습니다. 핵심은 사용자 정의 메시지 검사기를 사용하려면 전송 레벨 (SSL)을 제외하고 클라이언트 프록시 (서버에서 사용 가능한 상태로 유지)에서 모든 보안 개념을 사용하지 않아야한다는 것입니다.

this._contentService.Endpoint.Behaviors.Add(
    new BasicAuthenticationBehavior(
        username: this.Settings.HttpUser,
        password: this.Settings.HttpPass));
var binding = (BasicHttpBinding)this._contentService.Endpoint.Binding;
binding.Security.Mode = BasicHttpSecurityMode.Transport; // SSL only            
binding.Security.Transport.ClientCredentialType = 
   HttpClientCredentialType.None; // Do not provide

메시지 관리자와 사용자 지정 동작을 사용하여 직접 제공 할 것이기 때문에 여기에서 전송 보안을 해제했습니다.

internal class BasicAuthenticationBehavior : IEndpointBehavior
{
    private readonly string _username;
    private readonly string _password;

    public BasicAuthenticationBehavior(string username, string password)
    {
        this._username = username;
        this._password = password;
    }
    public void AddBindingParameters(ServiceEndpoint endpoint, 
        BindingParameterCollection bindingParameters) { }
    public void ApplyClientBehavior(ServiceEndpoint endpoint,
        ClientRuntime clientRuntime)
    {
        var inspector = new BasicAuthenticationInspector(
            this._username, this._password);
        clientRuntime.MessageInspectors.Add(inspector);
    }
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
        EndpointDispatcher endpointDispatcher) { }
    public void Validate(ServiceEndpoint endpoint) { }
}

internal class BasicAuthenticationInspector : IClientMessageInspector
{
    private readonly string _username;
    private readonly string _password;

    public BasicAuthenticationInspector(string username, string password)
    {
        this._username = username;
        this._password = password;
    }

    public void AfterReceiveReply(ref Message reply,
        object correlationState) { }

    public object BeforeSendRequest(ref Message request,
        IClientChannel channel)
    {
        // we add the headers manually rather than using credentials 
        // due to proxying issues, and with the 101-continue http verb 
        var authInfo = Convert.ToBase64String(
            Encoding.Default.GetBytes(this._username + ":" + this._password));

        var messageProperty = new HttpRequestMessageProperty();
        messageProperty.Headers.Add("Authorization", "Basic " + authInfo);
        request.Properties[HttpRequestMessageProperty.Name] = messageProperty;

        return null;
    }
}

따라서이 예는 MTOM 문제로 어려움을 겪고있는 사람을위한 것이지만 기본 WIF 보안 토큰 서비스에서 생성 한 토큰을 인증하기 위해 비슷한 것을 구현하기위한 뼈대입니다.

도움이 되었기를 바랍니다.

(1) 대용량 데이터 및 스트리밍

(2) WCF의 메시지 보안 ( "단점"참조)


MTOM and Basic Authorization, MTOM 및 OAuth2 ?
Kiquenet
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.