Безопасность базы данных: Посредник "to_be_deleted" столбец / таблица? - PullRequest
0 голосов
/ 19 сентября 2008

Каждый случайно забыл предложение WHERE в запросе DELETE и один или два раза обработал некоторые данные без резервного копирования. Я размышлял над этой проблемой, и мне было интересно, является ли решение, которое я нашел, практичным.

Что, если вместо реальных DELETE запросов сценарии приложения и обслуживания выполняли что-то вроде:

UPDATE foo SET to_be_deleted=1 WHERE blah = 50;

А потом задание cron было выполнено и фактически удалено все с флагом? Недостатком было бы то, что почти к каждому другому запросу нужно было бы добавить WHERE to_be_deleted != 1, но с другой стороны, вы никогда больше не ошибочно потеряете данные. Вы могли видеть «затронуто 2 349 325 строк» ​​и сказать: «Хм, похоже, я забыл предложение WHERE» и сбросить флаги. Вы даже можете сделать поле to_be_deleted столбцом DATE, чтобы задание cron проверило, пришло ли время строки.

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

Итак, мой вопрос: это хорошая идея или есть подводные камни, которых я не вижу?

Ответы [ 14 ]

4 голосов
/ 19 сентября 2008

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

Когда я работаю над производственной базой данных, я помещаю ВСЕ, что я делаю, в транзакцию, поэтому, если я испорчу, я могу откатиться. Мне просто помогла обычная практика, подобная этой.

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

2 голосов
/ 20 сентября 2008

Каждый случайно забыл предложение WHERE в запросе DELETE и взорвали некоторые данные без резервного копирования один раз или в два раза.

Нет. Я всегда прототипирую свои DELETE s как SELECT s, и только если последний дает результаты, которые я хочу удалить, измените оператор перед WHERE на DELETE. Это позволяет мне в любой необходимой детализации проверять строки, на которые я хочу повлиять, прежде чем что-либо делать.

2 голосов
/ 19 сентября 2008

Похоже, вы описываете три случая здесь.

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

  2. Случай 2 - SQL-инъекция. Это проблема архитектуры. Ваше приложение не должно передавать SQL в базу данных, доступ должен контролироваться через пакеты / хранимые процедуры / функции, а значения, которые собираются поступить из пользовательского интерфейса и использоваться в выражении DDL, должны применяться с использованием переменных связывания, а не создание динамического SQL путем добавления строк вместе.

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

2 голосов
/ 19 сентября 2008

Может быть проще создать параллельную таблицу для удаленных строк. Триггер DELETEUPDATE также, если вы также хотите отменить изменения) в исходной таблице может скопировать затронутые строки в параллельную таблицу. Добавление столбца datetime в параллельную таблицу для записи даты и времени изменения позволит вам навсегда удалить строки после определенного возраста, используя задание cron.

Таким образом, вы будете использовать обычные операторы DELETE в исходной таблице, поэтому вы не забудете запустить свой специальный оператор "DELETE". Вы также обойдете выражение to_be_deleted != 1, которое представляет собой ошибку, ожидающую, когда кто-то неизбежно забудет.

2 голосов
/ 19 сентября 2008

Слишком сложно. Стандартный подход заключается в том, чтобы выполнить всю работу внутри транзакции, поэтому, если вы облажаетесь и забываете предложение WHERE, вы просто откатываетесь, когда видите результат «2 349 325 строк затронут».

2 голосов
/ 19 сентября 2008

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

Также ... что произойдет, если следующий запрос будет выполнен "UPDATE foo SET to_be_deleted = 1", потому что они исключили предложение where. Если у вас нет проверяющих столбцов с отметкой времени, как узнать, какие столбцы были удалены, а какие были сделаны по ошибке? Но даже если у вас есть столбцы аудита с отметкой времени, если аудит выполняется с помощью хранимой процедуры или соглашения программиста, тогда эти внутренние запросы могут не предоставлять информацию, сообщающую, что они только что были применены.

2 голосов
/ 19 сентября 2008

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

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

1 голос
/ 19 сентября 2008

Вы можете настроить представление для этой таблицы, которое выбирает WHERE to_be_deleted! = 1, и все ваши обычные операции выбора выполняются в этом представлении, что позволяет избежать необходимости ставить WHERE на все ваши запросы.

0 голосов
/ 19 сентября 2008

Вот почему всякий раз, когда вы редактируете данные вручную, вы должны НАЧАТЬ ТРАНС, отредактировать свои данные, убедиться, что они выглядят хорошо (например, вы не удалили больше данных, чем ожидали), а затем ЗАКРЫТЬ Если вы используете Postgres, то вы также хотите создать множество точек сохранения, чтобы опечатка не уничтожала вашу промежуточную работу.

Но, тем не менее, во многих приложениях имеет смысл иметь записи программного обеспечения как недействительные, а не удалять их. Добавьте дату last_modified, которая автоматически обновляется, и вы все готовы настроить инкрементные обновления в хранилище данных. Даже если у вас нет хранилища данных сейчас , подготовка к будущему никогда не повредит, если подготовка дешевая. Кроме того, в случае ручных ошибок у вас все еще есть данные, и вы можете просто найти все записи, которые были «удалены», когда вы сделали свою ошибку, и исправить их. (Вы все равно должны использовать транзакции.)

0 голосов
/ 19 сентября 2008

Другой вариант - создать триггер удаления для каждой таблицы. Когда что-либо удаляется, он вставляет эту «подлежащую удалению» запись в другую таблицу, в идеале с именем TABLENAME_deleted.

Недостатком будет то, что в БД будет в два раза больше таблиц.

Я вообще не рекомендую триггеры, но это может быть то, что вы ищете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...