Se il nostro sito Web vede Facebook firmato nell'utente ha l'ID utente 678678678 nel cookie, come facciamo a sapere che questo cookie non è falsificato?

StackOverflow https://stackoverflow.com/questions/4002616

  •  25-09-2019
  •  | 
  •  

Domanda

Penso che se effettiamo chiamate a Facebook usando API REST o Graph, sapremo se è falso perché tornerà dicendo una sessione falsa / auth_token / access_token. Ma cosa succede se stiamo mostrando le nostre informazioni DB, come l'elenco dei prodotti più preferiti dell'utente, allora non facciamo alcuna chiamata a Facebook ma mostriamo i dati dei nostri DB. Come facciamo a sapere che è davvero l'utente, non qualcuno che finge il biscotto?

È stato utile?

Soluzione

Quando leggi un cookie con Facebook contiene un valore chiamato "sig". Con questo valore, gli altri valori dei cookie e la tua app segreti hai il contenuto del cookie e lo convalidano contro il SIG. Se corrispondono, il cookie è valido. Puoi fidarti di questo risultato perché solo tu e Facebook avete accesso al segreto dell'app. Ecco l'esempio di come lo fa SDK PHP di Facebook. Qualsiasi rispettabile SDK di Facebook farà tutto questo per te internamente.

/**
   * Validates a session_version=3 style session object.
   *
   * @param Array $session the session object
   * @return Array the session object if it validates, null otherwise
   */
  protected function validateSessionObject($session) {
    // make sure some essential fields exist
    if (is_array($session) &&
        isset($session['uid']) &&
        isset($session['access_token']) &&
        isset($session['sig'])) {
      // validate the signature
      $session_without_sig = $session;
      unset($session_without_sig['sig']);
      $expected_sig = self::generateSignature(
        $session_without_sig,
        $this->getApiSecret()
      );
      if ($session['sig'] != $expected_sig) {
        self::errorLog('Got invalid session signature in cookie.');
        $session = null;
      }
      // check expiry time
    } else {
      $session = null;
    }
    return $session;
  }

Ecco la stessa cosa in C# (Facebook C# SDK):

 /// <summary>
    /// Validates a session_version=3 style session object.
    /// </summary>
    /// <param name="session">The session to validate.</param>
    protected override void ValidateSessionObject(FacebookSession session)
    {
        if (session == null)
        {
            return;
        }

        var signature = this.GenerateSignature(session);
        if (session.Signature == signature.ToString())
        {
            return;
        }

        session = null;
    }

    /// <summary>
    /// Generates a MD5 signature for the facebook session.
    /// </summary>
    /// <param name="session">The session to generate a signature.</param>
    /// <returns>An MD5 signature.</returns>
    /// <exception cref="System.ArgumentNullException">If the session is null.</exception>
    /// <exception cref="System.InvalidOperationException">If there is a problem generating the hash.</exception>
    protected override string GenerateSignature(FacebookSession session)
    {
        var args = session.Dictionary;
        StringBuilder payload = new StringBuilder();
        var parts = (from a in args
                     orderby a.Key
                     where a.Key != "sig"
                     select string.Format(CultureInfo.InvariantCulture, "{0}={1}", a.Key, a.Value)).ToList();
        parts.ForEach((s) => { payload.Append(s); });
        payload.Append(this.ApiSecret);
        byte[] hash = null;
        using (var md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create())
        {
            if (md5 != null)
            {
                hash = md5.ComputeHash(Encoding.UTF8.GetBytes(payload.ToString()));
            }
        }

        if (hash == null)
        {
            throw new InvalidOperationException("Hash is not valid.");
        }

        StringBuilder signature = new StringBuilder();
        for (int i = 0; i < hash.Length; i++)
        {
            signature.Append(hash[i].ToString("x2", CultureInfo.InvariantCulture));
        }

        return signature.ToString();
    }

Altri suggerimenti

L'unica cosa di cui ti puoi fidare è session_key per la vecchia api di riposo e access_token per API grafico. Una volta ottenuto, passalo a un lato server con la tua richiesta di retrorval di dati. Sulla chiamata a margine del server API di Facebook e ottieni UserID corrente. Una volta ottenuto UserID, puoi archiviarlo in una sessione e usarlo più tardi.

Non archiviarlo in un biscotto. Mettilo in una variabile di sessione, in questo modo hai il controllo

Non mettere un ID utente in un cookie. Il cookie della sessione dovrebbe essere solo un numero casuale che mappa a un record nel database della sessione lato server. Tutti i dati associati a quella sessione sono solo sul lato server archiviato.

In questo modo, per falsificare una sessione, un utente malintenzionato dovrebbe indovinare un numero casuale che effettivamente in uso in quel momento. Dato che ci sono molti numeri e sessioni casuali scadono, è quasi impossibile.

Ci sono alcuni approcci qui.

Inefficiente: ogni volta che si esegue un'operazione autenticata, prendi il cookie FB e usa i dati in esso per effettuare una chiamata API fittizia per vedere che il token di accesso è valido e corrisponde all'utente (cioè grab /me? Fields = ID).

Più efficiente: la prima volta che vedi un cookie FB per un utente, memorizza quel cookie in una sessione sul lato server per l'utente (con un ID di sessione sufficientemente duro per essere passato al client in un cookie).

Un altro approccio e non richiede lo stato di sessione sul lato server: la prima volta che vedi un cookie FB per un utente, HMAC il cookie utilizzando un segreto solo i server e archiviano che risultavano hash in un cookie. Quindi puoi verificare se esiste un hash valido del cookie FB e, in tal caso, ti fidi. Altrimenti, torni alla convalida.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top