Почему бы не выставить первичный ключ

53

В моем образовании мне говорили, что ошибочно предлагать пользователю фактические первичные ключи (не только ключи БД, но и все первичные средства доступа).

Я всегда думал, что это проблема безопасности (потому что злоумышленник может попытаться прочитать что-то не свое).

Теперь я должен проверить, разрешен ли пользователю доступ в любом случае, так есть ли другая причина?

Кроме того, поскольку мои пользователи в любом случае должны иметь доступ к данным, мне потребуется открытый ключ для внешнего мира где-то посередине. Теперь этот открытый ключ имеет те же проблемы, что и первичный ключ, не так ли?


Был запрос о том, зачем это делать, так что вот один. Имейте в виду, что речь идет о самом принципе, а не только о его применении в этом примере. Ответы на другие ситуации приветствуются.

Приложение (Web, Mobile), которое обрабатывает действия, имеет несколько пользовательских интерфейсов и, по крайней мере, один автоматизированный API-интерфейс для межсистемного взаимодействия (например, бухгалтерия хочет знать, сколько взимать с клиента, исходя из того, что было сделано). Приложение имеет несколько клиентов, поэтому разделение их данных (логически данные хранятся в одной и той же БД) является обязательным требованием системы. Каждый запрос будет проверен на достоверность независимо от того, что.

Активность очень тонкая, поэтому она находится вместе в каком-либо объекте контейнера, давайте назовем его «Задача».

Три варианта использования:

  1. Пользователь А хочет отправить Пользователя Б какой-то Задаче, поэтому он отправляет ему ссылку (HTTP), чтобы выполнить некоторую Активность там.
  2. Пользователь B должен выйти из здания, чтобы открыть задание на своем мобильном устройстве.
  3. Бухгалтерия хочет взимать с клиента плату за задание, но использует стороннюю систему учета, которая автоматически загружает задание / действие с помощью некоторого кода, который ссылается на REST-API приложения

Каждый из сценариев использования требует (или облегчает, если), чтобы агент имел какой-либо адресуемый идентификатор для Задачи и Действия.

Анджело Фукс
источник
3
related: должен ли суррогатный ключ когда-либо предоставляться пользователю? «Вы должны быть готовы к любому идентификатору, который выставляется пользователям / клиентам, нуждающимся в изменении, и изменение идентификатора строки в базе данных и распространение этого изменения на все внешние ключи просто требует разбить данные ...»
Комнат
@gnat ON UPDATE CASCADEбыл создан для этого (специфичен для mysql?), хотя, если проблема заключается в безопасности, тогда проверка доступа должна выполняться на бэкэнде и в любом случае не доверять пользователю
Izkata
2
@Izkata Да, за исключением случаев, когда вы ссылаетесь на них в другом хранилище данных (в качестве простого примера - на UserID в LDAP), или вам необходимо восстановить некоторые данные из резервной копии. у комара есть хорошая точка зрения.
Анджело Фукс
Можете ли вы рассказать о том, что вы имеете в виду под «разоблачением»? Фактический пример может помочь. :-)
CodeCaster
«выставлять» означает показывать его пользователю. (Под пользователем я имею в виду, главным образом, человека, но вопрос, кажется, актуален и для машин)
Анджело Фукс

Ответы:

38

Кроме того, поскольку мои пользователи в любом случае должны иметь доступ к данным, мне потребуется открытый ключ для внешнего мира где-то посередине.

Именно так. Возьмем HTTP без сохранения состояния, который иначе не знал бы, какой ресурс он должен запрашивать: он отображает идентификатор вашего вопроса 218306в URL. Возможно, вам действительно интересно, может ли открытый идентификатор быть предсказуемым ?

Единственные места, где я слышал отрицательный ответ, использовали обоснование: «Но они могут изменить идентификатор в URL!» , Таким образом, они использовали GUID вместо реализации правильной авторизации.

Я могу представить одну ситуацию, когда вы не хотите, чтобы ваши идентификаторы были предсказуемыми: сбор ресурсов. Если у вас есть сайт, на котором публично размещаются определенные ресурсы, которые могут быть интересны другим, и вы размещаете их как, /images/n.jpgили /videos/n.mp4где nчисло увеличивается, любой, кто просматривает трафик на ваш сайт и с него, может собрать все ваши ресурсы.

Итак, чтобы прямо ответить на ваш вопрос: нет, неплохо бы напрямую «выставлять» идентификаторы, которые имеют значение только для вашей программы, обычно это даже требуется для успешной работы вашей программы.

CodeCaster
источник
2
Невозможные ссылки (например, содержащие криптографически случайный 128-битный токен) являются одной из форм правильной авторизации.
CodesInChaos
Собственно как в крайне чувствительных к переигровке атаках? Это удобно для одноразового использования, такого как URL сброса пароля, но не так, чтобы идентифицировать статический ресурс, так как после того, как токен открыт, его может использовать любой, без возможности изменить его, не нарушив никакой законной ссылки на Это.
CodeCaster
гм? Очевидно, что для этого требуется SSL, но это так, как бы вы ни проходили аутентификацию и авторизацию. По протоколу SSL злоумышленник не может выучить токен (точно так же, как он не может выучить куки), а также предотвращает повторные атаки. Основным недостатком этого подхода является то, что вы не можете отозвать доступ для отдельных пользователей, поэтому я предпочитаю использовать его только для неизменных ресурсов. Отзыв доступа к неизменным ресурсам не имеет смысла, поскольку злоумышленник может просто сохранить локальную копию.
CodesInChaos
2
Кажется, в эти дни я не способен выразить то, что имею в виду, извини. Я имею в виду использование случайного токена для статического ресурса в отличие от инкрементного идентификатора - это хорошо, если вы хотите, чтобы ресурс был общедоступным, но не догадывался. Для любого другого использования, хотя я бы предпочел одноразовое использование из-за аннулирования.
CodeCaster
1
Нет, моя точка зрения точно. Можете ли вы уточнить, что вы имеете в виду под «разоблачением» тогда?
CodeCaster
29

Вы не должны выставлять это, потому что люди, которые видят это, начнут использовать это как их «номер счета», который это НЕ. Например, для моего банковского счета я знаю, какой номер моего счета. Я запомнил это, я использую это по телефону со службой поддержки клиентов, я использую это при заполнении форм для других банков, чтобы сделать переводы, для юридических документов, для моей службы автоматической оплаты и т. Д., И т. Д. Я не хочу это изменить. Первичный ключ (для моей учетной записи), с другой стороны, я не знаю или никогда не вижу.
Система, в которой он хранится, с годами изменяется от одной системы к другой посредством банковских слияний, обновлений и замен системы и т. Д.
И т . Д. Первичные ключи могут изменяться в результате некоторых из этих преобразований, поэтому, если они никогда не были раскрыты, записаны или запомнены любым обычным пользователем, который
Ключи, не имеющие делового значения, часто называют суррогатными ключами и часто (но не всегда) используются в качестве первичных ключей.

Кстати, это даже происходит внутри, когда люди создают интерфейсы и программы, которые неправильно используют и выставляют первичные ключи и делают их частью таких систем вместо того, чтобы просто делать одно - однозначно идентифицировать внутреннюю запись базы данных. Я на самом деле узнал об этом через 6 лет поддержки системы хранения данных в больнице.

Майкл Даррант
источник
4
+1, но то, что вы описываете здесь, на самом деле является суррогатным ключом. Не каждая таблица имеет суррогатный ключ, и даже если он имеет суррогатный ключ, он не может быть «первичным» ключом.
nvogel
2
+1 Я думал, что номер счета будет суррогатным ключом, но я прочитал его, и вы на 100% правы :)
Майкл Даррант
2
+1, выставляя его пользователям, добавляет неявные требования (например, оставайтесь неизменными)
Мэтт
1
Отличный ответ. Мой краткий способ сказать, что суррогатные ключи полезны, потому что никто не заботится о них, и поэтому никто не заботится о том, изменили ли вы их или не изменили их. Если вы разоблачите их, люди начнут заботиться о них.
JimmyJames
tl; dr: потому что будущее. Если что-то внешнее полагается на ключ, все становится грязным, если реализация изменится позже; так что держите их более или менее скрытыми, чтобы было легче.
Адам Толи
27

Потому что первичные ключи - это детали реализации.

Если вы переносите базы данных, ваши первичные ключи могут измениться из-за порядка вставки, удаления старых записей ... по нескольким причинам. Если вы переносите платформы баз данных, у вас может вообще не быть фактического первичного ключа. Представление PK над уровнем доступа к данным является утечкой абстракции со всеми вытекающими проблемами связывания.

Telastyn
источник
3
Как прикладной уровень однозначно идентифицирует ресурс, который он хочет получить или обновить на уровне данных без первичного ключа?
CodeCaster
2
@CodeCaster - либо с помощью некоторого уникального индексированного набора данных, либо с помощью непубличного первичного ключа, который возвращается как часть объекта, предоставляемого уровнем доступа к данным.
Теластин
1
@CodeCaster - существует множество способов создать токен, который позволяет обратному вызову указывать, какая операция выполняется, и, конечно, не все из них просто пропускают первичный ключ.
Теластин
2
Но для этого требуется, чтобы уровень данных знал, какой токен принадлежит (или переводится) какому PK. Для меня это звучит как дополнительный слой ненужной сложности, просто ради сокрытия ПК. Какой цели это служит, кроме удовлетворения архитектора? Я согласен с вашей точкой зрения, я просто не нахожу ее применимой в реальных условиях и был бы признателен за реальный пример.
CodeCaster
1
@CodeCaster - Нет, средний уровень на самом деле делает свою работу и дает понять, что доступ к данным вообще возможен из пользовательского интерфейса. В мире много плохих архитекторов, но многие из лучших практик разработки программ существуют по определенной причине. Некоторые приложения могут рисковать этой утечкой, а некоторые - нет.
Теластин
10

Это комбинированный ответ других (иначе, что я узнал). Если вы чувствуете, что хотите проголосовать против этого, вы должны, по крайней мере, проголосовать за других, так как они сделали настоящую работу. Если вас больше интересует, прочитайте другие ответы.

Вы не должны выставлять первичный ключ базы данных, а вместо этого использовать суррогатный ключ

  1. Если вы хотите, чтобы ваши пользователи могли запомнить (хотя бы немного) или распознать идентификатор записи. ( Graystone28s Ответ )
  2. Если вы хотите запланировать заранее и подумать, что вы можете изменить системы (базы данных или иным образом), которые, вероятно, изменят ваш ПК. ( Теластинс Ответ )
  3. Если вы хотите, чтобы у ваших пользователей был постоянный доступ к данным, который не изменится, даже если ваша компания меняет владельца, и данные полностью переносятся в другую систему. ( Майкл Даррантс Ответ )
  4. Если ваш PK предсказуем (например, последовательность), ваша система может столкнуться с проблемами сбора ресурсов. ( Ответ CodeCasters ) Это применимо только в том случае, если в вашей системе есть информация, которую стоит собрать и которая доступна любому или, по крайней мере, кому-то, кто имеет интерес к добыче.

Примечание. Созданный вами ключ должен быть (как бы) понятен человеку ( ответ Sqlvogels ).

Если вашей системе не нужны цифры от 1 до 4., то нет причин не использовать базы данных PK в качестве вашего открытого идентификатора (несколько ответов). Также безопасность не является проблемой здесь (несколько ответов).

Анджело Фукс
источник
8

Одна причина, которую я обнаружил, это то, что конечный пользователь просил, чтобы его идентификатор что-то значил (например, наличие префикса или указатель года, в который он был введен). Сменить ПК сложно, но суррогат гораздо проще.

Ваш первичный ключ, вероятно, будет тем, что вы хотите, чтобы ваша база данных индексировалась по соображениям производительности, и вы можете со временем по техническим причинам изменить его, например, с номера на guid ... вы просто не знаете, по каким причинам новые технологии или знания может вести вас вниз. Ваш ПК - это ваш технический элемент данных, открытый ключ предназначен для конечных пользователей.

Уэйн М
источник
7
Вопрос такой: «Разве плохо выставлять первичные ключи?» , Ваш ответ: «Пользователи могут захотеть иметь свои собственные идентификаторы» . Я не понимаю отношения. Я предоставляю информацию InvoiceNumber, которая имеет значение и может изменяться клиентом, но я также раскрываю информацию InvoiceID, которую мой код использует для уникальной идентификации счета. Вам не нужно (и чаще всего не хотите ), чтобы ключ пользователя был ключом хранилища. Этот вопрос о последнем.
CodeCaster
Я думаю, что это хороший пример, потому что если вы переходите на мультитенантную версию своего приложения, вы можете сохранить тот же синтаксис и иметь несколько одинаковых счетов-фактур InvoiceNumber(для разных арендаторов), но иметь разные первичные ключи - точку (вид ) также упоминается в ответе.
отшельник
1
@CodeCaster этот вопрос на самом деле о "почему вы не хотите, чтобы они были одинаковыми"?
Анджело Фукс
В этом случае см. Теластинс ответ .
CodeCaster
2

Для большинства приложений очень важно, чтобы вы действительно открывали ключи для пользователей. Чтобы эффективно использовать информационную систему, пользователям этой системы обычно требуется способ идентифицировать информацию в ней и связать эту информацию с чем-то в мире вне базы данных. В терминах реляционной базы данных эти идентификаторы являются ключами.

Один из широко используемых шаблонов проектирования - это создание дополнительного, чисто «технического» ключа для таблиц базы данных в качестве средства абстракции. Например, чтобы обеспечить стабильный (относительно неизменный) ключ, в котором некоторые альтернативные ключи могут быть изменены. Такие технические ключи обычно не предоставляются конечным пользователям, потому что это подрывает предполагаемую абстракцию от требований пользователя. Это не имеет ничего общего с безопасностью.

Проблема / недоразумение, скрытое в вашем вопросе, связано с неправильным использованием термина первичный ключ. Первичный ключ - это только один из нескольких ключей-кандидатов (несколько возможных идентификаторов в таблице базы данных). Первичный ключ не обязательно требует какого-либо принципиально отличного свойства от любого другого ключа, поэтому утверждения и принципы разработки, которые применяются конкретно к первичным ключам, а не к другим ключам, всегда подозрительны и часто ошибочны.

Учитывая, что вам обычно нужно предоставлять ключ вашему пользователю, каким должен быть этот ключ? Постарайтесь сделать ваши ключи привычными, простыми и стабильными. Знакомство и простота делают ключи легкими для чтения и запоминания и помогут избежать ошибок при вводе данных. Стабильность означает, что ключ меняется нечасто, что также помогает избежать возможности ошибочной идентификации.

nvogel
источник
1
это зависит ... от чего? Я хочу узнать, каковы причины этой общей концепции, чтобы знать, когда ее применять, а когда нет.
Анджело Фукс
1
Привет, клиент, пожалуйста, дайте мне свой идентификатор, чтобы я мог помочь вам. Конечно, это gfds789gxb3456bgfx789fgh98076hytd6734nhg5678nghf875nhgf456. Хм, а как насчет твоего общения? ... суррогатное удостоверение личности
Майкл Даррант
@ Майкл, Ответ обновлен. Это знакомый, простой и стабильный ключ?
nvogel
1

Это из комментария на ответ Greystone28 от CodeCaster. Это пример того, что вы говорите:

Я раскрываю InvoiceNumber, который имеет значение и может изменяться клиентом, но я также раскрываю InvoiceID, который мой код использует для уникальной идентификации счета. Вам не нужно (и чаще всего не хотите), чтобы ключ пользователя был ключом хранилища. Этот вопрос о последнем.

Какую цель в вашем приложении выполняет воспроизведение InvoiceID?

Под разоблачением я предполагаю, что вы имеете в виду, что пользователь может видеть это. Выставляйте его только в том случае, если он нужен пользователю для использования вашего приложения. Это может быть использовано технической поддержкой или некоторыми административными сотрудниками. Я работал с несколькими приложениями, которые делают это. Это облегчает оказание поддержки, когда я знаю конкретную запись, о которой идет речь.

JeffO
источник
Счета имеют естественные идентификаторы (числа), но только для тех, которые вы пишете. Как насчет тех, которые вы получаете? У них есть InvoiceNumbers, но они перекрываются (потому что две компании используют одну и ту же, и обе отправляют вам счет-фактуру). В этой ситуации ваш InvoiceID является уникальным, а Number - нет, и то, что делает его уникальным, - это имя клиента, которое не является хорошим идентификатором данных (слишком длинный, слишком часто изменяется, может содержать неясные символы ...)
Анджело Фукс,
@AngeloNeuschitzer - Если пользователь может однозначно идентифицировать счет по имени и номеру клиента, ему не нужен PK InvoiceID, но база данных и соответствующий код могут его использовать. Это взаимоисключающие функции.
JeffO
Смотрите случаи 1 - 3 моего примера. Ни в одном из этих случаев Имя клиента не является полезным способом обращения к этому Объекту для Пользователя (будь то человек или машина). InvoiceID ПК есть.
Анджело Фукс
1

Это совершенно нормально для сущностей, имеющих уникальный идентификатор, который выставляется внешнему миру. Для некоторых объектов может быть возможно найти идентификатор, который на самом деле имеет значение (например, номер счета), но для других такой идентификатор не существует и, следовательно, его необходимо сгенерировать.

Для согласованности и читабельности я считаю хорошей практикой для всех сущностей в системе использовать один и тот же тип и имя для своего идентификатора. Обычно этот идентификатор будет выставлен ( <type> getId()) в некотором абстрактном базовом классе.

По той же причине каждая служба в системе (например, служба выставления счетов) должна предоставлять идентичные методы для доступа к объектам по их идентификатору. Обычно этот метод ( findById(<type> id)) наследуется от универсального интерфейса службы или базового класса.

Этот идентификатор не обязательно должен быть первичным ключом объекта, но он может быть одним. Единственное, что нужно гарантировать, - это то, что стратегия генерации ключей создает разумно уникальные идентификаторы (необязательно универсально уникальные, но, по крайней мере, внутри системы).

Если впоследствии система будет перенесена (если в моем случае это будет большой объем) в другую базу данных, то не составит труда использовать другую стратегию (не основанную на первичных ключах) для создания идентификаторов, если стратегия совместима с исходной.

Мутон
источник
Не могли бы вы объяснить, что в вашем ответе не было ответа в других?
Анджело Фукс
2
В своем ответе я не согласен хотя бы с пунктами 2. и 3. вашего резюме. Я не думаю, что это веские причины не использовать PK в качестве идентификаторов объектов.
Мутон
0

Здесь есть первичный ключ, как дескриптор кортежа (запись, строка), к которому вы пытаетесь обратиться как разработчик. Он также используется в ссылочной целостности (ограничения внешнего ключа), и, возможно, он также имеет один или несколько вариантов использования.

По сути, нет ничего плохого в том, чтобы показывать его пользователям или даже хакерам. Потому что я не знаю атаки, которая использует первичный ключ, например.

Но в области безопасности у нас есть много принципов (которые мы принимаем и не одобряем), и нам нужно их придерживаться:

  1. Принцип арендной льготы
  2. Безопасность через неизвестность

И некоторые другие принципы. По сути, они говорят, что:

Если вам не нужно раскрывать свои данные, зачем вам вообще?

Саид Нямати
источник
Часть ручки - то, где я согласен. Безопасность не является. Это может иметь отношение к безопасности, но наличие независимого внутреннего ключа, который не виден пользователю, в основном не относится к безопасности. Я бы назвал это приятным побочным эффектом.
JensG
Зачем вам: посмотрите пример, который я добавил к вопросу.
Анджело Фукс