Search code examples
c++architectureclient-serverserver-side

Way to recognize client at server side


I'm creating client - server application in C++. Server will accept requests from multiple clients. Each client has individual account created on server. After authentication, I know that client logged in to specific account with specific ip. Now I want to determine which requests consider specific account, for example:

Client A logs in with:

username: user
password: pass123

Server finds that these data matches account with id = 3.

Now when this client A sends some request, I want server to access (and eventually alter) account with id = 3. So far I've come up with these two ideas:

  1. I have a std::map where key is client ip and value is account id. After authentication, server stores client's ip and it's account id into this map and later when server receives request from client, it checks it's ip and looks for it in map.

  2. I have a std::map where key is randomly generated key and value is account id. After authentication, server generates random key for this specific client, sends this key to client, client saves it for further use, server stores this key and account id in map.

I'd like to know if those are good ways to handle this kind of problem. Which one is better, also considering security (it is very important for me)? Or there are better ways?


Solution

  • 1) I have a std::map where key is client ip and value is account id. After authentication, server stores client's ip and it's account id into this map and later when server receives request from client, it checks it's ip and looks for it in map.

    The IP itself is not enough: there can be several different clients connecting from the same IP (either from the very same computer, or from different computers behind a NAT so that you only see the NAT IP). If you want an unique key based on the IP, you need to use the client's IP/port tuple.

    2) I have a std::map where key is randomly generated key and value is account id. After authentication, server generates random key for this specific client, sends this key to client, client saves it for further use, server stores this key and account id in map.

    This is very dangerous: what forbids a client to send another client's "session id" instead of his one, and thus hijack the other client's session? This is the very same issue as HTTP Session Hijacking which you may want to read about. In short: don't do that if you can avoid it.


    Other possible solutions:

    • Still using std::map you could use your socket handle as a key: it is necessarily unique on the server so there is no confusion possible, and it saves you from retrieving the IP/port of the client at every message.

    • If your server uses the good old "one thread per connection" model then you don't have to jump through those hoops. Just associate your session data with a Thread Local Storage variable and you're done. Alternatively, pretty much all threading libraries allow you to pass parameters to your thread, which can be used to associate specific data with your thread (see example below).

    • If your server uses the good old "one process per connection" model (fork) then it is even easier, each process will have its own variables so you don't have anything special to do.


    Unfortunately your question is quite open-ended as long as we don't know the model your server uses (threaded, forked, select, aio, ...?) so it's hard to give you a definite answer.


    If you are using a threaded model, here's (roughly) how I usually do it (C++11 threads, but any other threading library can do it likewise):

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

    WARNING: Obviously this code is flawed, it never destroys the ClientSession objects so it has memory leaks but my point was to show how to associate a thread with a specific session object, I'll leave it to you to manage the lifetime of your objects depending on your exact architecture and needs.