Estou procurando uma maneira de criptografar / ofuscar um ID de inteiro em outro inteiro. Mais precisamente, preciso de uma função int F(int x)
, para que
- x <-> F (x) é correspondência um a um (se x! = y, F (x)! = F (y))
- dado F (x), é fácil descobrir x - então F não é uma função hash
- dados x e F (x) é difícil / impossível descobrir F (y), algo como
x ^ 0x1234
não vai funcionar
Para maior clareza, não estou procurando uma solução de criptografia forte, é apenas ofuscação. Imagine uma aplicação web com URLs como example.com/profile/1
, example.com/profile/2
etc. Os perfis em si não são segredo, mas eu gostaria de evitar voyeurs casuais para ver / buscar todos os perfis de um após o outro, então eu prefiro escondê-los atrás de algo como example.com/profile/23423
, example.com/profile/80980234
etc. Embora tokens armazenados em banco de dados podem fazer o trabalho facilmente, estou curioso para saber se há alguma matemática simples disponível para isso.
Um requisito importante sobre o qual não estava claro é que os resultados deveriam parecer "aleatórios", isto é, dada uma sequência x,x+1,...,x+n
, F(x),F(x+1)...F(x+n)
não deveriam formar uma progressão de nenhum tipo.
fonte
Respostas:
Ofusque-o com alguma combinação de 2 ou 3 métodos simples:
x
ey
que são inversos multiplicativos um do outro (módulo 2 32 ), depois multiplique porx
para ofuscar e multiplique pory
para restaurar, todas as multiplicações são módulo 2 32 (fonte: "Um uso prático de inversos multiplicativos" por Eric Lippert )O método do sistema numérico de comprimento variável não obedece ao seu requisito de "progressão" sozinho. Ele sempre produz progressões aritméticas curtas. Mas quando combinado com algum outro método, dá bons resultados.
O mesmo é verdade para o método de representação modular.
Aqui está um exemplo de código C ++ para 3 desses métodos. O exemplo de bits aleatórios pode usar algumas máscaras e distâncias diferentes para ser mais imprevisível. Outros 2 exemplos são bons para números pequenos (apenas para dar uma ideia). Eles devem ser estendidos para ofuscar todos os valores inteiros corretamente.
fonte
Você deseja que a transformação seja reversível e não óbvia. Isso soa como uma criptografia que pega um número em um determinado intervalo e produz um número diferente no mesmo intervalo. Se o seu intervalo for de números de 64 bits, use DES. Se o seu intervalo for de números de 128 bits, use AES. Se você deseja um intervalo diferente, sua melhor aposta é provavelmente a cifra Hasty Pudding , que é projetada para lidar com diferentes tamanhos de blocos e com intervalos de números que não se encaixam perfeitamente em um bloco, como 100.000 a 999.999.
fonte
A ofuscação não é realmente suficiente em termos de segurança.
No entanto, se você está tentando frustrar o observador casual, recomendo uma combinação de dois métodos:
Aqui está um exemplo (usando pseudocódigo):
Não testei, mas acho que é reversível, deve ser rápido e não muito fácil descobrir o método.
fonte
return x XOR rotr(31415927, 5)
, certo? O último xor desfaz o primeiro, e as rotações desfazem um ao outro ... é claro que qualquer cadeia de operações reversíveis também é reversível, portanto, satisfaz essa condição.x = x XOR F(0)
, oux = x XOR 3087989491
, oux = x XOR rotr(31415927, 5)
. Seu primeiro e último xors negam um ao outro, então tudo o que você está fazendo é xorando a entrada com deslocamento de bits com a chave - ou, de modo equivalente, xorando a entrada com a chave com desvio de bits. Observe que isso é verdade mesmo se você usar chaves diferentes para cada estágio - todas as chaves podem ser compostas em uma única chave que pode ser corrigida com o texto simples.Achei esta parte específica do código Python / PHP muito útil:
https://github.com/marekweb/opaque-id
fonte
Escrevi alguns códigos JS usando algumas das ideias neste tópico:
Produz bons resultados como:
Testando com:
XOR1
eXOR2
são apenas números aleatórios entre 0 eMAX
.MAX
é2**32-1
; você deve definir isso para o que você acha que será o seu maior ID.COPRIME
é um número que é coprime c /MAX
. Acho que os próprios números primos são coprimes com todos os outros números (exceto seus múltiplos).INVERSE
é o mais difícil de descobrir. Essas postagens de blog não fornecem uma resposta direta, mas o WolframAlpha pode descobrir por você . Basicamente, basta resolver a equação(COPRIME * x) % MAX = 1
parax
.A
build
função é algo que criei para facilitar a criação desses pipelines de codificação / decodificação. Você pode alimentá-lo com quantas operações quiser como[encode, decode]
pares. Essas funções devem ser iguais e opostas. AsXOR
funções são seus próprios elogios, então você não precisa de um par lá.Aqui está outra involução divertida :
(assume inteiros de 24 bits - basta alterar os números para qualquer outro tamanho)
fonte
n
é um postfix de número para BigInts . É um novo recurso JS que permite processar números realmente grandes. Eu precisei usá-lo porque estou multiplicando por números realmente grandes que podem fazer com que um dos valores intermediários exceda temporariamenteNumber.MAX_SAFE_INTEGER
e perca a precisão.Faça qualquer coisa com os bits do ID que não os destrua. Por exemplo:
Para descriptografar, faça tudo isso na ordem inversa.
Crie um programa que irá 'criptografar' alguns valores interessantes para você e colocá-los em uma tabela que você possa examinar. Tenha o mesmo programa TESTE sua rotina de criptografia / descriptografia COM todo o conjunto de valores que você deseja ter em seu sistema.
Acrescente coisas à lista acima nas rotinas até que seus números pareçam devidamente mutilados para você.
Para qualquer outra coisa, obtenha uma cópia do Livro .
fonte
Escrevi um artigo sobre permutações seguras com cifras de bloco , que deve atender aos seus requisitos conforme declarado.
Eu sugiro, entretanto, que se você quiser identificadores difíceis de adivinhar, você deve apenas usá-los em primeiro lugar: gerar UUIDs e usá-los como a chave primária para seus registros em primeiro lugar - não há necessidade de ser capaz para converter de e para um ID 'real'.
fonte
Não tenho certeza de quão "difícil" você precisa que seja, quão rápido ou quão pouca memória usar. Se você não tem restrições de memória, pode fazer uma lista de todos os inteiros, embaralhá-los e usar essa lista como um mapeamento. No entanto, mesmo para um número inteiro de 4 bytes, você precisaria de muita memória.
No entanto, isso poderia ser menor, então, em vez de mapear todos os inteiros, você mapearia apenas 2 (ou no pior caso 1) byte e aplicaria isso a cada grupo no inteiro. Assim, usando 2 bytes um inteiro seria (grupo1) (grupo2) você mapearia cada grupo através do mapa aleatório. Mas isso significa que se você alterar apenas o grupo2, o mapeamento para o grupo1 permanecerá o mesmo. Isso poderia ser "consertado" mapeando bits diferentes para cada grupo.
Portanto, * (grupo2) poderia ser (bit 14,12,10,8,6,4,2,0), portanto, adicionar 1 mudaria o grupo1 e o grupo2 .
Ainda assim, isso é apenas segurança por obscuridade, qualquer um que possa inserir números em sua função (mesmo que você mantenha a função em segredo) poderia facilmente descobrir isso.
fonte
Gere uma chave simétrica privada para usar em seu aplicativo e criptografe seu inteiro com ela. Isso irá satisfazer todos os três requisitos, incluindo o mais difícil # 3: seria necessário adivinhar sua chave para quebrar seu esquema.
fonte
O que você está descrevendo aqui parece ser o oposto de uma função unilateral: é fácil de inverter, mas muito difícil de aplicar. Uma opção seria usar um algoritmo de criptografia de chave pública padrão, disponível no mercado, onde você fixa uma chave pública (secreta, escolhida aleatoriamente) que você mantém em segredo e uma chave privada que você compartilha com o mundo. Dessa forma, sua função F (x) seria a criptografia de x usando a chave pública. Você poderia então descriptografar facilmente F (x) de volta para x usando a chave de descriptografia privada. Observe que as funções da chave pública e privada são invertidas aqui - você dá a chave privada a todos para que possam descriptografar a função, mas mantém a chave pública em segredo em seu servidor. Dessa maneira:
Isso tem muitas vantagens. Em primeiro lugar, você pode ter certeza de que o sistema de criptografia é seguro, pois se você usar um algoritmo bem estabelecido como o RSA, não precisa se preocupar com insegurança acidental. Em segundo lugar, já existem bibliotecas para fazer isso, então você não precisa codificar muito e pode ser imune a ataques de canal lateral. Finalmente, você pode tornar possível que qualquer pessoa inverta F (x) sem que ninguém seja realmente capaz de calcular F (x).
Um detalhe - você definitivamente não deve usar apenas o tipo int padrão aqui. Mesmo com inteiros de 64 bits, há tão poucas combinações possíveis que um invasor poderia simplesmente tentar inverter tudo com a força bruta até encontrar a criptografia F (y) para algum y, mesmo que não tenha a chave. Eu sugeriria usar algo como um valor de 512 bits, já que mesmo um ataque de ficção científica não seria capaz de aplicar força bruta a isso.
Espero que isto ajude!
fonte
Se
xor
é aceitável para tudo, exceto inferirF(y)
dadox
eF(x)
então acho que você pode fazer isso com um sal . Primeiro, escolha uma função secreta de mão única. Por exemploS(s) = MD5(secret ^ s)
. Então,F(x) = (s, S(s) ^ x)
ondes
é escolhido aleatoriamente. Eu escrevi isso como uma tupla, mas você pode combinar as duas partes em um inteiro, por exemploF(x) = 10000 * s + S(s) ^ x
. A descriptografia extrai o sals
novamente e usaF'(F(x)) = S(extract s) ^ (extract S(s)^x)
. Dadox
eF(x)
você pode vers
(embora seja um pouco ofuscado) e você pode inferir,S(s)
mas para algum outro usuárioy
com um sal aleatório diferente quet
o usuário sabeF(x)
não pode encontrarS(t)
.fonte
S(s)
também parecerá aleatório, portantoF(x)
, não terá nenhum tipo de progressão.