0

私はかなり基本的な検索サービスを提供するこの WCF Web サービスを持っています (したがって、サーバー側でデータは変更されません)。このサービスによって提供される情報は機密情報であるため、適切なセキュリティ対策を講じる必要があります。

標準的なことは、サービス バインディングで有効なクライアント証明書を要求することによって、トランスポートとメッセージのセキュリティを提供することです。これらの証明書を作成して発行することで、誰がこのサービスを使用できるかを制御します。しかし (私によると、ここで私が間違っていることを証明してください) クライアントが他のクライアント (私のサービスへのアクセスを許可していない) とクライアント証明書を交換することを妨げるものは何もありません。提供されたクライアント証明書が有効である限り、サービスは接続を受け入れます。ユーザー名/パスワード メカニズムを追加しても、これらの資格情報は交換できるため、これは変更されません。

クライアントには証明書を自分自身で保管する法的義務があると言うかもしれませんが、それでは十分なセキュリティが得られません. IP ベースのアクセス制御に頼らずに、誰がサービスに接続するかを具体的に制御する方法はありますか (クライアントは DHCP 発行の IP アドレスを使用するため、IP をクライアント/証明書に結合する方法はありません)。

私の最初の質問は、クライアント証明書 (GUID など) を証明書ストアにインストールするクライアントによって生成された一意の識別子を何らかの方法で使用し、これをサーバー側に登録して、証明書のサブジェクトとその一意の識別子の組み合わせのみが使用できるようにすることです。サービスにアクセスします。しかし、それを行うメカニズムがあるかどうかはわかりません。

編集: クライアントを制御できないため、クライアント側のコードを変更しても、この問題を簡単に解決することはできません。

ポインタ/回答に本当に感謝しています!

4

1 に答える 1

2

これにはを使用しIDispatchMessageInspectorます。これが最高のパフォーマンスのアプローチであるかどうかはわかりませんが、問題なく機能することが証明されています。

クライアントはメッセージヘッダーを使用して、一意のGUIDを送信します。各GUIDは各クライアント証明書に対応しており、これらはWindowsレジストリに保存されます。GUIDはクライアントによって生成されます(私はUuidCreateSequential()を使用して生成します)

私の解決策をあなたと共有します。これは私のサービスコードです:

    public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
    {
        IList<IIdentity> identities = OperationContext.Current.ServiceSecurityContext.AuthorizationContext.Properties["Identities"] as IList<IIdentity>;
        string clientGuid = request.Headers.GetHeader<string>("Guid", "MyNamespace");
        if (clientGuid == null)
            throw new FaultException("Client GUID not sent!");
        Guid testGuid = Guid.Empty;

        if (!Guid.TryParse(clientGuid, out testGuid))
        {
            throw new FaultException(string.Format("The format of the GUID '{0}' is not valid!", clientGuid));
        }

        IIdentity X509Identity = identities.Where(x => x.AuthenticationType == "X509").SingleOrDefault();
        RegistryKey identityKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\MySoftware\\Identities");
        string storedSubjectName = (string)identityKey.GetValue(clientGuid);

        if (storedSubjectName == null)
        {
            string[] valueNames = identityKey.GetValueNames();
            for (int idx = 0; idx < valueNames.Count(); idx++)
            {
                string testCN = (string)identityKey.GetValue(valueNames[idx]);
                if (testCN == X509Identity.Name)
                {
                    throw new FaultException(string.Format("Certificate '{0}' has already been registered!", X509Identity.Name));
                }
            }
            identityKey.SetValue(clientGuid, X509Identity.Name, RegistryValueKind.String);
        }
        else
        {
            if (storedSubjectName != X509Identity.Name)
                throw new FaultException(string.Format("Certificate '{0}' used by the user registered with the certificate '{0}'", X509Identity.Name, storedSubjectName));
        }

        identityKey.Close();

        return null;
    }

クライアントのコード:
アプリの起動時

        RegistryKey guidKey = Registry.CurrentUser.CreateSubKey("SOFTWARE\\MySoftware\\Settings");
        string clientGuid = (string)guidKey.GetValue("Guid");
        if (clientGuid == null)
        {
            clientGuid = UUID.mGetNewGUID().ToString();
            guidKey.SetValue("Guid", clientGuid, RegistryValueKind.String);
        }

この記事のおかげで一意のUUIDを取得するためのヘルパークラス

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;

static class UUID
{

    // ==========================================================
    // GUID Creation
    // ==========================================================
    private const int RPC_S_OK = 0;
    private const int RPC_S_OUT_OF_MEMORY = 14;
    private const int RPC_S_UUID_NO_ADDRESS = 1739;

    private const int RPC_S_UUID_LOCAL_ONLY = 1824;
    [DllImport("rpcrt4.dll", SetLastError = true)]
    public static extern int UuidCreateSequential(ref Guid guid);

    [DllImport("rpcrt4.dll", SetLastError = true)]
    public static extern int UuidCreate(ref Guid guid);

    /// <summary>
    /// Creates the machine GUID.
    /// </summary>
    public static Guid mGetNewGUID()
    {
        Guid guidMachineGUID = default(Guid);
        int intReturnValue = UuidCreateSequential(ref guidMachineGUID);
        switch (intReturnValue)
        {
            case RPC_S_OK:
                return guidMachineGUID;

            case RPC_S_OUT_OF_MEMORY:
                throw new Exception("UuidCreate returned RPC_S_OUT_OF_MEMORY");
            case RPC_S_UUID_NO_ADDRESS:
                throw new Exception("UuidCreate returned RPC_S_UUID_NO_ADDRESS");
            case RPC_S_UUID_LOCAL_ONLY:
                throw new Exception("UuidCreate returned RPC_S_UUID_LOCAL_ONLY");
            default:
                throw new Exception("UuidCreate returned an unexpected value = " + intReturnValue.ToString());
        }
    }

}

のでBeforeSendRequest_IClientMessageInspector

        var guidHeader = new MessageHeader<string>(Service.ClientGuid);
        var untypedGuid = guidHeader.GetUntypedHeader("Guid", "MyNamespace");
        request.Headers.Add(untypedGuid);
于 2012-05-31T13:14:46.837 に答える