2014-12-05 14 views
19

Edycja dla przyszłych czytelników: Niestety, bounty przyznawane odpowiedź nie działa; nic teraz nie mogę zrobić. Ale czytać moja własna odpowiedź poniżej (podczas testów) - potwierdzone pracować z minimalnym kodu zmieniaWyłączyć certyfikat klienta SSL na * niektórych * kontrolerach WebAPI?

Mamy Chmura usługi Azure (WebRole), która znajduje się w całości w ASP.NET WebAPI 2.2 (nie MVC, przedni koniec jest kątowa). Niektóre z naszych kontrolerów/punktów końcowych REST rozmawiają z zewnętrzną usługą w chmurze za pośrednictwem protokołu SSL (certyfikat uwierzytelnienia klienta/wzajemnego uwierzytelniania), a pozostałe kontrolery/punkty końcowe rozmawiają z interfejsem HTML5/AngularJS, także za pośrednictwem protokołu SSL (ale bardziej tradycyjnego uwierzytelniania serwera). SSL). Nie mamy żadnego punktu końcowego bez SSL. Mamy włączone SSL Client poprzez zadania startowego chmura usług takich jak:

IF NOT DEFINED APPCMD SET APPCMD=%SystemRoot%\system32\inetsrv\AppCmd.exe 
%APPCMD% unlock config /section:system.webServer/security/access 

Issue: To ustawienie jest w całej witrynie, więc nawet gdy użytkownicy trafić na pierwszą stronę (słownie https://domain.com, zwraca index.html dla angularjs) ich przeglądarka prosi o certyfikat SSL klienta. (Zdjęcie poniżej)

Jeżeli istnieje sposób, aby zarówno

  1. ograniczyć żądania certyfikatów SSL klienckim tylko kontrolery WebAPI że rozmawiać z 3rd party cloud usługi?

LUB

  1. Pomiń uwierzytelniania klienta SSL dla naszych Czo zasilania kontrolerów WebAPI?

web.config naszym serwerze jest skomplikowany, ale odpowiedni fragment kodu jest poniżej:

<system.webServer> 
    <security> 
    <access sslFlags="SslNegotiateCert" /> 
    </security> 
</system.webServer> 

A zrzut ekranu z klientem trafienia regularny WebAPI końcowy jeszcze próbuje klienta uwierzytelniania SSL (zdarza się w każdej przeglądarki, Chrome , Firefox lub IE) enter image description here

+0

Czy możesz wyświetlić wynik serverWebConfig i aplikację WebConfig? –

+0

witryna serwera.config powyżej, aplikacja webconfig nie istnieje, ponieważ aplikacja kliencka jest przeglądarką – DeepSpace101

+0

Myślę, że to pytanie byłoby lepiej dopasowane do błędu serwera. –

Odpowiedz

10

Niestety, odpowiedź cleftheris, która otrzymała nagrodę, nie działa. Próbuje działać zbyt późno w przetwarzaniu/przetwarzaniu serwera HTTP, aby uzyskać certyfikat klienta, ale this post dał mi kilka pomysłów.

Rozwiązanie oparte na web.config, które wymaga specjalnej obsługi "katalogów" (działa również dla wirtualnych folderów lub tras WebAPI).

Oto pożądane logicznych:

https://www.server.com/acmeapi/ ** => SSL z klientem CERTS

https://www.server.com/ ** => SSL

Oto odpowiednia konfiguracja

<configuration> 
    ... 
    <system.webServer> 
    <!-- This is for the rest of the site --> 
    <security> 
     <access sslFlags="Ssl" /> 
    </security> 
    </system.webServer> 

    <!--This is for the 3rd party API endpoint--> 
    <location path="acmeapi"> 
    <system.webServer> 
     <security> 
     <access sslFlags="SslNegotiateCert"/> 
     </security> 
    </system.webServer> 
    </location> 
... 
</configuration> 

Punkty bonusowe

Powyższe ustawienia uzgodnią odpowiednio uzgadnianie protokołu SSL. Nadal musisz sprawdzić certyfikat SSL klienta w swoim kodzie, jeśli jest taki, jakiego oczekujesz. Że zrobił następująco

kod Kontroler:

[RoutePrefix("acmeapi")] 
[SslClientCertActionFilter] // <== key part! 
public class AcmeProviderController : ApiController 
{ 
    [HttpGet] 
    [Route("{userId}")] 
    public async Task<OutputDto> GetInfo(Guid userId) 
    { 
     // do work ... 
    } 
} 

Rzeczywista atrybut z powyższego wynika, że ​​wykonanie walidacji Client SSL jest poniżej. Może być używany do ozdabiania całego kontrolera lub tylko określonych metod.

public class SslClientCertActionFilterAttribute : ActionFilterAttribute 
{ 
    public List<string> AllowedThumbprints = new List<string>() 
    { 
     // Replace with the thumbprints the 3rd party 
     // server will be presenting. You can make checks 
     // more elaborate but always have thumbprint checking ... 
     "0011223344556677889900112233445566778899", 
     "1122334455667788990011223344556677889900" 
    }; 

    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     var request = actionContext.Request; 

     if (!AuthorizeRequest(request)) 
     { 
      throw new HttpResponseException(HttpStatusCode.Forbidden); 
     } 
    } 

    private bool AuthorizeRequest(HttpRequestMessage request) 
    { 
     if (request==null) 
      throw new ArgumentNullException("request"); 

     var clientCertificate = request.GetClientCertificate(); 

     if (clientCertificate == null || AllowedThumbprints == null || AllowedThumbprints.Count < 1) 
     { 
      return false; 
     } 

     foreach (var thumbprint in AllowedThumbprints) 
     { 
      if (clientCertificate.Thumbprint != null && clientCertificate.Thumbprint.Equals(thumbprint, StringComparison.InvariantCultureIgnoreCase)) 
      { 
       return true; 
      } 
     } 
     return false; 
    } 
} 
+0

To działało jak czar. Nie mogłem znaleźć żadnych innych referencji, które sugerowałyby działające rozwiązanie. Dziękuję – Saravanan

2

Niestety, nie można tego skonfigurować na poziomie kontrolera. Serwer decyduje, którego kontrolera użyć na podstawie treści żądania HTTP (zazwyczaj ścieżki żądania). Protokół SSL chroni zawartość wiadomości HTTP przez jej zaszyfrowanie, a ścieżka żądania jest częścią zaszyfrowanej wiadomości. Kanał SSL musi zostać skonfigurowany przed wysłaniem jakichkolwiek komunikatów HTTP, dlatego konfiguracja kanału SSL (bez względu na to, czy serwer próbuje negocjować certyfikat klienta, na przykład), nie może polegać na zawartości żadnego protokołu HTTP wiadomości.

Więc tutaj są opcje:

  1. rozpędzają druga rola internetowej, który jest skonfigurowany tak, aby nie negocjować certyfikatów klienckich. Będziesz potrzebował do tego drugiej domeny, ponieważ jest to zasadniczo oddzielna usługa. Oznacza to, że https://domain.com wskazuje na wartość inną niż klient-cert, a https://foo.domain.com wskazuje na taką, która wymaga certyfikatów klienta.

  2. Użyj tej samej roli internetowej, ale skonfiguruj drugi port do obsługi IIS do nasłuchu i skonfiguruj go tak, aby nie negocjował certyfikatu klienta. Korzystanie z niestandardowych portów jest jednak uciążliwe, ponieważ jeden z Twoich klientów będzie musiał wykonać https://domain.com:444 (lub inny port oprócz 443).

  3. Wyłącz negocjację certyfikatów klienta we wszystkich obszarach. Może to nie działać w zależności od tego, w jaki sposób twoja usługa uzyskuje dostęp do certyfikatu klienta, ale zazwyczaj po uzyskaniu dostępu do właściwości ClientCertificate w obiekcie System.Web.HttpRequest (lub jej odpowiedniku) będzie negocjować certyfikat na żądanie. Oznacza to, że przezroczyście odrywa istniejącą sesję SSL i tworzy nową, tym razem wyzywając klienta do certyfikatu. Jest to dość nieefektywne, ponieważ konfigurowanie połączenia SSL w pierwszej kolejności wymaga kilku podróży w obie strony, a zrobienie tego dwa razy jest bolesne. Jednak w zależności od dostępnych opcji, wymagań dotyczących wydajności dla żądań korzystających z certyfikatów klienta i tego, czy uzyskasz wiele ponownych połączeń z zachowaniem, opcja ta może mieć sens.

Mam nadzieję, że to pomoże.

+0

Jeśli nie kontrolerów, czy mogę powiedzieć IIS, które ścieżki wymusić (lub pominąć) SSL klienta? Na podstawie Twojego opisu jest to właściwa warstwa do podejmowania decyzji. – DeepSpace101

+0

Niestety, nie możesz. Jedyne, czego możesz użyć, to adres IP i port. Kiedy przychodzi żądanie HTTP, negocjacja SSL musi nastąpić, zanim serwer znajdzie daną ścieżkę. Opcja nr 3 jest najbliższa do podjęcia różnych decyzji na podstawie ścieżki/kontrolera. Ale jest mniej wydajny. – daspek

+0

@ DeepSpace101 Czy możesz spróbować utworzyć pusty folder fizyczny z własnym plikiem web.config, a następnie utworzyć bezpieczną trasę Controllers pod tą ścieżką? Następnie możesz potencjalnie odblokować element dostępu tylko dla tej lokalizacji, używając polecenia Appcmd Unlock Config. – cleftheris

9

Można po prostu zezwolić na zwykły ruch http na poziomie web.config i napisać niestandardową procedurę delegowania w potoku Web Api. Możesz znaleźć handler delegujący klienta certyfikatu here i here. Wtedy możesz sprawić, aby ten moduł obsługi był aktywny "Per-Route", tak jak w tym przykładzie. here:

Taka konfiguracja powinna wyglądać.

public static class WebApiConfig 
{ 
    public static void Register(HttpConfiguration config) 
    { 
     config.Routes.MapHttpRoute(
      name: "Route1", 
      routeTemplate: "api/{controller}/{id}", 
      defaults: new { id = RouteParameter.Optional } 
     ); 

     config.Routes.MapHttpRoute(
      name: "Route2", 
      routeTemplate: "api2/{controller}/{id}", 
      defaults: new { id = RouteParameter.Optional }, 
      constraints: null, 
      handler: new CustomCertificateMessageHandler() // per-route message handler 
     ); 

     config.MessageHandlers.Add(new SomeOtherMessageHandler()); // global message handler 
    } 
} 

Należy pamiętać, że w przypadku trzeba „na trasie” delegowanie teleskopowe trzeba nie umieścić je na globalnej liście obsługi komunikatów.

+0

Należy zauważyć, że jest to realizacja opcji nr 3 z mojej odpowiedzi, więc ma problem z wydajnością ustanowienia dwukrotnie sesji SSL. – daspek

+0

@ DeepSpace101 spójrz na to [codeplex workitem] (http://aspnetwebstack.codeplex.com/workitem/980). Wymieniony kod powinien działać dobrze. – cleftheris

+0

Do przyszłych czytelników: Przeczytaj moją odpowiedź na moje własne pytanie. Potwierdziło się, że działa i wymaga minimalnych zmian kodu. – DeepSpace101

Powiązane problemy