Perché non esporre una chiave primaria


53

Nella mia formazione mi è stato detto che è un'idea imperfetta esporre all'utente le chiavi primarie effettive (non solo chiavi DB, ma tutti gli accessori principali).

Ho sempre pensato che fosse un problema di sicurezza (perché un utente malintenzionato poteva tentare di leggere cose non proprie).

Ora devo verificare se l'utente è autorizzato ad accedere comunque, quindi c'è un motivo diverso dietro di esso?

Inoltre, poiché i miei utenti devono comunque accedere ai dati, dovrò disporre di una chiave pubblica per il mondo esterno da qualche parte nel mezzo. Ora quella chiave pubblica ha gli stessi problemi della chiave primaria, non è vero?


C'è stata la richiesta di un esempio sul perché farlo comunque, quindi eccone uno. Tieni presente che la domanda dovrebbe riguardare il principio stesso non solo se si applica in questo esempio. Le risposte che affrontano altre situazioni sono esplicitamente benvenute.

Applicazione (Web, Mobile) che gestisce l'attività, ha più UI e almeno un'API automatizzata per la comunicazione tra sistemi (eG il reparto contabilità vuole sapere quanto addebitare al cliente in base a ciò che è stato fatto). L'applicazione ha più clienti, quindi la separazione dei loro dati (logicamente, i dati sono memorizzati nello stesso DB) è un must del sistema. Ogni richiesta sarà verificata per la validità, qualunque cosa accada.

L'attività è granulare molto fine, quindi è unita in alcuni oggetti contenitore, chiamiamola "Task".

Tre casi d'uso:

  1. L'utente A desidera inviare l'utente B a qualche attività, quindi gli invia un collegamento (HTTP) per svolgere alcune attività lì.
  2. L'utente B deve uscire dall'edificio, quindi apre l'attività sul suo dispositivo mobile.
  3. La contabilità vuole addebitare al cliente l'attività, ma utilizza un sistema di contabilità di terze parti che carica automaticamente l'attività / attività con un codice che fa riferimento al REST - API dell'applicazione

Ciascuno dei casi d'uso richiede (o diventa più semplice se) l'agente di avere un identificatore indirizzabile per l'attività e l'attività.


3
correlate: una chiave surrogata dovrebbe mai essere esposta a un utente? "Devi essere pronto per qualsiasi identificatore che è esposto agli utenti / clienti che devono essere cambiati, e cambiare l'identità di una riga in un database e propagare quella modifica a tutte le chiavi esterne sta solo chiedendo di rompere i dati ..."
moscerino

@gnat è ON UPDATE CASCADEstato creato per questo (mysql specifico?), anche se se il problema è la sicurezza, il controllo degli accessi dovrebbe essere sul backend e non fidarsi comunque dell'utente
Izkata

2
@Izkata Sì, tranne quando li fai riferimento in un archivio dati diverso (UserID in LDAP come un semplice esempio), oppure devi recuperare alcuni dati da un backup. moscerino ha un buon punto lì.
Angelo Fuchs,

Puoi pelase approfondire cosa intendi con "esporre"? Un esempio reale potrebbe aiutare. :-)
CodeCaster

"esporre" significa mostrarlo all'utente. (Per utente intendo principalmente un essere umano, ma la domanda sembra valida anche per le macchine)
Angelo Fuchs

Risposte:


38

Inoltre, poiché i miei utenti devono comunque accedere ai dati, dovrò disporre di una chiave pubblica per il mondo esterno da qualche parte nel mezzo.

Esattamente. Prendi l'HTTP senza stato, che altrimenti non saprebbe quale risorsa dovrebbe richiedere: espone l'ID della tua domanda 218306nell'URL. Forse ti stai davvero chiedendo se un identificatore esposto possa essere prevedibile ?

Gli unici posti in cui ho sentito una risposta negativa a questo, hanno usato la logica: "Ma possono cambiare l'ID nell'URL!" . Quindi hanno usato i GUID invece di implementare un'autorizzazione adeguata.

Posso immaginare una situazione in cui non vuoi che i tuoi identificatori siano prevedibili: raccolta di risorse. Se si dispone di un sito che ospita pubblicamente determinate risorse in cui altri potrebbero essere interessanti e le si ospita come /images/n.jpgo /videos/n.mp4dove c'è nsolo un numero crescente, chiunque guardi al traffico da e verso il tuo sito Web può raccogliere tutte le tue risorse.

Quindi, per rispondere direttamente alla tua domanda: no, non è male "esporre" direttamente gli identificatori che hanno un significato solo per il tuo programma, di solito è anche necessario che il tuo programma funzioni correttamente.


2
Gli URL non indelebili (ad esempio contenenti un token crittograficamente casuale a 128 bit) sono una forma di autorizzazione adeguata.
CodesInChaos,

Corretto come in estremamente sensibile agli attacchi replay? È utile per un utilizzo una tantum come un URL di reimpostazione della password, ma meno per identificare una risorsa statica, poiché una volta che il token è aperto, chiunque può utilizzarlo, senza che tu sia in grado di cambiarlo senza rompere alcun riferimento legittimo a esso.
CodeCaster

hm? Ovviamente richiede SSL, ma è il caso, indipendentemente da come autentichi e autorizzi. Su SSL un utente malintenzionato non può apprendere il token (proprio come non può imparare i cookie) e impedisce anche gli attacchi di replay. Il principale svantaggio di questo approccio è che non è possibile revocare l'accesso per i singoli utenti, quindi preferisco usarlo solo per risorse immutabili. La revoca dell'accesso a risorse immutabili non ha senso poiché un utente malintenzionato può semplicemente archiviare una copia locale.
CodesInChaos,

2
In questi giorni mi sembra incapace di esprimere ciò che intendo, mi dispiace. Intendo dire che usare un token casuale per una risorsa statica anziché un ID incrementale va bene, se si desidera che la risorsa sia accessibile al pubblico ma non indovinabile. Per qualsiasi altro uso, preferirei un uso una tantum, a causa della cosa di revoca.
CodeCaster

1
Nessuno, il mio punto esattamente. Puoi forse approfondire cosa intendi con "esporre" allora?
CodeCaster

29

Non dovresti esporlo perché le persone che lo vedono inizieranno a usarlo come loro "numero di conto" che NON è. Ad esempio, per il mio conto bancario so qual è il mio numero di conto. L'ho memorizzato, lo uso al telefono con il servizio clienti, lo uso durante la compilazione di moduli per altre banche per effettuare bonifici, documenti legali, per il mio servizio di pagamento automatico, ecc. Non voglio per cambiare. La chiave primaria (per il mio account) d'altra parte, non lo so o non vedo mai.
Il sistema che lo memorizza cambia nel corso degli anni da un sistema all'altro, attraverso fusioni bancarie, aggiornamenti e sostituzioni di sistema, ecc. Ecc.
Le chiavi primarie possono cambiare attraverso alcune di queste trasformazioni, quindi se non sono mai state esposte, scritte o ricordate da qualsiasi utente normale che "
Le chiavi senza significato commerciale sono spesso definite chiavi surrogate e sono spesso (ma non sempre) utilizzate come chiavi primarie.

tra l'altro, ciò accade anche internamente quando le persone costruiscono interfacce e programmi che usano in modo improprio ed espongono le chiavi primarie e le rendono parte di tali sistemi invece di fare solo una cosa: identificare in modo univoco un record di database internamente. In realtà ho imparato quanto sopra attraverso un periodo di 6 anni a supporto di un sistema di data warehouse in un ospedale.


4
+1 ma quello che stai descrivendo qui è in realtà una chiave surrogata . Non tutte le tabelle hanno una chiave surrogata e anche se lo fa la surrogata potrebbe non essere la chiave "primaria".
nvogel

2
+1 Ho pensato che il numero di conto sarebbe la chiave surrogata, ma ho letto su di esso e tu hai ragione al 100% :)
Michael Durrant

2
+1 esponendolo agli utenti aggiunge requisiti impliciti (ad es. Rimanere statici)
Matt

1
Bella risposta. Il mio modo abbreviato di dire questo è che le chiavi surrogate sono utili perché a nessuno importa di loro e quindi a nessuno importa se le cambi o non le cambi. Se li esponi, le persone inizieranno a preoccuparsi di loro.
JimmyJames,

tl; dr: perché il futuro. Se qualcosa di esterno fa affidamento su una chiave, le cose diventano confuse se l'implementazione cambia in seguito; quindi tienili più o meno nascosti per rendere le cose più facili.
Adam Tolley,

27

Perché le chiavi primarie sono un dettaglio di implementazione.

Se si esegue la migrazione di database, le chiavi primarie potrebbero cambiare a causa dell'ordine di inserimento, della rimozione di vecchi record ... per diversi motivi. Se si esegue la migrazione di piattaforme di database , è possibile che non si disponga più di una chiave primaria effettiva. Esporre il PK sopra il livello di accesso ai dati è un'astrazione che perde, con tutte le preoccupazioni di accoppiamento che ciò comporta.


3
In che modo un livello applicazione identificherà in modo univoco una risorsa da cui recuperare o aggiornare nel livello dati senza una chiave primaria?
CodeCaster

2
@CodeCaster: tramite un set di dati indicizzato univoco o tramite una chiave primaria non pubblica che viene restituita come parte dell'oggetto fornito dal livello di accesso ai dati.
Telastyn,

1
@CodeCaster - Esistono molti modi per creare un token che consente al callback di specificare quale operazione viene eseguita e certamente non tutti passano semplicemente attraverso la chiave primaria.
Telastyn,

2
Ma ciò richiede che il livello dati sappia a quale token appartiene (o si traduce) a quale PK. A me sembra un livello aggiuntivo di complessità inutile, semplicemente per nascondere il PK. A quale scopo serve, oltre a soddisfare l'architetto? Sono d'accordo con il tuo punto, semplicemente non lo trovo applicabile nell'uso del mondo reale e apprezzerei un esempio reale.
CodeCaster

1
@CodeCaster - No, il livello intermedio in realtà fa il suo lavoro e sottrae che esiste un accesso ai dati dall'interfaccia utente. Ci sono molti cattivi architetti al mondo, ma molte delle migliori pratiche di progettazione del programma esistono per un motivo. Alcune app possono correre il rischio di quell'astrazione che perde e altre no.
Telastyn,

10

Questa è una risposta combinata degli altri (ovvero ciò che ho imparato). Se hai voglia di votare questo, dovresti almeno votare uno degli altri così come hanno fatto il lavoro vero e proprio. Se sei più interessato, leggi invece le altre risposte.

Non è necessario esporre la chiave primaria del database ma utilizzare una chiave surrogata

  1. Se vuoi che i tuoi utenti siano in grado di ricordare (almeno un po ') o riconoscere l'identificatore di una voce. ( Risposta di Graystone28s )
  2. Se vuoi pianificare in anticipo e considerare che potresti cambiare i sistemi (database o altro) che probabilmente cambieranno il tuo PK. ( Risposta di Telastyns )
  3. Se vuoi assicurarti che i tuoi utenti abbiano un modo coerente di accedere ai dati che non cambieranno anche se la tua azienda sposta la proprietà e i dati vengono migrati in un sistema completamente diverso. ( Risposta di Michael Durrants )
  4. Se il tuo PK è prevedibile (come una sequenza), il tuo sistema può avere problemi di raccolta delle risorse. ( Risposta di CodeCasters ) Questo si applica solo se il tuo sistema ha informazioni che meritano di essere raccolte e che sono accessibili a chiunque o almeno a qualcuno che abbia un interesse nella raccolta.

Nota: la chiave creata dovrebbe essere (kinda) comprensibile all'uomo ( Sqlvogels Answer ).

Se il sistema non ha bisogno di da 1. a 4. non c'è motivo per non utilizzare il database PK come identificatore pubblico (molte delle risposte). Anche la sicurezza non è un problema qui (molte delle risposte).


8

Uno dei motivi per cui ho scoperto, nel corso del tempo, ho visto gli utenti finali richiedere che il loro identificatore significhi qualcosa (come avere un prefisso o un indicatore dell'anno in cui è stato accettato). Cambiare un PK è difficile, ma un surrogato è molto più facile.

La tua chiave primaria sarà probabilmente qualcosa su cui desideri che il tuo database sia indicizzato per motivi di prestazioni e potresti, per motivi tecnici, in tempo cambiarlo, ad esempio da un numero a un guid ... semplicemente non sai per quali motivi le nuove tecnologie o conoscenze potrebbe guidarti verso il basso. Il tuo pk è il tuo articolo tecnico di dati, la chiave pubblica è per il consumo degli utenti finali.


7
La domanda è: "È male esporre le chiavi primarie?" . La tua risposta: "Gli utenti potrebbero voler avere i propri identificativi" . Non capisco la relazione. Espongo InvoiceNumber, che ha un significato ed è modificabile dal cliente, ma espongo anche InvoiceID, che il mio codice utilizza per identificare in modo univoco la fattura. Non devi (e più spesso non vuoi ) lasciare che la chiave utente sia la chiave di archiviazione. Questa domanda riguarda quest'ultima.
CodeCaster

Penso che questo sia un buon esempio perché se passi alla versione multi-tenant della tua APP, puoi mantenere la stessa sintassi e avere più fatture della stessa InvoiceNumber(per diversi tenant) ma avere chiavi primarie diverse - un punto (tipo di ) menzionato anche nella risposta.
Richiama il

1
@CodeCaster questa domanda riguarda in realtà "perché non vuoi che siano uguali"?
Angelo Fuchs,

In tal caso, vedere la risposta di Telastyns .
CodeCaster

2

Per la maggior parte delle applicazioni è praticamente essenziale esporre le chiavi agli utenti. Per utilizzare un sistema di informazione in modo efficace, gli utenti di quel sistema avranno normalmente bisogno di un modo per identificare le informazioni al suo interno e mettere in relazione tali informazioni con qualcosa nel mondo esterno al database. In termini di database relazionale, questi identificatori sono chiavi.

Un modello di progettazione ben utilizzato è quello di creare una chiave aggiuntiva, puramente "tecnica" per le tabelle del database come mezzo di astrazione. Ad esempio per fornire una chiave stabile (relativamente invariata) in cui una chiave alternativa è soggetta a modifiche. Tali chiavi tecniche in genere non sono esposte agli utenti finali perché ciò compromette l'astrazione prevista dai requisiti dell'utente. Non ha nulla a che fare con la sicurezza.

Il problema / incomprensione implicito nella tua domanda è dovuto all'uso inappropriato del termine chiave primaria . Una chiave primaria è solo una tra diverse chiavi "candidate" (diversi identificatori possibili in una tabella di database). La chiave primaria non richiede necessariamente alcuna proprietà fondamentalmente diversa rispetto a qualsiasi altra chiave, quindi affermazioni e principi di progettazione che si applicano specificamente alle chiavi primarie e non ad altre chiavi sono sempre sospetti e spesso sbagliati.

Dato che di solito è necessario esporre una chiave per l'utente, quale dovrebbe essere quella chiave? Prova a rendere le tue chiavi familiari, semplici e stabili. Familiarità e semplicità rendono le chiavi facili da leggere e ricordare e aiuteranno ad evitare errori nell'inserimento dei dati. Stabilità significa che la chiave cambia di rado, il che aiuta anche a evitare la possibilità di errata identificazione.


1
dipende ... da cosa? Voglio imparare quali sono le ragioni dietro quel concetto generico per sapere quando applicarlo e quando no.
Angelo Fuchs,

1
Ciao cliente, per favore dammi il tuo ID in modo che io possa aiutarti. Certo, è gfds789gxb3456bgfx789fgh98076hytd6734nhg5678nghf875nhgf456. Hmm, che mi dici del tuo social? ... id surrogato
Michael Durrant,

@Michael, risposta aggiornata. È una chiave familiare, semplice e stabile?
nvogel

1

Questo è da un commento sulla risposta di Greystone28 di CodeCaster. È un esempio di ciò che stai dicendo:

Espongo InvoiceNumber, che ha un significato ed è modificabile dal cliente, ma espongo anche InvoiceID, che il mio codice utilizza per identificare in modo univoco la fattura. Non è necessario (e più spesso non si desidera) lasciare che la chiave utente sia la chiave di archiviazione. Questa domanda riguarda quest'ultima.

A cosa serve la tua app diplaying di InvoiceID?

Per esporre, presumo tu intenda che l'utente può vederlo. Esponilo solo se l'utente ne ha bisogno per utilizzare la tua app. Potrebbe essere utilizzato da supporto tecnico o da alcune cose amministrative. Ho lavorato con alcune app che lo fanno. Rende più semplice fornire supporto quando conosco il record specifico in questione.


Le fatture hanno identificatori (numeri) naturali ma solo per quelli che scrivi. E quelli che ottieni? Hanno numeri di fattura ma si sovrappongono (perché due aziende utilizzano la stessa ed entrambe inviano una fattura). In questa situazione il tuo InvoiceID è unico, il numero non lo è e ciò che lo rende unico sarebbe il nome utente che non è un buon identificatore per i dati (troppo a lungo, le modifiche troppo spesso possono contenere caratteri oscuri ...)
Angelo Fuchs

@AngeloNeuschitzer - Se l'utente può identificare in modo univoco una fattura in base al nome e al numero del cliente, l'utente non ha bisogno di InvoiceID PK, ma il database e il codice sottostante possono utilizzarlo. Sono funzioni reciprocamente esclusive.
JeffO,

Vedi i casi 1 - 3 del mio esempio. In nessuno di questi casi il Nome cliente è un modo utile di indirizzare quell'oggetto per l'utente (sia esso umano o macchina). InvoiceID PK è.
Angelo Fuchs,

1

È del tutto normale che le entità abbiano un identificatore univoco esposto al mondo esterno. Per alcuni oggetti potrebbe essere possibile trovare un identificatore che abbia effettivamente un significato (ad esempio il numero di fattura) ma per altri non esiste un identificatore di questo tipo e quindi deve essere generato.

Per motivi di coerenza e leggibilità, trovo una buona pratica per tutte le entità di un sistema usare esattamente lo stesso tipo e nome per il loro identificatore. Normalmente questo identificatore sarebbe esposto ( <type> getId()) in una classe base astratta.

Per lo stesso motivo, ogni servizio nel sistema (ad esempio il servizio di fatturazione) dovrebbe fornire metodi identici per l'accesso alle entità tramite il loro identificatore. Normalmente questo metodo ( findById(<type> id)) sarebbe ereditato da un'interfaccia di servizio generica o da una classe base.

Questo identificatore non deve essere la chiave primaria dell'entità ma può essere una chiave. L'unica cosa che bisogna garantire è che la strategia di generazione delle chiavi produca identificatori ragionevolmente univoci (non necessari universalmente univoci ma almeno all'interno del sistema).

Se il sistema viene successivamente migrato (grande se nella mia esperienza) su un altro database, non è un problema utilizzare una strategia diversa (non basata su chiavi primarie) per creare gli identificatori purché la strategia sia compatibile con quella originale.


Potresti spiegare che cosa nella tua risposta non ha ricevuto risposta negli altri?
Angelo Fuchs,

2
Nella mia risposta non sono d'accordo almeno con i punti 2. e 3. del tuo riassunto. Non penso che questi siano validi motivi per non usare i PK come identificatori di oggetti.
Mutone,

0

La chiave primaria è lì, proprio come un handle per la tupla (record, riga) a cui si tenta di accedere come sviluppatore. Viene anche utilizzato nell'integrità referenziale (vincoli di chiave esterna) e forse ha anche uno o più casi d'uso.

In sostanza, non c'è niente di male nell'esporlo agli utenti o persino agli hacker. Perché non conosco un attacco che utilizza ad esempio la chiave primaria.

Ma nella sicurezza, abbiamo molti principi (che accettiamo e non approviamo) e dobbiamo aderirli:

  1. Il principio del privilegio di locazione
  2. Sicurezza attraverso l'oscurità

E alcuni altri principi. Quello che dicono essenzialmente è che:

Se non hai bisogno di esporre i tuoi dati, perché dovresti farlo?


La parte di handle è dove sono d'accordo. La sicurezza non lo è. Esso potrebbe essere relativo titolo, ma che hanno un tasto interno indipendente, che non è visibile per l'utente non è in gran parte in realtà per la sicurezza. Lo definirei un piacevole effetto collaterale.
JensG,

Perché dovresti: vedi l'esempio che ho aggiunto alla domanda.
Angelo Fuchs,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.