2010-05-24 10 views
10

OK, probabilmente sto avendo un brutto lunedì, ma ho le seguenti necessità e sto vedendo molte soluzioni parziali ma sono sicuro di non essere la prima persona ad aver bisogno di questo, quindi mi sto chiedendo se mi manca l'ovvio.Componente URL sicuro non manomissione in Perl utilizzando la crittografia simmetrica?

$ client ha da 50 a 500 byte di dati binari che devono essere inseriti nel centro di un URL e roundtrip al browser del loro cliente. Dato che fa parte dell'URL, siamo contro il limite "teorico" di 1K di un URL GET. Inoltre, $ client non vuole che i propri clienti decodifichino i dati o li manomettano senza rilevamento. $ client preferirebbe non archiviare nulla lato server, quindi questo deve essere completamente indipendente. Deve essere il codice Perl e veloce, sia in codifica che in decodifica.

Penso che l'ultimo passo possa essere base64. Ma quali sono i passaggi per la crittografia e l'hashing che hanno più senso?

+0

È necessario memorizzare qualcosa sul lato server. E non capisco cosa significa "stand-alone" in questo caso, come accederà all'URL? –

+0

Se sto capendo questo, hai bisogno di un modo per crittografare 50-500 byte di dati in un URL senza far saltare oltre 1k di testo? – Schwern

+0

Devo passare da $ dati a URL cliente a $ dati. Niente sarà memorizzato sul lato server. –

risposta

5

Ho un codice in un'app Cat che utilizza Crypt::Util per codificare/decodificare l'indirizzo e-mail di un utente per un collegamento di verifica e-mail.

Ho impostato un modello Crypt::Util utilizzando Catalyst::Model::Adaptor con una chiave segreta. Poi, nel mio controller Ho la seguente logica sul lato di invio:

my $cu = $c->model('CryptUtil'); 
my $token = $cu->encode_string_uri_base64($cu->encode_string($user->email)); 
my $url = $c->uri_for($self->action_for('verify'), $token); 

io mando questo link al $user->email e quando si fa clic sul Io uso il seguente.

my $cu = $c->model('CryptUtil'); 
if (my $id = $cu->decode_string($cu->decode_string_uri_base64($token))) { 
    # handle valid link 
} else { 
    # invalid link 
} 

Questo è in pratica ciò che edanite ha appena suggerito in un'altra risposta. Dovrai solo assicurarti che i dati che utilizzi per formare il token con il numero finale $url non superino il limite arbitrario.

+2

FWIW significa che qualcuno potrebbe catturare un'istanza specifica di questo, può riprodurlo (ad esempio usarlo di nuovo). Spetta all'OP, suppongo, decidere quale tipo di rischio si pone. –

+1

Come altri hanno sottolineato, è possibile aggiungere banalmente un datetime/counter/"one time pad" al messaggio per verificarlo. Randall non ha spiegato il suo caso d'uso abbastanza bene da controllare completamente ciò che voleva, ed è un ragazzo intelligente, sono sicuro che lo capirà. ;) – perigrin

-1

Quanto è sicuro? Potresti semplicemente xorare i dati con una lunga stringa casuale, quindi aggiungere un hash MD5 dell'intero lotto con un altro salt segreto per rilevare la manomissione?

Non userei che per i dati bancari, ma probabilmente sarebbe bene per la maggior parte delle cose web ...

grande

+3

Stranamente, è un po 'quello che stanno facendo già, tranne che (a) la stringa è troppo corta e (b) il cliente può controllare alcuni dei dati, quindi sono possibili attacchi in chiaro. Dal momento che sto rompendo la compatibilità con le versioni precedenti, volevo fare qualcosa che non sarebbe finito su thedailywtf.com, che il codice attuale è chiaramente degno di. –

+0

Abbastanza corretto, sono seduto qui a pensare ai modi per "complicare" questo approccio, ma se si desidera una sicurezza "adeguata" probabilmente si dovrebbe parlare con qualcuno che è un vero criptologo. (Sono solo abbastanza furbo da sapere che inevitabilmente mi farò criptare da solo ...) – bigiain

+2

Con CPAN è più facile non "complicare" quell'approccio e basta usare un modulo CPAN per crittografare i dati usando qualcosa che si sa essere sicuro. – perigrin

4

creare una chiave segreta e memorizzarlo sul server. Se ci sono più server e le richieste non sono garantite per tornare allo stesso server; dovrai usare la stessa chiave su ogni server. Questa chiave dovrebbe essere ruotata periodicamente.

Se si crittografa i dati in modalità CBC (Cipher Block Chaining) (vedere il modulo Criptazione :: CBC), il sovraccarico della crittografia è al massimo di due blocchi (uno per la IV e uno per il riempimento). I blocchi di 128 bit (ovvero 16 byte) sono comuni, ma non universali. Raccomando di usare AES (aka Rijndael) come codice a blocchi.

È necessario autenticare i dati per assicurarsi che non siano stati modificati. A seconda della sicurezza dell'applicazione, l'hashing del messaggio e l'inclusione dell'hash nel testo in chiaro crittografato potrebbero essere sufficienti. Ciò dipende dal fatto che gli hacker non riescano a modificare l'hash per far corrispondere il messaggio senza conoscere la chiave di crittografia simmetrica. Se stai usando chiavi a 128 bit per il cifrario, usa un hash a 256 bit come SHA-256 (puoi usare il modulo Digest per questo). Si potrebbe anche voler includere alcune altre cose come un timestamp nei dati per evitare che la richiesta venga ripetuta più volte.

+0

È necessario autenticare i dati? Come può il cliente inviare dati modificati senza conoscere la chiave? – Schwern

+0

@Schwern, Randal menziona in un altro commento che il cliente può fornire alcuni dei dati inclusi nei dati da crittografare. Un abile aggressore potrebbe essere in grado di usarlo per modificare i dati senza conoscere realmente la chiave. Ad un certo punto StackOverflow era vulnerabile a questo attacco, Jeff e Joel hanno discusso i dettagli in uno dei primi podcast. –

+0

@ Ven'Tatsu Hai un collegamento a quei podcast? Ammetto che non sono un esperto di sicurezza dei dati e sarei interessato a saperne di più. – Schwern

3

Vedo tre passaggi qui. Innanzitutto, prova a comprimere i dati. Con così pochi dati, bzip2 potrebbe risparmiare forse il 5-20%. Inserirò una guardia per assicurarmi che non aumenti i dati. Questo passaggio potrebbe non valerne la pena.

use Compress::Bzip2 qw(:utilities); 
$data = memBzip $data; 

Si potrebbe anche provare a ridurre manualmente la lunghezza di qualsiasi chiave e valore nei dati. Ad esempio, first_name potrebbe essere ridotto a fname.

In secondo luogo, crittografarlo. Scegli il tuo codice preferito e usa Crypt :: CBC. Qui uso Rijndael perché è abbastanza buono per la NSA. Ti consigliamo di fare benchmarking per trovare il miglior equilibrio tra prestazioni e sicurezza.

use Crypt::CBC; 
my $key = "SUPER SEKRET"; 
my $cipher = Crypt::CBC->new($key, 'Rijndael'); 
my $encrypted_data = $cipher->encrypt($data); 

È necessario memorizzare la chiave sul server. Inserirlo in un file protetto dovrebbe essere sufficiente, assicurando che il file venga lasciato come esercizio. Quando dici che non puoi memorizzare nulla sul server presumo che questo non includa la chiave.

Infine, Base 64 lo codifica. Userò la base 64 sicura per URL modificata che usa - e _ invece di + e/salvandoti dal dover spendere spazio per codificare questi caratteri nella stringa 64 di base. MIME::Base64::URLSafe copre quello.

use MIME::Base64::URLSafe; 
my $safe_data = urlsafe_b64encode($encrypted_data); 

Quindi incollarlo nell'URL come desiderato. Invertire il processo per leggerlo.

Si dovrebbe essere sicuri sulle dimensioni. La crittografia aumenterà la dimensione dei dati, ma probabilmente inferiore al 25%. Base 64 aumenterà le dimensioni dei dati di un terzo (codifica come 2^6 invece di 2^8). Questo dovrebbe lasciare la codifica di 500 byte comodamente all'interno di 1K.

+1

Uhh protezione antimanomissione? Scusa ma se conosco il messaggio in qualche modo posso sostituirlo inosservato con questo schema. La protezione anti-manomissione più semplice è quella di includere nel contenitore crittografato l'hash del messaggio + nonce segreto noto solo al server. – Joshua

+1

Come si sostituisce senza conoscere la chiave? O vuoi dire che si potrebbe prendere un dato crittografato diverso e inserirlo? – Schwern

Problemi correlati