web-dev-qa-db-ja.com

*一部の* WebAPIコントローラーでSSLクライアント証明書を無効にしますか?

将来の読者のために編集:残念ながら、賞金を授与された回答は機能しません。今はそれについて何もできません。しかし、以下の私自身の答えを読んでください(テストを通して)-最小限のコード変更で動作することが確認されました

完全にASP.NETWebAPI2.2に含まれるAzureCloud Service(WebRole)があります(MVCはなく、フロントエンドはAngularです)。一部のコントローラー/ RESTエンドポイントはSSL(クライアント証明書認証/相互認証)を介してサードパーティのクラウドサービスと通信し、残りのコントローラー/エンドポイントは同じくSSL(ただし従来のサーバー認証)を介してHTML5/AngularJSフロントエンドと通信します。 SSL)。 SSL以外のエンドポイントはありません。次のようなクラウドサービスの起動タスクを介してクライアントSSLを有効にしました。

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

問題:この設定はサイト全体であるため、ユーザーが最初のページにアクセスした場合でも(たとえば、 https://domain.com 、angularJSのindex.htmlを返します)、ブラウザーはクライアントSSL証明書を要求します。 (下の画像)

どちらかにする方法があれば

  1. クライアントSSL証明書要求を、サードパーティのクラウドサービスと通信するWebAPIコントローラーのみに制限しますか?

OR

  1. WebAPIコントローラーに電力を供給するフロントエンドのクライアントSSL認証をスキップしますか?

サーバーのweb.configは複雑ですが、関連するスニペットは次のとおりです。

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

また、クライアントが通常のWebAPIエンドポイントに到達し、クライアントのSSL認証を試行しているスクリーンショット(任意のブラウザー、Chrome、Firefox、またはIEで発生) enter image description here

23
DeepSpace101

残念ながら、賞金を授与されたクレフテリスの答えは機能しません。 HTTPサーバーのパイプライン/処理でクライアント証明書を取得するには遅すぎますが、 この投稿 は私にいくつかのアイデアを与えてくれました。

このソリューションは、「ディレクトリ」の特別な処理を要求するweb.configに基づいています(仮想フォルダまたはWebAPIルートでも機能します)。

必要なロジックは次のとおりです。

https://www.server.com/acmeapi/ ** =>クライアント証明書を使用したSSL

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

対応する構成は次のとおりです

<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>

ボーナスポイント

上記はそれに応じてSSLハンドシェイクを設定します。それでも、コード内のクライアントSSL証明書が期待どおりであるかどうかを確認する必要があります。それは次のように行われます

コントローラーコード:

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

SSLクライアント検証を実行する上からの実際の属性は以下のとおりです。コントローラ全体または特定のメソッドのみを装飾するために使用できます。

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;
    }
}
15
DeepSpace101

Web.configレベルでプレーンなhttpトラフィックを許可し、このためにWebApiパイプラインにカスタム委任ハンドラーを作成するだけです。クライアント証明書委任ハンドラー ここ および ここ を見つけることができます。次に、この例のように、このハンドラーを「ルートごと」にアクティブにすることができます ここ

これは、ルート構成がどのようになるかを示しています。

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
    }
}

「ルートごとの」委任ハンドラーが必要な場合は、グローバルメッセージハンドラーリストにnot入れてはならないことに注意してください。

11
cleftheris

残念ながら、これはコントローラーレベルで構成することはできません。サーバーは、HTTPリクエストの内容(通常はリクエストパス)に基づいて、使用するコントローラーを決定します。 SSLは、HTTPメッセージを暗号化することでその内容を保護し、要求パスは暗号化されたメッセージの一部です。 HTTPメッセージを送信する前にSSLチャネルを設定する必要があります。そのため、SSLチャネルの構成(サーバーがクライアント証明書のネゴシエーションを試みるかどうかなど)は、HTTPの内容に依存できません。メッセージ。

だからここにあなたのオプションがあります:

  1. クライアント証明書をネゴシエートしないように構成された2番目のWebロールを起動します。これは本質的に別個のサービスであるため、このために2番目のドメインが必要になります。つまり、 https://domain.com 非クライアント証明書を指し、 https://foo.domain.com を指します。クライアント証明書が必要です。

  2. 同じWebロールを使用しますが、リッスンするためにIISの2番目のポートを設定し、クライアント証明書をネゴシエートしないように構成します。ただし、非標準のポートを使用するのは面倒です。クライアントの1つが実行する必要があるため https://domain.com:444 (または443以外の他のポート)。

  3. 全面的にクライアント証明書ネゴシエーションを無効にします。これは、サービスがクライアント証明書にアクセスする方法によっては機能しない場合がありますが、通常、System.Web.HttpRequestオブジェクト(または同等のもの)のClientCertificateプロパティにアクセスすると、オンデマンドで証明書をネゴシエートします。これは、既存のSSLセッションを透過的に破棄し、新しいセッションをセットアップすることを意味します。今回は、クライアントに証明書を要求します。そもそもSSL接続のセットアップには数回のラウンドトリップが必要であり、2回行うのは面倒なので、これはかなり非効率的です。ただし、使用可能なオプション、クライアント証明書を使用する要求のパフォーマンス要件、およびキープアライブから多くの接続の再利用が得られるかどうかによっては、このオプションが理にかなっている場合があります。

お役に立てれば。

2
daspek