Por que não expor uma chave primária

53

Na minha educação, fui informado de que é uma idéia falha expor as chaves primárias reais (não apenas as chaves do banco de dados, mas todos os acessadores primários) ao usuário.

Eu sempre pensei que fosse um problema de segurança (porque um invasor poderia tentar ler coisas que não eram suas).

Agora eu tenho que verificar se o usuário tem permissão para acessar de qualquer maneira, existe uma razão diferente por trás disso?

Além disso, como meus usuários precisam acessar os dados de qualquer maneira, precisarei ter uma chave pública para o mundo exterior em algum lugar no meio. Agora essa chave pública tem os mesmos problemas que a chave primária, não é?


Houve a solicitação de um exemplo sobre por que fazer isso de qualquer maneira, então aqui está um. Lembre-se de que a pergunta deve ser sobre o próprio princípio, não apenas se for aplicável neste exemplo. As respostas para outras situações são explicitamente bem-vindas.

O aplicativo (Web, celular) que lida com atividades, possui várias interfaces de usuário e pelo menos uma API automatizada para comunicação entre sistemas (por exemplo, o departamento de contabilidade deseja saber quanto cobrar do cliente com base no que foi feito). O aplicativo tem vários clientes, portanto a separação de seus dados (logicamente, os dados são armazenados no mesmo banco de dados) é essencial no sistema. Cada solicitação será verificada quanto à validade, não importa o quê.

A atividade é granular muito fina e, portanto, fica junto em algum objeto contêiner, vamos chamá-lo de "Tarefa".

Três casos de uso:

  1. O usuário A deseja enviar o usuário B para alguma tarefa, para que ele envie um link (HTTP) para realizar alguma atividade lá.
  2. O usuário B precisa sair do prédio para abrir a tarefa em seu dispositivo móvel.
  3. A contabilidade deseja cobrar do cliente pela tarefa, mas usa um sistema de contabilidade de terceiros que carrega automaticamente a tarefa / atividade por algum código que se refere à API REST do aplicativo

Cada um dos casos de uso exige (ou fica mais fácil se) o agente possuir algum identificador endereçável para a tarefa e a atividade.

Angelo Fuchs
fonte
3
relacionados: uma chave substituta deve ser exposta a um usuário? "Você precisa estar pronto para qualquer identificador exposto a usuários / clientes que precisem ser alterados, e alterar a identidade de uma linha em um banco de dados e propagá-la para todas as chaves estrangeiras é apenas pedir para quebrar os dados ..."
gnat
@gnat ON UPDATE CASCADEfoi feito para que, embora se o problema é a segurança, em seguida, a verificação de acesso deve estar no backend e não confiar o usuário de qualquer maneira (específico mysql?)
Izkata
2
@ Izkata Sim, exceto quando você os referencia em um armazenamento de dados diferente (UserID no LDAP como um exemplo simples), ou você precisa recuperar alguns dados de um backup. mosquito tem um bom argumento lá.
Angelo Fuchs
Você pode explicar o que você quer dizer com "expor"? Um exemplo real pode ajudar. :-)
CodeCaster 13/11
"expor" significa mostrá-lo ao usuário. (Por usuário eu quero dizer um ser humano em sua maioria, mas a questão parece válido para máquinas também)
Angelo Fuchs

Respostas:

38

Além disso, como meus usuários precisam acessar os dados de qualquer maneira, precisarei ter uma chave pública para o mundo exterior em algum lugar no meio.

Exatamente. Pegue o HTTP sem estado, que de outra forma não saberia qual recurso deve solicitar: expõe o ID da sua pergunta 218306no URL. Talvez você esteja realmente se perguntando se um identificador exposto pode ser previsível ?

Os únicos lugares onde ouvi uma resposta negativa a isso, usaram a lógica: "Mas eles podem alterar o ID no URL!" . Então, eles usaram GUIDs em vez de implementar a autorização adequada.

Posso imaginar uma situação em que você não deseja que seus identificadores sejam previsíveis: coleta de recursos. Se você possui um site que hospeda publicamente determinados recursos, outros podem ser interessantes e você os hospeda como /images/n.jpgou /videos/n.mp4onde nhá um número crescente, qualquer pessoa olhando o tráfego de e para o seu site pode coletar todos os seus recursos.

Portanto, para responder diretamente à sua pergunta: não, não é ruim "expor" identificadores diretamente que só têm significado para o seu programa, geralmente é necessário que seu programa funcione com êxito.

CodeCaster
fonte
2
URLs indizíveis (por exemplo, contendo um token criptograficamente aleatório de 128 bits) são uma forma de autorização adequada.
CodesInChaos
Adequado como extremamente sensível a ataques de repetição? É bom para um uso único como um URL de redefinição de senha, mas menos para identificar um recurso estático, pois assim que o token é aberto, qualquer pessoa pode usá-lo, sem que você possa alterá-lo sem quebrar nenhuma referência legítima a isto.
CodeCaster
hm? Obviamente, ele requer SSL, mas é esse o caso, independentemente de como você se autentica e autoriza. Sobre SSL, um invasor não pode aprender o token (assim como não pode aprender cookies) e também evita ataques de repetição. A principal desvantagem dessa abordagem é que você não pode revogar o acesso de usuários individuais; portanto, prefiro usá-lo apenas para recursos imutáveis. Revogar o acesso a recursos imutáveis ​​não faz sentido, pois um invasor pode simplesmente armazenar uma cópia local.
CodesInChaos
2
Hoje em dia, pareço ser incapaz de realmente expressar o que quero dizer. Quero dizer, usar um token aleatório para um recurso estático em vez de um ID incremental é bom, se você deseja que o recurso seja acessível ao público, mas não seja possível adivinhar. Para qualquer outro uso, eu prefiro o uso único, por causa da revogação.
CodeCaster
11
Nenhum, meu ponto exatamente. Talvez você possa elaborar o que você quer dizer com "expor" então?
CodeCaster 13/11
29

Você não deve expô-lo porque as pessoas que o veem começarão a usá-lo como seu 'número da conta', o que NÃO é. Por exemplo, para minha conta bancária, eu sei qual é o número da minha conta. Memorizei, uso no telefone com o atendimento ao cliente, uso para preencher formulários de outros bancos para fazer transferências, documentos legais, serviço de pagamento automático, etc. Não quero para mudar. A chave primária (para minha conta), por outro lado, eu não sei ou nunca vejo.
O sistema que o armazena muda ao longo dos anos de um sistema para outro, por meio de fusões bancárias, atualizações e substituições do sistema, etc. etc.
As chaves primárias podem ser alteradas por algumas dessas transformações, portanto, se nunca forem expostas, anotadas ou lembradas por qualquer usuário comum que '
Chaves sem significado comercial são freqüentemente denominadas chaves substitutas e são frequentemente (mas nem sempre) usadas como chaves primárias.

Aliás, isso acontece internamente quando as pessoas constroem interfaces e programas que usam indevidamente e expõem chaves primárias e os fazem parte desses sistemas, em vez de apenas fazerem uma coisa: identificar exclusivamente um registro de banco de dados internamente. Na verdade, eu aprendi o que foi dito acima por um período de 6 anos apoiando um sistema de data warehouse em um hospital.

Michael Durrant
fonte
4
+1, mas o que você está descrevendo aqui é na verdade uma chave substituta . Nem todas as tabelas possuem uma chave substituta e, mesmo que possua, a substituta pode não ser a chave "primária".
Nvogel
2
+1 Eu pensei que o número de conta seria a chave substituta, mas eu ler sobre ele e você está 100% correto :)
Michael Durrant
2
+1 expô-lo aos usuários acrescentar exigências implícitas (por exemplo, permanecer estático)
Matt
11
Ótima resposta. Minha maneira abreviada de dizer isso é que as chaves substitutas são úteis porque ninguém se importa com elas e, portanto, ninguém se importa se você as altera ou não. Se você os expuser, as pessoas começarão a se preocupar com eles.
JimmyJames
tl; dr: porque o futuro. Se algo externo passa a depender de uma chave, as coisas ficam confusas se a implementação mudar mais tarde; portanto, mantenha-os mais ou menos ocultos para facilitar as coisas.
Adam Tolley
27

Porque Chaves Primárias são um detalhe de implementação.

Se você migrar bancos de dados, suas chaves primárias podem mudar devido à ordem de inserção, remoção de registros antigos ... por alguns motivos diferentes. Se você migrar plataformas de banco de dados , poderá não ter mais uma chave primária real. Expor a PK acima da camada de acesso a dados é uma abstração com vazamento, com todas as preocupações de acoplamento que isso implica.

Telastyn
fonte
3
Como uma camada de aplicativo identificará exclusivamente um recurso que deseja recuperar ou atualizar na camada de dados sem uma chave primária?
CodeCaster 13/11
2
@CodeCaster - por um conjunto exclusivo de dados indexados ou por uma chave primária não pública que é retornada como parte do objeto fornecido pela camada de acesso a dados.
Telastyn
11
@CodeCaster - Existem várias maneiras de criar um token que permite que o retorno de chamada especifique qual operação está sendo executada, e certamente nem todos eles apenas passam a chave primária.
Telastyn #
2
Mas isso requer que a camada de dados saiba a qual token pertence (ou se traduz) a qual PK. Para mim, isso parece uma camada adicional de complexidade desnecessária, apenas para esconder o PK. Que finalidade isso serve, além de satisfazer o arquiteto? Eu concordo com o seu argumento, apenas não o acho aplicável no uso no mundo real e gostaria de receber um exemplo real.
CodeCaster
11
@ CodeCaster - Não, a camada intermediária realmente faz seu trabalho e resume que há acesso a dados da interface do usuário. Existem muitos arquitetos ruins no mundo, mas muitas das melhores práticas de design de programas existem por um motivo. Alguns aplicativos podem correr o risco dessa abstração com vazamento e outros não.
Telastyn #
10

Esta é uma resposta combinada dos outros (aka. O que eu aprendi). Se você deseja votar neste, você deve pelo menos votar em um dos outros, assim como eles fizeram o trabalho real. Se você estiver mais interessado, leia as outras respostas.

Você não deve expor a chave primária do banco de dados, mas usar uma chave substituta

  1. Se você deseja que seus usuários possam se lembrar (pelo menos um pouco) ou reconhecer o identificador de uma entrada. ( Resposta Graystone28s )
  2. Se você deseja planejar com antecedência e considerar que pode alterar os sistemas (banco de dados ou outros) que provavelmente mudarão seu PK. ( Resposta Telastyns )
  3. Se você deseja garantir que seus usuários tenham uma maneira consistente de acessar dados que não serão alterados, mesmo que sua empresa mude de propriedade e os dados sejam milhares migrados para um sistema completamente diferente. ( Resposta de Michael Durrants )
  4. Se sua PK for previsível (como uma sequência), seu sistema poderá sofrer problemas de coleta de recursos. ( Resposta do CodeCasters ) Isso só se aplica se o seu sistema tiver informações que valem a pena colher e que sejam acessíveis por qualquer pessoa ou pelo menos alguém que tenha interesse em colher.

Nota: Sua chave criada deve ser (um pouco) compreensível por humanos ( resposta do Sqlvogels ).

Se o seu sistema não precisa de 1. a 4., não há razão para não usar o PK dos bancos de dados como seu identificador público (várias das respostas). Além disso, a segurança não é um problema aqui (várias das respostas).

Angelo Fuchs
fonte
8

Uma das razões pelas quais descobri que, na totalidade do tempo, vi os usuários finais solicitarem que seu identificador significasse algo (como ter um prefixo ou um indicador do ano em que foi aceito). Alterar uma PK é difícil, mas um substituto é muito mais fácil.

Sua chave primária provavelmente será algo em que você deseja indexar seu banco de dados por razões de desempenho, e você poderá, a tempo, por motivos técnicos, alterá-la, por exemplo, de um número para um guia ... você simplesmente não sabe por que novas tecnologias ou conhecimentos pode guiá-lo para baixo. Seu pk é seu item técnico de dados, a chave pública é para consumo dos usuários finais.

Wayne M
fonte
7
A pergunta é: "É ruim expor chaves primárias?" . Sua resposta: "Os usuários podem querer ter seus próprios identificadores" . Eu não entendo a relação. Exponho InvoiceNumber, que tem um significado e é alterável pelo cliente, mas também o exponho InvoiceID, que meu código usa para identificar exclusivamente a fatura. Você não precisa (e nem sempre quer ) permitir que a chave do usuário seja a chave de armazenamento. Esta questão é sobre o último.
CodeCaster
Acho que este é um bom exemplo, porque se você mudar para a versão multi-inquilino do seu APP, poderá manter a mesma sintaxe e ter várias faturas iguais InvoiceNumber(para inquilinos diferentes), mas com chaves primárias diferentes - um ponto (tipo de ) mencionados na resposta também.
recluze
11
@ CodeCaster, esta pergunta é realmente sobre "por que você não quer que eles sejam os mesmos"?
Angelo Fuchs
Nesse caso, veja Telastyns responder .
CodeCaster 13/11
2

Para a maioria dos aplicativos, é essencial que você exponha as chaves aos usuários. Para usar um sistema de informações de maneira eficaz, os usuários desse sistema normalmente precisam de uma maneira de identificar as informações nele contidas e relacioná-las com algo no mundo fora do banco de dados. Em termos de banco de dados relacional, esses identificadores são chaves.

Um padrão de design bem usado é criar uma chave adicional, puramente "técnica" para tabelas de banco de dados como um meio de abstração. Por exemplo, para fornecer uma chave estável (relativamente imutável) onde alguma chave alternativa está sujeita a alterações. Essas chaves técnicas normalmente não são expostas aos usuários finais, pois isso prejudica a abstração pretendida dos requisitos do usuário. Não tem nada a ver com segurança.

O problema / mal-entendido implícito na sua pergunta ocorre devido ao uso inadequado do termo chave primária . Uma chave primária é apenas uma dentre várias chaves "candidatas" (vários identificadores possíveis em uma tabela de banco de dados). A chave primária não requer necessariamente nenhuma propriedade fundamentalmente diferente de qualquer outra chave; portanto, asserções e princípios de design que se aplicam especificamente a chaves primárias e não a outras chaves são sempre suspeitos e geralmente errados.

Como geralmente você precisa expor uma chave ao usuário, qual deve ser essa chave? Tente tornar suas chaves familiares, simples e estáveis. A familiaridade e a simplicidade tornam as chaves fáceis de ler e lembrar e ajudarão a evitar erros de entrada de dados. Estabilidade significa que a chave muda com pouca frequência, o que também ajuda a evitar a possibilidade de identificação incorreta.

nvogel
fonte
11
depende ... de que? Quero saber quais são as razões por trás desse conceito genérico para saber quando aplicá-lo e quando não.
Angelo Fuchs
11
Oi cliente, por favor, me dê seu ID para que eu possa ajudá-lo. Claro, seus gfds789gxb3456bgfx789fgh98076hytd6734nhg5678nghf875nhgf456. Hmm, e o seu social? ... identificação substituta
Michael Durrant
@ Michael, resposta atualizada. Essa é uma chave familiar, simples e estável?
Nvogel
1

Isto é de um comentário na resposta de Greystone28 pelo CodeCaster. É um exemplo do que você está dizendo:

Exponho InvoiceNumber, que tem um significado e é alterável pelo cliente, mas também exponho InvoiceID, que meu código usa para identificar exclusivamente a fatura. Você não precisa (e nem sempre quer) permitir que a chave do usuário seja a chave de armazenamento. Esta questão é sobre o último.

Qual é a finalidade do seu aplicativo para exibir o InvoiceID?

Ao expor, suponho que você queira dizer que o usuário pode vê-lo. Só o exponha se o usuário precisar dele para usar seu aplicativo. Pode ser usado por suporte técnico ou algum material administrativo. Eu trabalhei com alguns aplicativos que fazem isso. Facilita o suporte quando conheço o registro específico em questão.

JeffO
fonte
As faturas possuem identificadores naturais (números), mas apenas os que você escreve. E os que você recebe? Eles têm InvoiceNumbers, mas se sobrepõem (porque duas empresas usam o mesmo e ambas enviam uma fatura). Nessa situação, seu InvoiceID é único, o número não é e o que o torna único seria o nome personalizado, que não é um bom identificador de dados (muito tempo, alterações com muita frequência, podem conter caracteres obscuros ...)
Angelo Fuchs
@AngeloNeuschitzer - Se o usuário puder identificar exclusivamente uma fatura por nome e número do cliente, não precisará do PK do InvoiceID, mas o banco de dados e o código subjacente poderão usá-lo. São funções mutuamente exclusivas.
111313 JeffO
Veja os casos 1 - 3 do meu exemplo. Em nenhum desses casos, o Nome do Cliente é uma maneira útil de endereçar esse Objeto para o Usuário (seja humano ou máquina). InvoiceID PK é.
Angelo Fuchs
1

É completamente normal que as entidades tenham um identificador exclusivo exposto ao mundo exterior. Para alguns objetos, pode ser possível encontrar um identificador que realmente tenha um significado (por exemplo, número da fatura), mas para outros, esse identificador não existe e, portanto, deve ser gerado.

Por uma questão de consistência e legibilidade, acho uma boa prática para todas as entidades em um sistema usar exatamente o mesmo tipo e nome para seu identificador. Normalmente esse identificador seria exposto ( <type> getId()) em alguma classe base abstrata.

Pelo mesmo motivo, cada serviço no sistema (por exemplo, serviço de fatura) deve fornecer métodos idênticos para acessar entidades por seu identificador. Normalmente esse método ( findById(<type> id)) seria herdado de uma interface de serviço genérica ou classe base.

Esse identificador não precisa ser a chave primária da entidade, mas pode ser uma. A única coisa que é preciso garantir é que a estratégia de geração de chaves produza identificadores razoavelmente únicos (não necessários universalmente únicos, mas pelo menos dentro do sistema).

Se o sistema for migrado posteriormente (grande na minha experiência) para outro banco de dados, não será um problema usar uma estratégia diferente (não baseada em chaves primárias) para criar os identificadores, desde que a estratégia seja compatível com a original.

Muton
fonte
Você poderia explicar o que na sua resposta não foi respondido nos outros?
Angelo Fuchs
2
Na minha resposta, discordo, pelo menos, dos pontos 2. e 3. do seu resumo. Eu não acho que esses são motivos válidos para não usar PKs como identificadores de objeto.
Muton
0

A chave primária está lá, como um identificador para a tupla (registro, linha) que você tenta acessar como desenvolvedor. Também é usado em integridade referencial (restrições de chave estrangeira) e talvez também tenha um ou mais casos de uso.

Essencialmente, não há nada de ruim em expô-lo aos usuários ou até hackers. Porque não conheço um ataque que use a chave primária, por exemplo.

Mas em segurança, temos muitos princípios (que aceitamos e não aprovamos) e precisamos segui-los:

  1. O princípio do privilégio de locação
  2. Segurança através da obscuridade

E alguns outros princípios. O que eles dizem essencialmente é que:

Se você não precisa expor seus dados, por que precisaria?

Saeed Neamati
fonte
A parte do identificador é onde eu concordo. A segurança não é. Ele pode ser de segurança relevantes, mas ter uma chave interna independente, que não é visível para o usuário em sua maioria não é realmente sobre segurança. Eu chamaria isso de um bom efeito colateral.
JensG
Por que você: veja o exemplo que adicionei à pergunta.
Angelo Fuchs