Catch-22 ป้องกันบริการ TCP WCF แบบสตรีมที่ปลอดภัยโดย WIF ทำลายคริสมาสต์ของฉันสุขภาพจิต


181

ฉันมีความต้องการไปสู่การรักษาความปลอดภัยการสตรีม WCF net.tcp ปลายทางบริการโดยใช้ WIF ควรตรวจสอบการโทรเข้ากับเซิร์ฟเวอร์โทเค็นของเรา บริการถูกสตรีมเนื่องจากถูกออกแบบมาเพื่อถ่ายโอนข้อมูลจำนวนมากและสิ่งต่าง ๆ

ดูเหมือนว่าจะเป็นไปไม่ได้ และถ้าฉันไม่สามารถจับได้คริสต์มาสของฉันจะถูกทำลายและฉันจะดื่มจนตายในรางน้ำในขณะที่นักช้อปผู้ร่าเริงเดินผ่านร่างกายที่เย็นตัวลงอย่างช้าๆของฉัน Totes จริงจังพวกคุณ

ทำไมเป็นไปไม่ได้ นี่คือ Catch-22

บนไคลเอนต์ฉันต้องสร้างช่องทางด้วยGenericXmlSecurityToken ที่ฉันได้รับจากเซิร์ฟเวอร์โทเค็นของเรา ไม่มีปัญหา

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

ฉันว่า "ไม่มีปัญหา" หรือเปล่า? problemo ในความเป็นจริงNullReferenceExceptionปัญหาสไตล์

"Bro," ฉันถาม Framework, "คุณตรวจสอบเป็นโมฆะหรือไม่" Framework ทำงานเงียบฉันเลยถอดออกและพบว่า

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

เป็นแหล่งที่มาของข้อยกเว้นและว่าโทรกลับGetProperty nullงั้น WTF เหรอ? ปรากฎว่าถ้าฉันเปิดการรักษาความปลอดภัยของข้อความและตั้งค่าประเภทข้อมูลรับรองของลูกค้าเป็นIssuedTokenคุณสมบัตินี้ตอนนี้มีอยู่ในClientFactory(protip: ไม่มี "SetProperty" เทียบเท่าใน IChannel, ไอ้เลว)

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

หวาน. ไม่มี NREs อีกต่อไป อย่างไรก็ตามตอนนี้ลูกค้าของฉันเกิดข้อผิดพลาดตั้งแต่แรกเกิด (ยังคงรักเขาอยู่) ขุดผ่านการวินิจฉัยของ WCF (protip: ทำให้ศัตรูที่เลวร้ายที่สุดของคุณทำสิ่งนี้หลังจากที่พวกมันบดขยี้และขับไล่พวกเขาไปข้างหน้าคุณ แต่ก่อนที่จะเพลิดเพลินไปกับความเศร้าโศกของผู้หญิงและเด็ก ๆ )

การอัพเกรดที่ร้องขอไม่รองรับโดย 'net.tcp: // localhost: 49627 / MyService' อาจเกิดจากการรวมที่ไม่ตรงกัน (ตัวอย่างเช่นความปลอดภัยที่เปิดใช้งานบนไคลเอ็นต์และไม่ได้อยู่บนเซิร์ฟเวอร์)

ตรวจสอบ diags โฮสต์ (อีกครั้ง: บดขยี้ขับอ่านบันทึกเพลิดเพลินไปกับการคร่ำครวญ) ฉันเห็นว่านี่เป็นความจริง

แอปพลิเคชันประเภทโปรโตคอล / ssl-tls ถูกส่งไปยังบริการที่ไม่รองรับการอัปเกรดประเภทนั้น

"ดีตัวเอง" ฉันพูดว่า "ฉันจะเปิดการรักษาความปลอดภัยข้อความบนโฮสต์!" และฉันก็ทำ หากคุณต้องการรู้ว่ามันเป็นอย่างไรมันเป็นสำเนาที่แน่นอนของการกำหนดค่าไคลเอนต์ เงยหน้าขึ้นมอง

ผลลัพธ์: Kaboom

การเชื่อมโยง ('NetTcpBinding', ' http://tempuri.org/ ') รองรับการสตรีมซึ่งไม่สามารถกำหนดค่าพร้อมความปลอดภัยระดับข้อความ พิจารณาเลือกโหมดการถ่ายโอนอื่นหรือเลือกระดับความปลอดภัยของการขนส่ง

ดังนั้นโฮสต์ของฉันไม่สามารถเป็นได้ทั้งแบบสตรีมและการรักษาความปลอดภัยผ่านทางราชสกุล จับ 22.

tl; dr: ฉันจะรักษาความปลอดภัยให้กับจุดสิ้นสุด WCF net.tcp ที่สตรีมโดยใช้ WIF ได้อย่างไร


3
ตกลงอาจเป็นคำถามที่ไม่รู้ แต่ WIF จำเป็นต้องใช้โหมดข้อความหรือไม่ โหมดการขนส่งดูเหมือนว่าจะทำงานได้ดีขึ้นด้วยการสตรีมสิ่งที่ยังไม่ทดลองที่ชัดเจน<security mode="Transport" /> <transport clientCredentialType="IssuedToken" /> </security>
Joachim Isaksson

3
TransportWithMessageCredentialโหมดอาจเป็นตัวเลือกอื่น
Joachim Isaksson

3
TMLK, MessageSecurity สามารถเซ็นชื่อและเข้ารหัสเพย์โหลดบัฟเฟอร์ แต่จะสะดุดเมื่อจัดการกับสตรีม คุณได้พิจารณาการใช้ authenticationMode = IssuedTokenOverTransport หรือไม่?
OnoSendai

7
ให้ฉันดูว่าฉันสามารถเรียกผีมาจากอดีตเพื่อช่วยให้วันหยุดของคุณนั้นดีขึ้นได้ไหม มีคำแนะนำบางอย่างที่นี่: social.msdn.microsoft.com/Forums/vstudio/en-US/…
OnoSendai

2
โอกาสใดที่คุณสามารถโพสต์โครงการกรณีทดสอบที่ผู้อื่นสามารถทดลองด้วยได้
antiduh

คำตอบ:


41

WCF มี gotchas ในบางพื้นที่ที่มีการสตรีมมิ่ง (ฉันกำลังมองหาคุณ, MTOM 1 ) เนื่องจากปัญหาพื้นฐานในการล้มเหลวในการดำเนินการตรวจสอบสิทธิ์ล่วงหน้าก่อนที่คนส่วนใหญ่คิดว่าควรจะทำงานได้ ไม่ใช่คำขอแรก) ตกลงดังนั้นนี่ไม่ใช่ปัญหาของคุณอย่างแน่นอน แต่โปรดติดตามตามที่ฉันจะไปหาคุณในตอนท้าย โดยปกติความท้าทาย HTTP จะทำงานดังนี้:

  1. ไคลเอนต์เซิร์ฟเวอร์ฮิตโดยไม่ระบุชื่อ
  2. เซิร์ฟเวอร์บอกว่าขอโทษ 401 ฉันต้องการการรับรองความถูกต้อง
  3. ไคลเอนต์เซิร์ฟเวอร์ยอดนิยมกับโทเค็นการรับรองความถูกต้อง
  4. เซิร์ฟเวอร์ยอมรับ

ตอนนี้ถ้าคุณเคยลองเปิดใช้งานการสตรีม MTOM บนจุดสิ้นสุด WCF บนเซิร์ฟเวอร์มันจะไม่บ่น แต่เมื่อคุณกำหนดค่าบนไคลเอนต์พร็อกซี่ (ตามที่คุณควรจะต้องตรงกับการผูก) มันจะระเบิดในความตายคะนอง เหตุผลสำหรับสิ่งนี้คือลำดับเหตุการณ์ข้างต้นที่ WCF พยายามป้องกันคือ:

  1. ไคลเอนต์สตรีมไฟล์ 100MB ไปยังเซิร์ฟเวอร์โดยไม่ระบุชื่อใน POST เดียว
  2. เซิร์ฟเวอร์บอกว่าขอโทษ 401 ฉันต้องการการรับรองความถูกต้อง
  3. ลูกค้าอีกครั้งสตรีมไฟล์ 100MB ไปยังเซิร์ฟเวอร์ที่มีส่วนหัวการตรวจสอบ
  4. เซิร์ฟเวอร์ยอมรับ

โปรดสังเกตว่าคุณเพิ่งส่ง 200MB ไปยังเซิร์ฟเวอร์เมื่อคุณต้องการส่ง 100MB เท่านั้น นี่เป็นปัญหา คำตอบคือการส่งการรับรองความถูกต้องในครั้งแรก แต่เป็นไปไม่ได้ใน WCF โดยไม่ต้องเขียนพฤติกรรมที่กำหนดเอง อย่างไรก็ตามฉันพูดนอกเรื่อง

ปัญหาของคุณ

ก่อนอื่นให้ฉันบอกคุณว่าสิ่งที่คุณพยายามเป็นไปไม่ได้2ที่ 2ตอนนี้เพื่อให้คุณหยุดหมุนล้อให้ฉันบอกคุณทำไม:

มันทำให้ฉันรู้สึกว่าคุณกำลังหลงทางในปัญหาที่คล้ายกัน หากคุณเปิดใช้งานการรักษาความปลอดภัยระดับข้อความลูกค้าจะต้องโหลดสตรีมข้อมูลทั้งหมดลงในหน่วยความจำก่อนที่จะสามารถปิดข้อความด้วยฟังก์ชั่นแฮชปกติและลายเซ็น xml ที่ต้องการโดย ws-security หากต้องอ่านสตรีมทั้งหมดเพื่อลงชื่อข้อความเดียว (ซึ่งไม่ใช่ข้อความจริงๆ แต่เป็นสตรีมต่อเนื่องเดียว) จากนั้นคุณจะเห็นปัญหาที่นี่ 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) Message Security in WCF (ดูที่ "ข้อเสีย")


MTOM and Basic AuthorizationและMTOM และ OAuth2 ?
Kiquenet
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.