Skip to main content
Cómo detecta cside el uso compartido de cuentas
Language

Detectar el uso compartido de cuentas

Prevenga el uso compartido de cuentas no autorizado y aplique los límites de licencias. Etiquete la telemetría con la cuenta que ha iniciado sesión, luego lea la regla account_shared_detected desde su backend para ver cuándo una cuenta se usa desde más dispositivos de los que permite.

Un conjunto de credenciales, usado por más personas de las que pagaron por él: la suscripción compartida, los dos asientos que en silencio sostienen a ocho personas, la cuenta revendida. Nada de esto se ve inusual en sus propios logs, porque cada caso es un inicio de sesión exitoso con la contraseña correcta.

Device Intelligence agrega lo que a los logs les falta: una identidad de dispositivo en cada sesión. Etiquete su telemetría con la cuenta que está iniciando sesión, y el veredicto llega a su backend durante el propio inicio de sesión. En el dashboard también obtiene la forma del problema: qué dispositivos, desde dónde inician sesión, si las sesiones se solaparon, y si la cuenta saltó de región más rápido de lo que cualquiera puede viajar.

El mismo mecanismo aplica los límites de licencias. Fije el límite al número de asientos vendidos, y la regla le indica cuándo un cliente los ha superado en silencio.

Cómo detecta cside el uso compartido de cuentas

Etiquete su telemetría con el id de cuenta de quien inició sesión. cside cuenta los dispositivos distintos vistos para esa cuenta dentro de una ventana móvil, y cuando ese conteo supera el límite de su sitio, la regla se dispara.

Esto son dos mitades, y se ejecutan en lugares distintos:

  • Su navegador llama a sendClientTelemetry({ userId }) y recibe un token de vuelta.
  • Su servidor envía ese token a cside y lee el veredicto.

Mantenga la comprobación del veredicto en su servidor. Su clave API de Device Intelligence no puede acercarse al código del navegador, y un veredicto que el navegador pudiera leer sería un veredicto que el navegador podría falsificar.

Lo que necesitará

  • Device Intelligence activado en el dominio
  • El script instalado y sendClientTelemetry siendo llamado: consulte Configurar Device Intelligence
  • Una clave API de Device Intelligence para backend: consulte Events API
  • La regla activada para su dominio. La activación autoservicio todavía no existe, así que pida a cside que la active

La pantalla de configuración de Device Intelligence con la pestaña npm seleccionada, mostrando el comando de instalación y la llamada a initDeviceIntelligence La pestaña de configuración de Device Intelligence mostrando una clave API de backend activa, con un recordatorio de mantener las claves solo en el servidor

En el navegador: enviar el id de cuenta

Etiquetar la telemetría con userId

Llame a sendClientTelemetry una vez al cargar la página sin userId, para que la recopilación empiece temprano, luego llámelo de nuevo con userId adjunto en cuanto sepa quién inició sesión. Pase el token que devuelve directamente a su propio backend; el navegador no hace nada más con él.

Puede llamarlo de nuevo cuando quiera una lectura actualizada, no solo al iniciar sesión. Cada llamada produce una evaluación nueva, así que en sesiones de larga duración una llamada periódica mantiene el conteo de dispositivos al día en lugar de dejarlo con un veredicto de la última vez que la persona inició sesión.

import { initDeviceIntelligence } from "@cside.dev/device-intelligence";

const { sendClientTelemetry } = await initDeviceIntelligence({
  teamID: "[your-team-id]",
});

// Call this at sign-in, and anywhere else you want to refresh the reading:
// long-running sessions benefit from a periodic call.
async function onSignIn(userId) {
  const { token, errors } = await sendClientTelemetry({ userId });

  if (errors) {
    console.error(errors);
    return;
  }

  // Your own backend route, not a cside endpoint. It does the actual
  // verdict check server-side; see the next section.
  await fetch("/api/account-sharing-check", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ token }),
  });
}
La clave tiene que coincidir con la que lee la regla

userId es la opción predeterminada, no un requisito. Lo que importa es que la clave que envía y la clave que lee la regla sean la misma, escrita de forma idéntica. Si envía accountId o user_id contra una regla todavía configurada en userId, no es un error: el valor se lee como permanentemente ausente, y verá outcome: 3 en cada evaluación en lugar de outcome: 1 o outcome: 2. Si ya envía una clave distinta, cside puede apuntar la regla a esa en su lugar. Consulte ¿Ya envía un id distinto?.

Sin PII ni secretos en los valores de id externos

userId y cada otro id externo se almacenan y se incluyen en las exportaciones. Envíe un identificador de cuenta opaco, no una dirección de correo electrónico, un nombre, ni nada más que no quiera persistir.

¿Ya envía un id distinto?

No tiene que cambiar a userId. Lo que importa es que la regla lea la misma clave que usted envía, y cside puede reasignar la regla, así que nada cambia en su cliente.

La clave sí tiene que ser una de userId, accountId, organizationId o teamId. Esas son las únicas cuatro sobre las que la regla puede indexar, porque indexar necesita un campo con un conjunto acotado de valores. email, orderId y cualquier otra cosa se pueden seguir enviando y almacenando, simplemente no pueden ser la clave, así que envíe una de las cuatro junto con lo que ya use.

En su servidor: leer el veredicto

Esta es la ruta /api/account-sharing-check del paso anterior. Intercambia el token por la evaluación completa y decide qué hacer, todo en el servidor, con su clave API de Device Intelligence.

curl https://api.cside.com/token/v2/verify \
  --request POST \
  --header "Authorization: Bearer $CSIDE_FINGERPRINT_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"token":"'"$CSIDE_FINGERPRINT_SESSION_TOKEN"'"}'

El endpoint devuelve 202 Accepted con { "status": "pending" } mientras la evaluación sigue en curso, luego 200 OK cuando termina. Consulte hasta unas 3 veces, con unos 3 segundos de separación como límite superior seguro: el motor de reglas se ejecuta de forma síncrona como parte de almacenar el resultado, así que el veredicto normalmente está listo en su primera consulta. Y está listo por completo: rule_evaluations_v2 se completa en el momento exacto en que status pasa a "completed", nunca después, así que una vez que ve "completed", una entrada ausente está genuinamente ausente, no en camino todavía. Si se le agotan los reintentos y sigue pendiente, trátelo como ningún veredicto para este inicio de sesión en lugar de bloquear a la persona: el siguiente inicio de sesión produce una evaluación nueva.

Aquí está todo conectado: intercambiar el token, comprobar que pertenece a la cuenta que realmente ha iniciado sesión, y luego encontrar la entrada de uso compartido de cuentas y actuar según ella. Compruebe tanto outcome como evaluated_as en el propio código, no solo el slug de la regla: otras reglas, incluido el experimento shadow de alguien más, pueden viajar en el mismo array.

// apps/api-style illustrative route handler. Adjust to your own framework.
// The wire sends these as integers. Naming them keeps the check readable;
// the full set of values is in the tables below.
const OUTCOME_TRUE = 1;
const LIFECYCLE_ACTIVE = 3;

app.post("/api/account-sharing-check", async (req, res) => {
  const { token } = req.body;
  const evaluation = await verifyToken(token);

  // Trust your own session, never the browser. The userId on the telemetry is
  // whatever the page chose to send, so bind it to the signed-in account
  // before you act on anything.
  if (evaluation.fingerprint?.external_ids?.userId !== req.session.userId) {
    return res.sendStatus(409);
  }

  const sharing = findAccountSharing(evaluation);

  if (sharing !== undefined) {
    // This account is over your site's device limit right now.
    // Step up auth, cap sessions, flag for review: see "Decide what to do" below.
  }

  res.sendStatus(204);
});

async function verifyToken(token, attempt = 0) {
  const response = await fetch("https://api.cside.com/token/v2/verify", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.CSIDE_FINGERPRINT_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ token }),
  });

  if (!response.ok) {
    // An expired key, a bad token, a rate limit. Never fall through: a failed
    // request is an outage, not a verdict of "nobody is sharing this account".
    throw new Error(`Device Intelligence verify failed: ${response.status}`);
  }

  const data = await response.json();

  if (data.status === "pending" && attempt < 3) {
    await new Promise((resolve) => setTimeout(resolve, 3000));
    return verifyToken(token, attempt + 1);
  }

  if (data.status === "failed") {
    // Evaluation itself failed. Don't treat this the same as "no entry":
    // a failure isn't the healthy case.
    throw new Error("Device Intelligence evaluation failed");
  }

  return data;
}

function findAccountSharing(data) {
  // An absent entry means the rule wasn't evaluated, not that it
  // evaluated to false.
  return data.rule_evaluations_v2?.values?.find(
    (v) =>
      v.evaluation.rule_slug === "account_shared_detected" &&
      v.evaluation.outcome === OUTCOME_TRUE &&
      // Shadow rules ride in the same array. Acting on one would enforce
      // an experiment that isn't live for your site.
      v.evaluation.evaluated_as === LIFECYCLE_ACTIVE,
  );
}
Vincule el token a su propia sesión

El userId de un token es lo que sea que haya enviado el navegador. Alguien puede omitirlo, o poner ahí una cuenta distinta, así que tratarlo como prueba de identidad le permite empujar observaciones hacia una cuenta que no es la suya, o mantener su propia cuenta con apariencia limpia. Compare fingerprint.external_ids.userId con la cuenta de su sesión de servidor y rechace todo lo que no coincida. cside le dice lo que afirmó el navegador; solo usted sabe quién ha iniciado sesión realmente.

outcome y evaluated_as son enteros, no cadenas. Cada regla evaluada y publicada reporta una entrada en cada evento, en el resultado que sea: 1, 2 y 3 son estados estables que verá de forma continua, no picos aislados. 4 es la excepción, y significa algo más específico de lo que parece: la regla necesita una medición que existe pero todavía no se ha calentado. Eso es distinto de un status de "pending", que significa que la evaluación en sí no ha terminado y es para lo que sirve el bucle de consultas anterior. Consultar el mismo token con más fuerza no convertirá un 4 en un veredicto; se resuelve en evaluaciones posteriores a medida que se acumulan datos para esa cuenta.

Mismo enum que la referencia de rule_evaluations_v2; esto es lo que significa cada valor específicamente para esta regla:

outcomeSignificadoCuándo lo observará
0Sin especificarNo se produce
1TrueDe forma continua, mientras la cuenta esté por encima de su límite
2FalseDe forma continua, el estado normal para una cuenta saludable que no está por encima del límite
3Ausente (estructural)De forma continua, si la regla no puede leer userId de su telemetría, por ejemplo una clave incorrecta
4Pendiente (transitorio)Cuando la medición de la regla aún no está caliente. No es lo mismo que status: "pending", y volver a consultar este token no lo resolverá
evaluated_asSignificado
1Draft
2Shadow: el experimento de alguien, puede aparecer en cualquier momento, no actúe sobre él
3Active: la regla que su sitio activó
4Archived

Una nota sobre transition, útil si prefiere actuar una vez en lugar de en cada solicitud: una vez que ya comprobó outcome: 1, transition: 3 significa que la cuenta acaba de cruzar el umbral en esta evaluación, y transition: 2 significa que ya estaba por encima desde una anterior.

La cuenta que disparó la regla está en fingerprint.external_ids en la misma respuesta, no en evaluation.key. key es un hash de aquello sobre lo que indexó la regla y nunca lleva el id de cuenta en texto plano. Note también que key.key_hash es una cadena JSON, no un número, y que este cuerpo de respuesta mezcla dos formas de timestamp: created_at y updated_at a nivel superior son números en milisegundos, mientras que evaluated_at y event_time dentro de la evaluación son objetos { seconds, nanos }.

Ejemplo de respuesta elidido, capturado de una evaluación real, con el resto de campos omitidos. Note la segunda entrada: otras reglas realmente viajan en el mismo array, por lo que el código anterior compara slug, outcome y evaluated_as juntos, no solo el slug.

{
  "status": "completed",
  "created_at": 1789731723590,
  "updated_at": 1789731723590,
  "fingerprint": {
    "device_id": "dev_3f8a21c9",
    "external_ids": {
      "userId": "acct_9f2a1c"
    }
  },
  "rule_evaluations_v2": {
    "values": [
      {
        "evaluation": {
          "rule_slug": "account_shared_detected",
          "outcome": 1,
          "evaluated_as": 3,
          "transition": 3,
          "key": { "key_hash": "16386830586737169487" },
          "event_time": { "seconds": 1789731723, "nanos": 590000000 },
          "evaluated_at": { "seconds": 1789731723, "nanos": 605000000 }
        }
      },
      {
        "evaluation": {
          "rule_slug": "multi_account_detected",
          "outcome": 2,
          "evaluated_as": 3,
          "transition": 2,
          "key": { "key_hash": "261153104274697293" },
          "event_time": { "seconds": 1789731723, "nanos": 590000000 },
          "evaluated_at": { "seconds": 1789731723, "nanos": 605000000 }
        }
      }
    ]
  }
}

No se muestra arriba: algunos objetos anidados en la respuesta completa usan nombres de campo camelCase en lugar de snake_case, allí donde comparten un formato de wire con el diseñador de reglas del dashboard (dentro de explanation, y key.components[].dimension). Si imprime su propia respuesta completa y ve una mezcla de ambos, es lo esperado, no un error en su integración.

Consulte la referencia completa del campo rule_evaluations_v2 para el resto de campos.

Decida qué hacer al respecto

cside reporta, no bloquea. Qué hace con una regla que se dispara depende de usted: reforzar la autenticación, limitar las sesiones concurrentes de la cuenta, o marcarla para revisión manual.

Confirme que funciona

Primero, confirme que el id realmente está llegando: intercambie un token y compruebe que fingerprint.external_ids contiene el valor que espera bajo userId.

Luego compruebe la regla en sí, antes de que nadie comparta nada. Como cada regla evaluada y publicada reporta en cada evento, no necesita provocar un caso de uso compartido para ver si el pipeline funciona:

  • outcome: 2 en la entrada de account_shared_detected es lo que se ve en una integración sana y bien conectada: la regla se está ejecutando, lee la clave correcta, y nadie está por encima del límite. Esta es su confirmación negativa, y la verá de forma continua, no solo una vez.
  • outcome: 3 significa que está enviando la clave incorrecta. Corríjalo ahora, antes de seguir probando; consulte ¿Ya envía un id distinto?.
  • Ninguna entrada para account_shared_detected significa que la regla todavía no está activada para este dominio, que es una regla de solo medición que no publica, o que la evaluación no se ejecutó para este evento. Pregunte a cside.
ventana móvil de 2 horas0h1h2h3hDispositivo 1Dispositivo 2Dispositivo 3supera el límite de su sitioDispositivo 4Inicio de sesión posterior, fuera de la ventanano cuenta

Vea cómo cambia a true

Una vez confirmada la base sana de outcome: 2 de arriba, dispare la regla a propósito para ver el flujo completo, incluida la transición:

  1. Inicie sesión en una cuenta de prueba desde tres navegadores o perfiles de dispositivo distintos, dentro de un lapso de 2 horas. Esos son los valores predeterminados enviados; si el límite o la ventana de su sitio se han reconfigurado, use los suyos
  2. Intercambie un token de sesión después de cada inicio de sesión y observe account_shared_detected: se mantiene en outcome: 2 para los dos primeros, luego cambia a outcome: 1 (con transition: 3) en cuanto el tercer dispositivo pone a la cuenta por encima del límite de su sitio
  3. Confirme evaluated_as: 3 junto a él, para saber que está leyendo la regla activa y no un experimento shadow

Las ventanas de navegación privada en la misma máquina física pueden no leerse como dispositivos distintos, así que use navegadores o perfiles de dispositivo separados en lugar de varias ventanas privadas en un mismo navegador.

curl https://api.cside.com/token/v2/verify \
  --request POST \
  --header "Authorization: Bearer $CSIDE_FINGERPRINT_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"token":"'"$CSIDE_FINGERPRINT_SESSION_TOKEN"'"}'

# rule_evaluations_v2 carries the rule outcomes, account_shared_detected among them.
# If "status" is "pending", wait a few seconds and retry, up to about 3 times.

Una vez confirmado en la API, abra esa sesión en el dashboard de cside: su página de detalle de sesión tiene una pestaña de Identity graph que muestra lo mismo de forma visual, una cuenta que se ramifica en varios nodos de dispositivo.

Una sesión agrupada por el id externo userId, cubriendo cuatro sesiones a través de cuatro dispositivos El gráfico de identidad de una cuenta, ramificándose a través de cuatro navegadores hacia cuatro dispositivos y cuatro redes, con la regla de cuenta compartida disparada

Si su equipo tiene acceso a la página de caso de uso de cuentas compartidas en el dashboard, su tile de cobertura es la forma más directa de detectar una clave incorrecta: mide qué fracción de sus eventos realmente lleva el id que lee la regla. Esa página todavía está en acceso anticipado, así que puede que no esté activada para usted.

La pantalla de Account sharing listando dos cuentas detectadas, con una seleccionada mostrando sus dispositivos agrupados por navegador

Siguientes pasos

Solución de problemas

Casi toda integración que no ve nada tiene la misma causa: el id externo que envía no es el que lee la regla. userId es la opción predeterminada, no un requisito, así que cside puede reasignar la regla a cualquiera de los otros campos indexables. Su telemetría y la regla solo tienen que coincidir en uno.

  1. Compruebe qué llegó. Intercambie un token y mire fingerprint.external_ids. Si su id de cuenta no está ahí, sendClientTelemetry se ejecutó sin él, o se ejecutó antes de que supiera quién estaba iniciando sesión.
  2. Compruebe que la regla pudo leerlo. Un outcome de 3 en la entrada de account_shared_detected significa que la regla se ejecutó y no pudo encontrar su clave.
  3. Compruebe que el valor es estable. Un id de sesión o un token rotativo hace que cada inicio de sesión parezca una cuenta nueva, así que nada se acumula nunca contra una sola.
  4. Reasigne en lugar de reescribir. Pida a cside que apunte la regla a su clave en su lugar. Consulte ¿Ya envía un id distinto?.
Was this page helpful?