Estoy buscando una forma de cifrar / ofuscar un ID de entero en otro entero. Más precisamente, necesito una función int F(int x)
, para que
- x <-> F (x) es una correspondencia uno a uno (si x! = y, F (x)! = F (y))
- dado F (x), es fácil averiguar x, por lo que F no es una función hash
- dado x y F (x) es difícil / imposible averiguar F (y), algo como
x ^ 0x1234
no funcionará
Para mayor claridad, no estoy buscando una solución de cifrado sólida, es solo una ofuscación. Imagina una aplicación web con URL como example.com/profile/1
, example.com/profile/2
etc. Los perfiles en sí no son secretos, pero me gustaría evitar que mirones casuales vean / recuperen todos los perfiles uno tras otro, así que prefiero esconderlos detrás de algo como example.com/profile/23423
, example.com/profile/80980234
etc. Aunque Los tokens almacenados en la base de datos pueden hacer el trabajo con bastante facilidad, tengo curiosidad por saber si hay algunas matemáticas simples disponibles para esto.
Un requisito importante que no tenía claro es que los resultados deben verse "aleatorios", es decir, dada una secuencia x,x+1,...,x+n
, F(x),F(x+1)...F(x+n)
no deben formar una progresión de ningún tipo.
fuente
Respuestas:
Ofuscarlo con alguna combinación de 2 o 3 métodos simples:
x
yy
que sean inversos multiplicativos entre sí (módulo 2 32 ), luego multiplique porx
para ofuscar y multiplique pory
para restaurar, todas las multiplicaciones son módulo 2 32 (fuente: "Un uso práctico de inversos multiplicativos" por Eric Lippert )El método del sistema numérico de longitud variable no obedece a su requisito de "progresión" por sí solo. Siempre produce pequeñas progresiones aritméticas. Pero cuando se combina con algún otro método, da buenos resultados.
Lo mismo ocurre con el método de representación modular.
Aquí hay un ejemplo de código C ++ para 3 de estos métodos. El ejemplo de Shuffle bits puede usar algunas máscaras y distancias diferentes para ser más impredecible. Otros 2 ejemplos son buenos para números pequeños (solo para dar una idea). Deben extenderse para ofuscar correctamente todos los valores enteros.
fuente
Quiere que la transformación sea reversible y no obvia. Eso suena como un cifrado que toma un número en un rango dado y produce un número diferente en el mismo rango. Si su rango son números de 64 bits, utilice DES. Si su rango es de 128 bits, utilice AES. Si desea un rango diferente, entonces su mejor opción es probablemente el cifrado Hasty Pudding , que está diseñado para hacer frente a diferentes tamaños de bloques y rangos de números que no encajan perfectamente en un bloque, como 100.000 a 999.999.
fuente
La ofuscación no es realmente suficiente en términos de seguridad.
Sin embargo, si está tratando de frustrar al espectador casual, le recomendaría una combinación de dos métodos:
Aquí hay un ejemplo (usando pseudocódigo):
No lo he probado, pero creo que es reversible, debería ser rápido y no demasiado fácil de descifrar el método.
fuente
return x XOR rotr(31415927, 5)
, ¿verdad? El último xor deshace el primero, y los giros se deshacen entre sí ... por supuesto, cualquier cadena de operaciones reversibles también es reversible, por lo que satisface esa condición.x = x XOR F(0)
, ox = x XOR 3087989491
, ox = x XOR rotr(31415927, 5)
. Su primer y último xor se niegan entre sí, por lo que todo lo que está haciendo es xorizar la entrada con desplazamiento de bits con la clave, o equivalentemente, xorizar la entrada con la tecla con desplazamiento de bits. Tenga en cuenta que esto es cierto incluso si utilizó diferentes claves para cada etapa: todas las claves se pueden componer en una sola clave que se puede copiar con el texto sin formato.Encontré esta pieza particular de código Python / PHP muy útil:
https://github.com/marekweb/opaque-id
fuente
Escribí un código JS usando algunas de las ideas en este hilo:
Produce buenos resultados como:
Prueba con:
XOR1
yXOR2
son números aleatorios entre 0 yMAX
.MAX
es2**32-1
; debe establecer esto en lo que crea que será su ID más alto.COPRIME
es un número que es coprime w /MAX
. Creo que los números primos en sí mismos son coprimos con cualquier otro número (excepto sus múltiplos).INVERSE
es el más complicado de entender. Estas publicaciones de blog no dan una respuesta directa, pero WolframAlpha puede resolverlo por usted . Básicamente, simplemente resolver la ecuación(COPRIME * x) % MAX = 1
parax
.La
build
función es algo que creé para facilitar la creación de estas canalizaciones de codificación / decodificación. Puedes alimentarlo tantas operaciones como quieras por[encode, decode]
parejas. Estas funciones tienen que ser iguales y opuestas. LasXOR
funciones son sus propios cumplidos, por lo que no necesita un par allí.Aquí hay otra involución divertida :
(asume enteros de 24 bits, simplemente cambie los números por cualquier otro tamaño)
fuente
n
es un sufijo de número para BigInts . Es una nueva función de JS que le permite procesar números realmente grandes. Necesitaba usarlo porque estoy multiplicando por números realmente grandes que podrían hacer que uno de los valores intermedios exceda temporalmenteNumber.MAX_SAFE_INTEGER
y pierda precisión.Haga cualquier cosa con los bits de la identificación que no los destruya. Por ejemplo:
Para el descifrado, haga todo eso en orden inverso.
Cree un programa que 'cifre' algunos valores interesantes para usted y póngalos en una tabla que pueda examinar. Haga que el mismo programa PRUEBE su rutina de cifrado / descifrado CON todo el conjunto de valores que desea tener en su sistema.
Agregue cosas a la lista anterior en las rutinas hasta que sus números se vean correctamente destrozados.
Para cualquier otra cosa, obtenga una copia de The Book .
fuente
Escribí un artículo sobre permutaciones seguras con cifrado en bloque , que debería cumplir con sus requisitos como se indica.
Sin embargo, sugeriría que si desea identificar identificadores difíciles de adivinar, debe usarlos en primer lugar: generar UUID y usarlos como clave principal para sus registros en primer lugar; no es necesario poder para convertir hacia y desde una identificación "real".
fuente
No estoy seguro de qué tan "difícil" necesita que sea, qué tan rápido o qué poca memoria usar. Si no tiene restricciones de memoria, puede hacer una lista de todos los enteros, mezclarlos y usar esa lista como un mapeo. Sin embargo, incluso para un entero de 4 bytes, necesitaría mucha memoria.
Sin embargo, esto podría hacerse más pequeño, por lo que en lugar de mapear todos los enteros, mapearía solo 2 (o el peor de los casos, 1) byte y aplicaría esto a cada grupo en el entero. Entonces, usando 2 bytes, un número entero sería (grupo1) (grupo2), mapearía cada grupo a través del mapa aleatorio. Pero eso significa que si solo cambia el grupo2, la asignación para el grupo1 seguirá siendo la misma. Esto podría "arreglarse" mapeando diferentes bits a cada grupo.
Entonces, * (group2) podría ser (bit 14,12,10,8,6,4,2,0), por lo que agregar 1 cambiaría tanto el grupo1 como el grupo2 .
Aún así, esto es solo seguridad por oscuridad, cualquiera que pueda ingresar números en su función (incluso si mantiene la función en secreto) podría resolverlo fácilmente.
fuente
Genere una clave simétrica privada para usar en su aplicación y cifre su entero con ella. Esto satisfará los tres requisitos, incluido el número 3 más difícil: uno necesitaría adivinar su clave para romper su esquema.
fuente
Lo que está describiendo aquí parece ser lo opuesto a una función unidireccional: es fácil de invertir pero muy difícil de aplicar. Una opción sería utilizar un algoritmo de cifrado de clave pública estándar y listo para usar en el que se fija una clave pública (secreta, elegida al azar) que se mantiene en secreto y una clave privada que se comparte con el mundo. De esa manera, su función F (x) sería el cifrado de x usando la clave pública. A continuación, puede descifrar fácilmente F (x) de nuevo ax utilizando la clave de descifrado privada. Tenga en cuenta que los roles de la clave pública y privada se invierten aquí: les da la clave privada a todos para que puedan descifrar la función, pero mantiene la clave pública en secreto en su servidor. De esa manera:
Esto tiene muchas ventajas. Primero, puede estar seguro de que el sistema de cifrado es seguro, ya que si utiliza un algoritmo bien establecido como RSA, no debe preocuparse por la inseguridad accidental. En segundo lugar, ya existen bibliotecas para hacer esto, por lo que no necesita codificar mucho y puede ser inmune a los ataques de canal lateral. Por último, puede hacer posible que cualquiera pueda invertir F (x) sin que nadie pueda calcular F (x).
Un detalle: definitivamente no debería usar el tipo int estándar aquí. Incluso con enteros de 64 bits, hay tan pocas combinaciones posibles que un atacante podría simplemente intentar invertir todo por fuerza bruta hasta que encuentre el cifrado F (y) durante algún y, incluso si no tiene la clave. Sugeriría usar algo como un valor de 512 bits, ya que incluso un ataque de ciencia ficción no podría forzar esto.
¡Espero que esto ayude!
fuente
Si
xor
es aceptable para todo menos para inferirF(y)
dadox
yF(x)
entonces creo que puedes hacerlo con una sal . Primero elija una función secreta unidireccional. Por ejemploS(s) = MD5(secret ^ s)
. Entonces,F(x) = (s, S(s) ^ x)
dondes
se elige al azar. Escribí eso como una tupla, pero puedes combinar las dos partes en un número entero, por ejemploF(x) = 10000 * s + S(s) ^ x
. El descifrado extrae la sals
nuevamente y la usaF'(F(x)) = S(extract s) ^ (extract S(s)^x)
. Dadox
yF(x)
puede vers
(aunque está un poco confuso) y puede inferir,S(s)
pero para algún otro usuarioy
con una sal aleatoria diferente,t
el usuario que sabeF(x)
que no puede encontrarS(t)
.fuente
S(s)
también se verá al azar, porF(x)
lo que no tendrá ningún tipo de progresión.