3

C++ でクライアント - サーバー アプリケーションを作成しています。サーバーは、複数のクライアントからの要求を受け入れます。各クライアントには、サーバー上に作成された個別のアカウントがあります。認証後、クライアントが特定の IP を持つ特定のアカウントにログインしたことがわかります。次に、どのリクエストが特定のアカウントを考慮するかを判断したいと思います。たとえば、次のようになります。

クライアントAは次を使用してログインします。

username: user
password: pass123

サーバーは、これらのデータがid= 3 のアカウントと一致することを検出します。

このクライアントがA何らかのリクエストを送信するとき、サーバーに = 3 のアカウントにアクセス (そして最終的には変更) してもらいたいと考えていますid。これまでのところ、次の 2 つのアイデアを思いつきました。

  1. 私はstd::mapwhere key isclient ipと value isを持っていますaccount id。認証後、サーバーはクライアントのIPとアカウントIDをこれmapに保存し、後でサーバーがクライアントからリクエストを受信すると、IPをチェックしてmap.

  2. std::mapキーはランダムに生成されたキーで、値はaccount idです。認証後、サーバーはこの特定のクライアント用のランダム キーを生成し、このキーをクライアントに送信します。クライアントはそれを後で使用するために保存し、サーバーはこのキーをaccount idに保存しますmap

この種の問題を処理する良い方法があるかどうか知りたいです。セキュリティも考慮して、どちらが優れていますか(私にとっては非常に重要です)?それとももっと良い方法がありますか?

4

3 に答える 3

3

1) キーがクライアント IP で、値がアカウント ID である std::map があります。認証後、サーバーはクライアントの IP とそのアカウント ID をこのマップに格納し、後でサーバーがクライアントから要求を受信すると、その IP をチェックしてマップで検索します。

IP 自体は十分ではありません。同じ IP から接続している複数の異なるクライアントが存在する可能性があります (まったく同じコンピューターから、または NAT IP のみが表示されるように NAT の背後にある別のコンピューターから)。IP に基づく一意のキーが必要な場合は、クライアントの IP/ポート タプルを使用する必要があります。

2) キーがランダムに生成されたキーで、値がアカウント ID である std::map があります。認証後、サーバーはこの特定のクライアントのランダム キーを生成し、このキーをクライアントに送信します。クライアントはそれを後で使用するために保存し、サーバーはこのキーとアカウント ID をマップに格納します。

これは非常に危険です。クライアントが別のクライアントの「セッション ID」を自分のセッション ID ではなく送信することを禁止し、他のクライアントのセッションをハイジャックするのはなぜでしょうか? これは、HTTP セッション ハイジャックとまったく同じ問題です。要するに、回避できる場合はそうしないでください。


その他の可能な解決策:

  • 引き続きstd::mapソケットハンドルをキーとして使用できます。これはサーバー上で必ず一意であるため、混乱が生じる可能性はなく、メッセージごとにクライアントの IP/ポートを取得する手間が省けます。

  • サーバーが古き良き「接続ごとに 1 つのスレッド」モデルを使用している場合、それらのフープをジャンプする必要はありません。セッション データを Thread Local Storage 変数に関連付けるだけで完了です。あるいは、ほとんどすべてのスレッド ライブラリで、特定のデータをスレッドに関連付けるために使用できるパラメーターをスレッドに渡すことができます (以下の例を参照)。

  • サーバーが古き良き「接続ごとに1つのプロセス」モデル(フォーク)を使用している場合は、さらに簡単です。各プロセスには独自の変数があるため、特別なことは何もありません。


残念ながら、あなたのサーバーが使用するモデル (スレッド化、分岐、選択、aio など) がわからない限り、あなたの質問は非常に自由なので、明確な答えを出すのは困難です。


スレッド化されたモデルを使用している場合、これが(大まかに)私が通常行う方法です(C ++ 11スレッドですが、他のスレッド化ライブラリでも同様に実行できます):

class ClientSession {
public:
    ClientSession(int sock)
        : m_sock(sock),
          m_thread(&ClientSession::threadFunction, this)
    {
    }
private:
    int m_sock;
    WhateverType m_someSessionVariable;

    std::thread m_thread; // the thread object should be declared last so that
    // it is initialised last in the constructor, thus avoiding race conditions
    // during the initialisation (you really don't want the thread to access
    // your member variables if they are not yet initialised!)

    static void threadFunction(ClientSession* object) {
      object->threadMethod();
    }

    void threadMethod() {
      // handle your connection
      // the current ClientSession object (this) *is* your session
      // put whatever you want in it, eg. m_someSessionVariable
    }
};

//...

int sock_client = TEMP_FAILURE_RETRY(accept(sock_server, 0, 0));
if (sock_client >= 0)
  new ClientSession(sock_client);

警告:明らかに、このコードには欠陥があります。ClientSession オブジェクトを破棄することはないため、メモリ リークが発生しますが、私のポイントは、スレッドを特定のセッション オブジェクトに関連付ける方法を示すことでした。オブジェクトの有効期間を管理するのはあなたに任せます。正確なアーキテクチャとニーズに応じて。

于 2013-04-16T13:26:09.023 に答える
2

後の認証に単一のキーを使用することは、あまり安全ではないと思います。中間者攻撃により、そのキーが傍受されるか、サーバーが何が起こったかを気付かずにコピーされて使用される可能性があります。セキュリティが本当に重要な場合は、ネットワーク層の上に適切な認証/暗号化ライブラリを使用して、安全な通信を確保することを検討してください。

于 2013-04-16T13:11:00.277 に答える
2

通常、サーバーへの接続ごとにクライアントが送信するもの (別名セッション変数) をクライアントに送信します。これは一意であり、着信要求を識別するために使用されます。

最初に説明したアイデアには、ローカル ネットワーク上の 2 つ以上のコンピューターがリモート サーバーに対して同じ外部 IP を共有するという問題が残ります。これは、1 つのクライアントがその IP でログインすると、内部ネットワーク上の全員がそのユーザーとして認証されることを意味します。

接続プロセスの最初がセッション変数の生成から始まる場合、実際に IP 情報を知らなくても、認​​証される前であっても、同じクライアントからの接続を識別できます。新しい接続ごとにこれを変更したり、新しく生成された関数を呼び出した後でも、中間者攻撃の問題が常に発生します。

セッションの作成/再開プロセスの前に、クライアントとサーバー間の通信に何らかのセキュリティ スキームを実装する必要があります。

于 2013-04-16T13:17:03.700 に答える