【问题标题】:Way to recognize client at server side在服务器端识别客户端的方法
【发布时间】:2013-04-08 21:29:51
【问题描述】:

我正在用 C++ 创建客户端 - 服务器应用程序。服务器将接受来自多个客户端的请求。每个客户都有在服务器上创建的个人帐户。身份验证后,我知道客户端使用特定 ip 登录到特定帐户。现在我想确定哪些请求考虑特定帐户,例如:

客户端A 登录:

username: user
password: pass123

服务器发现这些数据与id = 3 的帐户匹配。

现在当这个客户端A 发送一些请求时,我希望服务器访问(并最终更改)id = 3 的帐户。到目前为止,我已经提出了这两个想法:

  1. 我有一个std::map,其中键是client ip,值是account id。身份验证后,服务器将客户端的 ip 和它的帐户 ID 存储到这个 map 中,然后当服务器收到来自客户端的请求时,它检查它的 ip 并在 map 中查找它。

  2. 我有一个std::map,其中键是随机生成的键,值是account id。认证后,服务器为这个特定的客户端生成随机密钥,将此密钥发送给客户端,客户端保存以供进一步使用,服务器将此密钥和account id存储在map中。

我想知道这些是否是处理此类问题的好方法。哪个更好,还要考虑安全性(这对我来说非常重要)?还是有更好的方法?

【问题讨论】:

  • 我认为您不希望客户从共享网络中使用它,例如使用 NAT 或类似的公司、家庭集线器,因为您会为多个客户端获得相同的 IP 地址。

标签: c++ architecture client-server server-side


【解决方案1】:

1) 我有一个 std::map,其中键是客户端 IP,值是帐户 ID。身份验证后,服务器将客户端的 ip 和它的帐户 ID 存储到此映射中,然后当服务器收到客户端的请求时,它会检查它的 ip 并在映射中查找它。

IP 本身是不够的:可能有多个不同的客户端从同一个 IP 连接(来自同一台计算机,或者来自 NAT 后面的不同计算机,因此您只能看到 NAT IP)。如果想要基于 IP 的唯一密钥,则需要使用客户端的 IP/端口元组。

2) 我有一个 std::map,其中键是随机生成的键,值是帐户 ID。认证后,服务器为这个特定的客户端生成随机密钥,将此密钥发送给客户端,客户端保存以供进一步使用,服务器将此密钥和帐户ID存储在地图中。

这是非常危险的:是什么禁止客户端发送另一个客户端的“会话 id”而不是他的,从而劫持另一个客户端的会话?这与您可能想要阅读的 HTTP Session Hijacking 完全相同。简而言之:如果可以避免,就不要这样做。


其他可能的解决方案:

    1234563消息。
  • 如果您的服务器使用旧的“每个连接一个线程”模型,那么您不必跳过这些环节。只需将您的会话数据与 Thread Local Storage 变量相关联即可。或者,几乎所有线程库都允许您将参数传递给线程,这些参数可用于将特定数据与您的线程相关联(参见下面的示例)。

  • 1234563 >

很遗憾,只要我们不知道您的服务器使用的模型(线程、分叉、选择、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 对象,所以它有内存泄漏,但我的意思是展示如何将线程与特定的session 对象,我将根据您的具体架构和需求,让您管理对象的生命周期。

【讨论】:

    【解决方案2】:

    我认为使用单个密钥进行以后的身份验证不是很安全。通过中间人攻击,该密钥可能被截获,或者可能被复制和使用,而服务器不会注意到发生了什么。如果安全性对您来说真的很重要,请考虑在网络层之上使用适当的身份验证/加密库以确保安全通信。

    【讨论】:

      【解决方案3】:

      通常,您向客户端发送一些他们将在每次连接到您的服务器时发送的内容(也称为会话变量)。这是唯一的,用于识别传入的请求。

      您概述的第一个想法留下了一个问题,即本地网络上的两台或多台计算机将与远程服务器共享相同的外部 IP。这意味着如果一个客户端使用该 IP 登录,则内部网络上的每个人都将被验证为该用户。

      如果您的连接过程开始于会话变量生成,您可以识别来自同一个客户端的连接,甚至在它经过身份验证之前,实际上不必知道任何 IP 信息。如果您在每个新连接上更改此设置,甚至在每次使用新生成的函数调用之后,您总会遇到中间人攻击问题。

      您需要在会话创建/恢复过程之前对客户端和服务器之间的通信实施一些安全方案。

      【讨论】:

        猜你喜欢
        • 2022-11-02
        • 1970-01-01
        • 2011-09-09
        • 2011-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-14
        • 1970-01-01
        相关资源
        最近更新 更多