it-swarm.dev

Tecniche migliori della crittografia dei parametri url

Sono un programmatore che lavora su un'applicazione in cui l'unica scelta/vs/scadenza era quella di implementare la crittografia simmetrica sui valori dei parametri url. I dati sono di natura insensibile, ma dovevamo impedire agli agenti di vendita di sbirciare a vicenda. (Le chiavi vengono generate durante la creazione della sessione e sono crittograficamente efficaci.) Le sessioni dovrebbero terminare frequentemente.

La gerarchia dei ruoli era Manager--> Supervisor--> Agents. Le strutture dati al momento non tengono conto di questi ruoli in modo da far rispettare rigorosamente chi può vedere cosa. Ottenere queste informazioni dal database NON era da nessuna parte vicino al semplice. (Database ricorsivo.)

So che questa tecnica è in fondo alla lista come difesa contro la manipolazione dei parametri. Quale sarebbe stata una tecnica migliore?

Vincoli:
Il controllo basato sui ruoli non è un'opzione.

[Ulteriori informazioni] Gli URL creati e inviati al client prima di apportare qualsiasi modifica sembravano:

https://www.example.com/agent/?producerId=12345

La superficie della minaccia specifica qui è la manipolazione dei parametri contro ?agentId=12345. Gli ID agente sono assegnati in modo univoco a ciascun agente. Pertanto, se l'agente A desidera esaminare le statistiche dell'agente B, potrebbe aver inserito agentId = 22222 per esaminare le quotazioni dell'agente e le statistiche sulle vendite correnti.

Ancora una volta, il controllo basato sui ruoli non era un'opzione per me: non ero in grado di apportare modifiche al database OR il livello di persistenza.

La mia soluzione era quella di utilizzare una chiave di crittografia creata dalla sessione (utilizzando la classe KeyGenerator di Java) e crittografare gli URL in uscita inviati al client. Quindi ora, l'URL appare come:

https://www.example.com/agent/?producerId=<ciphertext>

Ora, se qualcuno prova agentId = 22222, il server decifrerà ciò che pensa è un testo cifrato e alla fine creerà una sequenza di caratteri non valida.

(Ciò lascia aperta la possibilità di trovare un agente esistente potrebbe, ma è abbastanza improbabile che sia rilevante per la persona che sta eseguendo l'attacco.

Sottolineerò che questa domanda non riguarda la sicurezza ottimale (che sarebbe un controllo basato sui ruoli per garantire l'accesso alle risorse) e sul tentativo di ridurre la sicurezza in un'area grigia.

La soluzione di crittografia dei parametri qui mi è stata consigliata da uno dei nostri addetti alla sicurezza. Ho preso un takeaway che non avevo considerato su questa soluzione - URL interrotti - e userò that nonché il problema di manutenzione creato da questa soluzione per discutere del tempo per applicare le regole di accesso in modo meno frenetico.

21
avgvstvs

Buona domanda! Grazie per aver elaborato la minaccia che stai cercando di difendere. Ho modificato la mia risposta di conseguenza.

Riepilogo. La tua difesa principale dovrebbe essere controllo degli accessi. Devi limitare quali utenti possono visualizzare quali pagine. Dettagli sotto.

Controllo dell'accesso nelle applicazioni Web. Quello che devi fare è controllare che l'utente sia autorizzato ad accedere ai dati che mostrerai su una pagina, prima di consentire loro di vedere quei dati. Questo in sostanza si riduce al controllo degli accessi: vuoi controlli che limitano quali utenti possono visualizzare quali dati, sulla base di una politica di autorizzazione.

Sembra che tu abbia una sequenza di pagine, una per ogni agente:

http://www.example.com/agent/?producerId=12345
http://www.example.com/agent/?producerId=12346
http://www.example.com/agent/?producerId=12347
...

dove i producerIds (agentIds) sono potenzialmente ipotizzabili o prevedibili. Vuoi assicurarti che l'agente 12345 possa visualizzare http://www.example.com/agent/?producerId=12345 ma nessuna delle altre pagine. OK.

Questa è una situazione standard e la difesa standard è: controllo degli accessi.

Per implementare il controllo dell'accesso, si codifica l'applicazione Web in modo che ogni pagina controlli se l'utente è autorizzato a visualizzare quella pagina prima di consentire all'utente di visualizzare quella pagina. Ad esempio, per la pagina sopra elencata, la logica che implementa quella pagina verifica l'identità dell'utente attualmente connesso. Se l'id dell'utente che ha effettuato l'accesso corrisponde a producerId del parametro page, mostrate loro le informazioni. Se l'id non corrisponde, non mostri loro le informazioni: se è un altro utente, mostri loro una pagina di errore (con informazioni su come ottenere l'accesso) o se l'utente non ha ancora effettuato l'accesso, reindirizza li ad una pagina di accesso.

Questo non romperà i segnalibri. Non richiede modifiche al database, modifiche al livello di persistenza o controllo dell'accesso basato sui ruoli. Richiede di avere un modo per cercare l'identità dell'utente attualmente connesso e associarlo con l'ID del proprio provider. Inoltre, se si desidera consentire a manager e supervisori di visualizzare i dati per tutti gli altri agenti, è necessario un modo per cercare l'utente attualmente connesso e determinare se si tratta di un manager o supervisore o meno. Se si desidera consentire solo al manager/supervisore dell'agente di visualizzare la propria pagina (non tutti gli altri manager/supervisori), è necessario disporre di un modo per determinare il manager/supervisore di ciascun agente. Questi sono requisiti piuttosto basilari e minimi; è difficile vedere come potresti evitarli.

Come sottolinea correttamente @symbcbean, questo è un errore molto comune che si trova spesso nelle applicazioni web. Un esempio tipico potrebbe essere un sito che utilizza un valore di parametro indovinabile per identificare una risorsa e non autentica adeguatamente l'utente. Ad esempio, supponiamo che agli ordini venga assegnato un numero di ordine sequenziale:

https://www.example.com/show_order.php?id=1234
https://www.example.com/show_order.php?id=1235
https://www.example.com/show_order.php?id=1236
...

e supponiamo che chiunque conosca l'URL possa visualizzare l'ordine. Sarebbe un male, perché significa che chiunque conosca (o indovina) il numero dell'ordine può visualizzare l'ordine, anche se non è autorizzato a farlo. Questo è uno dei Primi dieci di OWASP rischi per la sicurezza delle applicazioni Web: Riferimenti a oggetti diretti non sicuri . Per ulteriori informazioni, consiglio di leggere le risorse disponibili su OWASP. OWASP ha molte ottime risorse sulla sicurezza delle applicazioni web.

Altri commenti. Altri hanno suggerito di utilizzare SSL. Sebbene ciò non impedisca la manomissione dei parametri, è una buona prassi di sicurezza generale che difende da altri tipi di problemi. L'uso di SSL è semplice: configura il tuo sito Web per utilizzare https, anziché http (e idealmente, abilita HSTS e imposta il bit secure su tutti i cookie).

Inoltre, è spesso meglio evitare di memorizzare informazioni riservate nei parametri URL, a parità di altre condizioni. È possibile memorizzare le informazioni riservate nello stato della sessione o nel database.

16
D.W.

In breve: Non crittografare i parametri URL, utilizzare una ricerca separata .

Inoltre, l'utilizzo di HTTPS è sostanzialmente non negoziabile se si desidera qualsiasi misura di sicurezza delle applicazioni Web. È obbligatorio nel 2015. Mettiti comodo con TLS 1.1+.


Cosa vogliono fare gli sviluppatori

What developers want to do

Cosa dovrebbero fare invece gli sviluppatori

enter image description here

7

ma dovevamo impedire agli agenti di vendita di sbirciare a vicenda

Ciò implica piuttosto che il client è un browser: a un certo punto stai inviando la chiave come testo in chiaro?

Il polinomio è corretto, dovresti usare SSL. Questo non risolverà il problema degli utenti che digitano valori adiacenti in un URL simile a:

https://www.example.com/show_order.php?id=1234
https://www.example.com/show_order.php?id=1235
https://www.example.com/show_order.php?id=1236
...

È possibile generare un token di autenticazione lato server in base ai parametri che devono essere presentati per convalidare la richiesta. Idealmente dovresti usare un codice di autenticazione dei messaggi (MAC) per questo, ma un hash funzionerebbe anche se stai attento. per esempio. in PHP ...

 print "<a href='show_order.php?id=" . $id . "&valid=" . md5($id . crypto_key()) . "'>...

Che è validato semplicemente da:

if ($_GET['valid'] != md5($_GET['id'] . crypto_key()) {
   die('not authorized');
}

Qui crypto_key() restituisce una chiave crittografica statica (generala estraendo, diciamo, 128 bit da /dev/urandom E memorizzandola nel database).

Ma devi ancora controllare l'accesso al codice che genera l'URL.

3
symcbean

Ecco la mia soluzione

$id=1234;
$en_id = encrypString( $id);

e creo l'URL come

https://www.example.com/show_order.php?id=$en_id

sembrerà l'URL

https://www.example.com/show_order.php?id=9muEYh4lShFDeCnXqoNpxucs42Fuz5Nexq1IUGWYEffffe88yRbJu

e dall'altra parte decifro

$en_id= decryptString($_GET['id']);

le funzioni per crypt e decrypt sono

function encrypString($plaintext) {
         # --- ENCRYPTION ---

        $key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");//change this

        # show key size use either 16, 24 or 32 byte keys for AES-128, 192
        # and 256 respectively
        $key_size =  strlen($key);
        //echo "Key size: " . $key_size . "\n";


        # create a random IV to use with CBC encoding
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);

        # creates a cipher text compatible with AES (Rijndael block size = 128)
        # to keep the text confidential 
        # only suitable for encoded input that never ends with value 00h
        # (because of default zero padding)
        $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
                                     $plaintext, MCRYPT_MODE_CBC, $iv);

        # prepend the IV for it to be available for decryption
        $ciphertext = $iv . $ciphertext;

        # encode the resulting cipher text so it can be represented by a string
        $ciphertext_base64 = base64_encode($ciphertext);

        return  rawurlencode($ciphertext_base64);//important rawurlencode for + symbol in url

    }


decryptString($ciphertext_base64) {
        # --- DECRYPTION ---

        $key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");//change this

        # show key size use either 16, 24 or 32 byte keys for AES-128, 192
        # and 256 respectively
        $key_size =  strlen($key);
        //echo "Key size: " . $key_size . "\n";

        # create a random IV to use with CBC encoding
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_DEV_URANDOM);

        $ciphertext_dec = base64_decode($ciphertext_base64);

        # retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
        $iv_dec = substr($ciphertext_dec, 0, $iv_size);

        # retrieves the cipher text (everything except the $iv_size in the front)
        $ciphertext_dec = substr($ciphertext_dec, $iv_size);

        # may remove 00h valued characters from end of plain text
        $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,
                                    $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

        return rawurldecode($plaintext_dec);
    }
2

Per evitare la manomissione dei parametri, ho sempre inviato un hash con i valori di testo semplice.

per esempio. prendi questo url che vuoi proteggere

https://www.mysite.com/somepage?param1=abc&param2=xyz

Sul server, il tuo modello eseguirà l'hashing di tutti i valori dell'URL con un sale segreto

string salt = "A1B2C3D4...";
string param1 = "abc";
string param2 = "xyz";
string hash = YourFavoriteHashingAlgorithm(param1 + param2 + salt);
// result hash = "Y83YMB38DX83YUHFIEIGKDHSEUG"

quindi invii questo hash insieme agli altri valori url

https://www.mysite.com/somepage?param1=abc&param2=xyz&hash=Y83YMB38DX83YUHFIEIGKDHSEUG

Ora, quando ricevi una richiesta per questo URL, prendi di nuovo i parametri che ti vengono presentati e li hash tramite lo stesso algoritmo. L'hash che hai appena generato deve corrispondere all'hash che ti viene presentato, altrimenti invia loro un risultato "Richiesta errata" 400 !.

La cosa bella è che i tuoi parametri sono ancora leggibili dagli umani e tutta la tua logica di validazione esistente può rimanere la stessa.

1
raterus

Non usa input dell'utente

(perché non puoi fiducia esso)

Questa risposta estende accettato con ciò che mi sembra una semplificazione significativa.

Ora, dalla tua descrizione e dalla mia migliore comprensione, hai detto che vuoi impedire a Sales Agent A (Ovvero 12345) di dare un'occhiata I dati di Sales Agent B (Ovvero 54321).

Semplicemente, uccidi il parametro agentId dalla stringa della query e prendilo dalla sessione

L'URL diventa https://example.org/show_order.php

Internamente, l'applicazione deve estrarre l'ID agente di vendita dall'entità archiviata nella sessione. Sono eccessivamente arrugginito in PHP quindi userò lo pseudo codice

SELECT * FROM sales where salesman_id = ?1;
[1 = getPrincipalSalesId()]

Questa query ignorerà semplicemente tutto ciò che proviene dal client. Non richiede modifiche al livello di persistenza. Non richiede nemmeno l'implementazione di RBAC (controllo di accesso basato sui ruoli), ma tutto riguarda quella funzione getPrincipalSalesId.

È sostanzialmente lo stesso codice che usi per generare l'URL, ma questa volta inserisci quel valore nella query, rendendolo implicito .