Cadena de identificación secuencial que no se puede realizar ingeniería inversa (el problema del "número de factura")

53

Digamos que opero un sitio web donde puedes crear imágenes de gatos. Le doy a cada imagen de gato un identificador único para que pueda compartirse en las redes sociales con http://catpictures.com/base62Identifier .

Podría dar a los cuadros de gato identificadores secuenciales como 1,2,3, etc., pero luego sería posible descubrir fácilmente cuántas fotos de gato nuevas crean los usuarios por día (por el identificador más grande que devuelve HTTP 200 cada uno). día). Esto me expone a la estrategia común de pedir un producto a sus competidores una vez al mes y anotar el número de factura. Las cifras de tráfico del sitio web están bien correlacionadas con los ingresos del negocio, por lo que obviamente quiero mantener esta información en secreto.

Lo que estoy considerando probar:

Esto suena como un trabajo para un algoritmo de hash, ¿verdad? El problema es que al observar un hash es bastante fácil saber qué algoritmo lo creó (md5, crc32, etc.). Alguien con una mesa de arco iris haría un breve trabajo de esa idea. Podría agregar el identificador [hash ("sal" +1), hash ("sal" +2), ...], pero luego tendría que preocuparme por la seguridad asociada con la sal. Y control de colisiones.

Otra idea que tuve fue generar una cadena aleatoria de caracteres y usarla como la clave principal de la imagen del gato en la base de datos (o simplemente podría marcar los primeros n bits de los datos de la imagen del gato). De esta manera solo tendría que verificar las colisiones.

¿Existe una forma estándar y de mejores prácticas para evitar exponer sus volúmenes de tráfico a través de las URL de identificadores únicos?

Editar: estoy buscando específicamente una solución que sea una buena combinación de seguridad y conveniencia como clave principal de la base de datos o columna indexable.

    
pregunta Escher 13.12.2015 - 17:07
fuente

13 respuestas

79

El enfoque estándar para este tipo de problema es crear un UUID (Identificador universal único) para cada imagen. Por lo general, este es un identificador aleatorio de 128 bits que puede asignar a cada imagen sin ninguna preocupación particular de que sería posible enumerar las imágenes mediante un ataque de fuerza bruta en el espacio de nombres.

Por ejemplo, en .NET puede usar el GUID estructura para este tipo de propósitos. Desde Windows 2000 ( fuente ), Guid.NewGuid genera un UUID aleatorio (versión 4). (Las versiones antiguas generaron un versión 1 UUID que revela la fecha en que se generó , sin hacer nada para protegerlo del problema del "número de factura".)

    
respondido por el Rоry McCune 13.12.2015 - 17:52
fuente
30

Simplemente usaría el hash de la imagen. ¿Cuál es el problema de que alguien descubra el hash que usaste? Si pienso que "esta parte de la url parece un sha1", descargue el archivo y tiene que sha1, tenía razón. Pero eso no me permite romper tu «seguridad de gato». Incluso si era tratable intentar romper el hash para descubrir la imagen, no tiene sentido intentarlo en lugar de simplemente descargarlo.

    
respondido por el Ángel 13.12.2015 - 21:07
fuente
14

Simplemente genere un hash criptográficamente seguro de los datos de la imagen y utilícelo como un identificador.

Esto tiene dos efectos secundarios:

  • Las personas pueden saber si una imagen ya existe en tu servicio al solicitar una imagen con ese hash.
  • La gente no puede subir imágenes duplicadas.

Ambos de estos efectos no son inherentemente malos. Incluso podrían ser útiles. Pero si desea evitarlos, puede agregar a cada hash de imagen un número pseudoaleatorio de un generador de números aleatorios seguro.

Las colisiones no son nada de qué preocuparse, por cierto. Con una función hash como SHA256, the las posibilidades de una colisión aleatoria son tan pequeñas desde el punto de vista astronómico, sería una sensación cuando encontraras una .

    
respondido por el Philipp 13.12.2015 - 18:09
fuente
9

La forma estándar es simplemente generar aleatoriamente sus URL, utilizando un generador de números pseudoaleatorios criptográficamente seguro (CSPRNG).

No es necesario realizar ningún hashing o algo similar, solo use números aleatorios antiguos y sencillos. Tampoco tienen que ser GUID (a menos que su base de datos maneje GUID mejor que simples números por alguna razón). Es de suponer que su sitio ya recuerda a qué imagen se puede acceder en cada URL, así que modifíquelo para tratar con URL aleatorias en lugar de secuenciales.

Un número aleatorio de 128 bits debería ser lo suficientemente largo.

Recuerde verificar si hay URL duplicadas al procesar nuevas imágenes.

    
respondido por el immibis 13.12.2015 - 22:33
fuente
8

Por lo que leí en la pregunta, los comentarios y otras respuestas, todo gira en torno a la búsqueda de un identificador único para cada imagen, lo que no se puede adivinar, ni proporcionaría información sobre el número de imágenes, y es fácil de manejar en una base de datos.

Entonces, ¿por qué no usa la marca de tiempo de inserción (número de milisegundos desde 1970)? Si existe la posibilidad de que dos personas inserten una imagen de gato en el mismo milisegundo, puede concatenarla con un número secuencial correspondiente al número de inserción en ese milisegundo.

De esa manera, lo único que alguien que busca de forma agresiva en tu última foto podría descubrir es la última vez que alguien agregó una foto, siempre y cuando dejes que un imbécil haga lo que parece un ataque diario.

Mientras tanto, no te preocuparían las colisiones ni el soporte de la base de datos.

    
respondido por el Aldian 15.12.2015 - 10:52
fuente
6

Solución alternativa:

Agregue metadatos a sus identificadores de imagen. Ejemplo:

philipp_20151213_00002.jpg - Segunda imagen publicada por el usuario Philipp el 13 de diciembre de 2015.

Fuga esos metadatos, pero solo son datos que un usuario puede ver de todos modos al hacer clic en ese enlace (supongo).

Esto no le dice a un observador cuántas imágenes se publican en total en su servicio, solo sobre la actividad de ese usuario en particular en ese día en particular. Si desea ocultar esto también, puede usar números pseudoaleatorios en lugar de números secuenciales. Las colisiones aún podrían ser posibles cuando un solo usuario carga una gran cantidad de imágenes en un día, pero será lo suficientemente raro como para poder manejarlas simplemente generando nuevos números aleatorios hasta que tenga uno que no se toma.

    
respondido por el Philipp 13.12.2015 - 18:22
fuente
1

Aquí hay un método. Mantenga un servidor CSPRNG de 8 bytes. Luego, para cada nueva imagen, genere otros 8 bytes CSPRNG. Hash este CSPRNG con su CSPRNG en todo el servidor (md5 está bien). Luego, XOR los últimos 8 bytes del hash con la ID de imagen (que se incrementará automáticamente desde 0 en una base de datos). El cliente recibirá una codificación Base64 del CSPRNG exclusivo de 8 bytes de la imagen junto con el resultado XOR de 8 bytes. Esta será la identificación de la imagen pública.

Cuando el servidor reciba la ID de imagen pública, tendrá los primeros 8 bytes de la ID pública junto con el CSPRNG de 8 bytes en todo el servidor. Luego tomará los últimos 8 bytes del hash y XOR con los últimos 8 bytes del ID público. El resultado sería la ID interna privada que se puede indexar desde la base de datos.

Actualización (explicación):

Primero, defina previamente un CSPRNG global aleatorio que se utilizará para todos los cálculos de ID (8 bytes o 64 bits con 18,446,744,073,709,551,616 combinaciones posibles).

serverCSPRNG = CSPRNG(8)

Para crear un nuevo ID público (16 bytes) a partir de un ID privado (8 bytes), haga lo siguiente:

newCSPRNG = CSPRNG(8)
hashEnding = last8Bytes(md5(newCSPRNG + serverCSPRNG))
publicID = newCSPRNG + XOR(hashEnding, privateID)

Para obtener el privateID del publicID:

hashEnding = last8Bytes(md5(first8Bytes(publicID) + serverCSPRNG))
privateID = XOR(hashEnding, last8Bytes(publicID))

Para mayor seguridad, un CSPRNG global secundario (solo servidor estático) puede tener XOR en los últimos 8 bytes del publicID para protegerlo completamente de ataques de fuerza bruta (ya que implementa el modelo de seguridad inherente de un pad de una sola vez).

    
respondido por el Jonathan Gray 13.12.2015 - 17:55
fuente
1

Como se señaló aquí : Hashes, UUID's, etc. tienen la "desventaja" de que las inserciones de registros en la base de datos donde estos hashes / uuid son el PK y el PK está agrupado son posiblemente muy costosos (definir costosos ...) ya que por lo general no son secuenciales (a menos que se use una función específica como NEWSEQUENTIALID : note el bloque "importante" en esa página: " Si la privacidad es una preocupación, no use esta función. Es posible adivinar el valor del próximo GUID generado ... ").

Aparte de las sugerencias aquí, consideraría algo como Twitter ( descontinuado ) snowflake . Escribí una biblioteca .Net similar ( IdGen ); es readme tiene alguna información sobre cómo funciona exactamente. La ventaja es que los ID generados son todavía (en su mayoría) secuenciales, no requieren mucho espacio (64bit vs. 128bit UUID's / hashes) y se pueden usar en un entorno distribuido (no coordinado) donde tiene varios hosts / procesos que generan ID sin causar colisiones. Y a pesar de que son secuenciales, no revelan mucha información sobre el número de imágenes de gatos (o, más generalmente, el número de "identificaciones usadas") durante un período de tiempo.

    
respondido por el RobIII 16.12.2015 - 16:02
fuente
1
  

Esto suena como un trabajo para un algoritmo de hash, ¿verdad?

No, porque como observas debes preocuparte por las colisiones. Para mí, suena como un trabajo para una permutación, es decir, un cifrado de bloque. Esto requiere la administración de una clave, que es la desventaja, pero le permite usar la función de incremento automático de su base de datos y no preocuparse por las colisiones.

La parte difícil es decidir qué hacer con el IV, y aquí tienes opciones. Podría generar una nueva cada vez que cree una URL, por lo que potencialmente habrá, por ejemplo. 2 ^ 128 URL diferentes por imagen de gato. Puede hacer que el IV sea por usuario o por sesión y almacenado en el lado del servidor como parte del perfil de usuario / estado de sesión. Incluso puede hacer que sea por usuario pero incluido en la URL, de modo que pueda hacer un seguimiento de quién hace que las imágenes se vuelvan virales.

    
respondido por el Peter Taylor 16.12.2015 - 18:00
fuente
0

Un enfoque es utilizar hashids .

  

Hashids es una pequeña biblioteca de código abierto que genera identificadores cortos, únicos y no secuenciales a partir de números.

     

Convierte números como 347 en cadenas como "yr8", o una matriz de números como [27, 986] en "3kTMd".

     

También puedes decodificar esos IDs de vuelta. Esto es útil para agrupar varios parámetros en uno o simplemente usarlos como UID cortos.

El rendimiento de su base de datos no se ve afectado, ya que puede seguir utilizando identificadores numéricos secuenciales internamente. Mientras tanto, las teclas externas son opacas.

    
respondido por el Alfred Armstrong 14.12.2015 - 18:29
fuente
0

Tengo una solución de baja tecnología para este problema. Simplemente use un servicio de acortamiento de URL o escriba el suyo.

Proporciona lo siguiente:

  1. Su URL pública no está expuesta en los sitios de redes sociales.
  2. Se garantiza que tus URL son aleatorias y aleatorias.
  3. Usted es libre de cambiar su implementación subyacente de la asignación de nombres de recursos, y los enlaces externos continuarán funcionando.
  4. Compartir más fácilmente http://catpic.to/i34dhY contra http://catpictures.com/some-guid-string .
  5. La ID única se puede indexar / buscar fácilmente.

Si no desea confiar en un servicio de terceros, puede rodar fácilmente su propio implementando una función bijective en el idioma de su elección.

    
respondido por el Burhan Khalid 16.12.2015 - 07:54
fuente
0

Problema :

  • Deseamos tener un número que sea secuencial; de lo contrario, resulta caro agregar registros a la base de datos, ya que la mitad de los índices deben actualizarse en un orden mayormente aleatorio.
  • No queremos que el número se relacione con la cantidad de gatos que se han cargado.
  • Necesitamos que el número sea único, pero solo dentro de su sitio web.

Por lo tanto:

  • nextCat se establece en 0 cuando el sitio web primero se inicia, es probable que tenga que ser de 64 bits.
  • nextCat es incremented cada vez que se agrega un gato, y newCat se establece en true .
  • nextCat es incremented por un temporizador aleatorio que se dispara a una velocidad más rápida de lo que usted espera que se agreguen los gatos. Sin embargo, si newCat es true , entonces el incremento no se realiza para este disparo del temporizador, y newCat se establece en false .
  • A cada gato TAMBIÉN se le da un GUID, pero nunca se debe encontrar en función de su GUID
  • la dirección web de un gato es something.com/cats/catNumber-catGuid
  • si cuando se solicita un gato, catGuid está equivocado, se da la misma respuesta para un número de gato que no se relaciona con un gato.

(El temporizador se realiza por un tiempo aleatorio, por lo que es difícil saber si se agregan dos gatos entre un disparo del temporizador).

    
respondido por el Ian Ringrose 16.12.2015 - 16:49
fuente
-2

Práctica recomendada general: nunca exponga el PKEY en ningún enlace web.

En su base de datos: su PKEY debe ser un BIGINT para la velocidad. También en su base de datos, considere agregar este campo ... ( public_filename .. si no existe) a su tabla. El campo public_filename debe ser una cadena guid. Use una función guid para cambiar el nombre del archivo con un nombre de archivo único al cargarlo en su servidor, y rellene public_filename con eso.

El public_filename se debe usar para los enlaces web, no para el PKEY.

También recomiendo mantener un campo user_filename para admitir cualquier búsqueda forense del cargador, si lo permites. user_filename sería el nombre de archivo original subido por el usuario.

Nunca exponga el PKEY en ningún enlace web, siempre use alguna forma de public_filename . Utilice las consultas de su base de datos para quitar la referencia de public_filename a un PKEY, y desde allí puede averiguar qué archivo debe servir desde el servidor.

Otra práctica recomendada: nunca sobrescribas automáticamente la carga de un archivo de usuarios. Cambie el nombre del campo user_filename con una serialización (-001, -002).

Es probable que obtenga muchos archivos con el nombre "mycat" del mismo usuario.

    
respondido por el fredogone 13.12.2015 - 21:04
fuente

Lea otras preguntas en las etiquetas