Introducción
No sé si existe o alguna vez habrá una manera de identificar máquinas de manera única utilizando solo un navegador. Las razones principales son:
- Deberá guardar datos en la computadora del usuario. El usuario puede eliminar estos datos en cualquier momento. A menos que tenga una manera de recrear esta información que es única para cada máquina, entonces está atascado.
- Validación. Debe protegerse contra la suplantación de identidad, el secuestro de sesión, etc.
Incluso si hay formas de rastrear una computadora sin usar cookies, siempre habrá una forma de evitarla y un software que lo hará automáticamente. Si realmente necesita rastrear algo basado en una computadora, tendrá que escribir una aplicación nativa (Apple Store / Android Store / Windows Program / etc).
Es posible que no pueda darle una respuesta a la pregunta que hizo, pero puedo mostrarle cómo implementar el seguimiento de la sesión. Con el seguimiento de sesión, intenta realizar un seguimiento de la sesión de navegación en lugar de que la computadora visite su sitio. Al rastrear la sesión, el esquema de su base de datos se verá así:
sesssion:
sessionID: string
// Global session data goes here
computers: [{
BrowserID: string
ComputerID: string
FingerprintID: string
userID: string
authToken: string
ipAddresses: ["203.525....", "203.525...", ...]
// Computer session data goes here
}, ...]
Ventajas del seguimiento basado en sesión:
- Para los usuarios registrados, siempre puede generar el mismo ID de sesión de los usuarios
username
/ password
/ email
.
- Todavía puede rastrear usuarios invitados usando
sessionID
.
- Incluso si varias personas usan la misma computadora (es decir, cibercafé), puede rastrearlas por separado si inician sesión.
Desventajas del seguimiento basado en sesión:
- Las sesiones se basan en el navegador y no en la computadora. Si un usuario usa 2 navegadores diferentes, dará como resultado 2 sesiones diferentes. Si esto es un problema, puede dejar de leer aquí.
- Las sesiones caducan si el usuario no ha iniciado sesión. Si un usuario no ha iniciado sesión, utilizará una sesión de invitado que se invalidará si el usuario elimina las cookies y la memoria caché del navegador.
Implementación
Hay muchas formas de implementar esto. No creo que pueda cubrirlos a todos, solo enumeraré mi favorito, lo que haría que esta sea una respuesta obstinada . Tenlo en cuenta.
Lo esencial
Seguiré la sesión usando lo que se conoce como una cookie para siempre. Estos son datos que se recrearán automáticamente incluso si el usuario elimina sus cookies o actualiza su navegador. Sin embargo, no sobrevivirá al usuario eliminando sus cookies y su caché de navegación.
Para implementar esto, utilizaré el mecanismo de almacenamiento en caché de los navegadores ( RFC ), la API de WebStorage ( MDN ) y las cookies del navegador ( RFC , Google Analytics ).
Legal
Para utilizar identificadores de seguimiento, debe agregarlos tanto a su política de privacidad como a sus términos de uso, preferiblemente bajo el subtítulo Seguimiento . Utilizaremos las siguientes teclas en ambos document.cookie
y window.localStorage
:
- _ga : datos de Google Analytics
- __utma : cookie de seguimiento de Google Analytics
- sid : SessionID
Asegúrese de incluir enlaces a su Política de privacidad y términos de uso en todas las páginas que usan seguimiento
¿Dónde guardo los datos de mi sesión?
Puede almacenar los datos de su sesión en la base de datos de su sitio web o en la computadora del usuario. Como normalmente trabajo en sitios más pequeños (deje que más de 10 mil conexiones continuas) que usen aplicaciones de terceros (Google Analytics / Clicky / etc.), es mejor para mí almacenar datos en la computadora del cliente. Esto tiene las siguientes ventajas:
- Sin búsqueda de base de datos / gastos generales / carga / latencia / espacio / etc.
- El usuario puede eliminar sus datos cuando lo desee sin la necesidad de escribirme correos electrónicos molestos.
y desventajas:
- Los datos tienen que estar encriptados / desencriptados y firmados / verificados, lo que crea una sobrecarga de la CPU en el cliente (no tan mal) y el servidor (¡bah!).
- Los datos se eliminan cuando el usuario elimina sus cookies y caché. (esto es lo que realmente quiero)
- Los datos no están disponibles para el análisis cuando los usuarios se desconectan. (análisis solo para usuarios que actualmente navegan)
UUIDS
- BrowserID : Identificación única generada a partir de la cadena del agente de usuario de los navegadores.
Browser|BrowserVersion|OS|OSVersion|Processor|MozzilaMajorVersion|GeckoMajorVersion
- ComputerID : generado a partir de la dirección IP de los usuarios y la clave de sesión HTTPS.
getISP(requestIP)|getHTTPSClientKey()
- FingerPrintID : huella digital basada en JavaScript basada en una huella digital modificada.js .
FingerPrint.get()
- SessionID : clave aleatoria generada cuando el usuario visita el sitio por primera vez.
BrowserID|ComputerID|randombytes(256)
- GoogleID : generado a partir de
__utma
cookies.getCookie(__utma).uniqueid
Mecanismo
El otro día estaba viendo el show de Wendy Williams con mi novia y estaba completamente horrorizado cuando el anfitrión le aconsejó a sus espectadores que eliminaran el historial de su navegador al menos una vez al mes. Eliminar el historial del navegador normalmente tiene los siguientes efectos:
- Elimina el historial de los sitios web visitados.
- Elimina cookies y
window.localStorage
(aww man).
La mayoría de los navegadores modernos hacen que esta opción esté fácilmente disponible, pero no temas a tus amigos. Porque hay una solución. El navegador tiene un mecanismo de almacenamiento en caché para almacenar secuencias de comandos / imágenes y otras cosas. Por lo general, incluso si eliminamos nuestro historial, este caché del navegador aún permanece. Todo lo que necesitamos es una forma de almacenar nuestros datos aquí. Hay 2 métodos para hacer esto. La mejor es usar una imagen SVG y almacenar nuestros datos dentro de sus etiquetas. De esta manera, los datos aún se pueden extraer incluso si JavaScript está deshabilitado con flash. Sin embargo, dado que es un poco complicado, demostraré el otro enfoque que usa JSONP ( Wikipedia )
example.com/assets/js/tracking.js (realmente tracking.php)
var now = new Date();
var window.__sid = "SessionID"; // Server generated
setCookie("sid", window.__sid, now.setFullYear(now.getFullYear() + 1, now.getMonth(), now.getDate() - 1));
if( "localStorage" in window ) {
window.localStorage.setItem("sid", window.__sid);
}
Ahora podemos obtener nuestra clave de sesión en cualquier momento:
window.__sid || window.localStorage.getItem("sid") || getCookie("sid") || ""
¿Cómo hago que tracking.js se pegue en el navegador?
Podemos lograr esto usando los encabezados HTTP Cache-Control , Last-Modified y ETag . Podemos usar el SessionID
valor as para el encabezado etag:
setHeaders({
"ETag": SessionID,
"Last-Modified": new Date(0).toUTCString(),
"Cache-Control": "private, max-age=31536000, s-max-age=31536000, must-revalidate"
})
Last-Modified
El encabezado le dice al navegador que este archivo básicamente nunca se modifica. Cache-Control
le dice a los proxies y gateways que no almacenen en caché el documento, pero le dice al navegador que lo guarde en caché durante 1 año.
La próxima vez que el navegador solicite el documento, lo enviará If-Modified-Since
y los If-None-Match
encabezados. Podemos usar estos para devolver una 304 Not Modified
respuesta.
example.com/assets/js/tracking.php
$sid = getHeader("If-None-Match") ?: getHeader("if-none-match") ?: getHeader("IF-NONE-MATCH") ?: "";
$ifModifiedSince = hasHeader("If-Modified-Since") ?: hasHeader("if-modified-since") ?: hasHeader("IF-MODIFIED-SINCE");
if( validateSession($sid) ) {
if( sessionExists($sid) ) {
continueSession($sid);
send304();
} else {
startSession($sid);
send304();
}
} else if( $ifModifiedSince ) {
send304();
} else {
startSession();
send200();
}
Ahora, cada vez que el navegador solicite, tracking.js
nuestro servidor responderá con un 304 Not Modified
resultado y forzará una ejecución de la copia local de tracking.js
.
Aún no lo entiendo. Explícamelo
Supongamos que el usuario borra su historial de navegación y actualiza la página. Lo único que queda en la computadora del usuario es una copia de la tracking.js
memoria caché del navegador. Cuando el navegador lo solicita tracking.js
, recibe una 304 Not Modified
respuesta que hace que ejecute la primera versión tracking.js
recibida. tracking.js
ejecuta y restaura lo SessionID
que fue eliminado.
Validación
Supongamos que Haxor X roba las cookies de nuestros clientes mientras todavía están conectados. ¿Cómo los protegemos? Criptografía y huellas digitales del navegador al rescate. Recuerde que nuestra definición original SessionID
era:
BrowserID|ComputerID|randomBytes(256)
Podemos cambiar esto a:
Timestamp|BrowserID|ComputerID|encrypt(randomBytes(256), hk)|sign(Timestamp|BrowserID|ComputerID|randomBytes(256), hk)
Donde hk = sign(Timestamp|BrowserID|ComputerID, serverKey)
.
Ahora podemos validar nuestro SessionID
uso del siguiente algoritmo:
if( getTimestamp($sid) is older than 1 year ) return false;
if( getBrowserID($sid) !== createBrowserID($_Request, $_Server) ) return false;
if( getComputerID($sid) !== createComputerID($_Request, $_Server) return false;
$hk = sign(getTimestamp($sid) + getBrowserID($sid) + getComputerID($sid), $SERVER["key"]);
if( !verify(getTimestamp($sid) + getBrowserID($sid) + getComputerID($sid) + decrypt(getRandomBytes($sid), hk), getSignature($sid), $hk) ) return false;
return true;
Ahora para que el ataque de Haxor funcione, deben:
- Tener lo mismo
ComputerID
. Eso significa que tienen que tener el mismo proveedor de ISP que víctima (Tricky). Esto le dará a nuestra víctima la oportunidad de emprender acciones legales en su propio país. Haxor también debe obtener la clave de sesión HTTPS de la víctima (Hard).
- Tener lo mismo
BrowserID
. Cualquiera puede falsificar la cadena de agente de usuario (molesto).
- Ser capaz de crear su propio falso
SessionID
(Muy difícil). Los ataques de volumen no funcionarán porque usamos una marca de tiempo para generar la clave de cifrado / firma, así que básicamente es como generar una nueva clave para cada sesión. Además de eso, ciframos bytes aleatorios, por lo que un simple ataque de diccionario también está fuera de discusión.
Podemos mejorar la validación mediante el reenvío GoogleID
y FingerprintID
(a través de ajax o campos ocultos) y la comparación con esos.
if( GoogleID != getStoredGoodleID($sid) ) return false;
if( byte_difference(FingerPrintID, getStoredFingerprint($sid) > 10%) return false;