Если бы вам пришлось выбирать свои любимые (умные) методы для защитного кодирования, какими бы они были? Хотя мои текущие языки - Java и Objective-C (с опытом работы в C ++), не стесняйтесь отвечать на любом языке. Акцент здесь будет сделан на хитрых методах защиты, отличных от тех, о которых 70% из нас уже знают. Так что теперь пришло время копаться в вашей сумке трюков.
Другими словами, постарайтесь думать о другом, чем этот неинтересный пример:
if(5 == x)
вместоif(x == 5)
: чтобы избежать непреднамеренного назначения
Вот некоторые примеры некоторых интригующих лучших методов защитного программирования (специфичные для языка примеры в Java):
- Блокируйте свои переменные, пока не узнаете, что вам нужно их изменить
То есть вы можете объявлять все переменные, final
пока не узнаете, что вам нужно будет их изменить, после чего вы можете удалить final
. Один обычно неизвестный факт - это также верно для параметров метода:
public void foo(final int arg) { /* Stuff Here */ }
- Когда происходит что-то плохое, оставьте след улик
Есть несколько вещей, которые вы можете сделать, когда у вас есть исключение: очевидно, зарегистрировать его и выполнить некоторую очистку будет несколько. Но вы также можете оставить след (например, установка переменных для значений часового типа, таких как «UNABLE TO LOAD FILE» или 99999, будет полезна в отладчике на случай, если вы catch
пропустите исключение -блок).
- Когда дело доходит до последовательности: дьявол кроется в деталях
Будьте максимально совместимы с другими библиотеками, которые вы используете. Например, в Java, если вы создаете метод, который извлекает диапазон значений, сделайте нижнюю границу включающей и верхнюю границу исключающей . Это сделает его совместимым с такими методами, String.substring(start, end)
которые работают таким же образом. Вы найдете все эти типы методов в Sun JDK, которые ведут себя таким образом, так как он выполняет различные операции, включая итерацию элементов в соответствии с массивами, где индексы от нуля ( включительно ) до длины массива ( исключая ).
Каковы ваши любимые защитные практики?
Обновление: если вы еще этого не сделали, не стесняйтесь, присоединяйтесь. Я даю шанс получить больше ответов, прежде чем я выберу официальный ответ.
источник
Ответы:
В C ++ мне когда-то нравилось переопределять новое, чтобы оно обеспечивало дополнительную память для перехвата ошибок забора.
В настоящее время я предпочитаю избегать защитного программирования в пользу Test Driven Development . Если вы обнаруживаете ошибки быстро и внешне, вам не нужно путать ваш код с защитными маневрами, ваш код - СУХОЙ, и вы получаете меньше ошибок, от которых вам нужно защищаться.
Как писал WikiKnowledge :
источник
SQL
Когда мне нужно удалить данные, я пишу
Когда я запускаю его, я узнаю, забыл ли я или не испортил условие where. У меня есть безопасность. Если все в порядке, я выделяю все после маркеров комментариев «-» и запускаю его.
Редактировать: если я удаляю много данных, я буду использовать count (*) вместо *
источник
Выделите разумный кусок памяти при запуске приложения - я думаю, что Стив Макконнелл назвал это « парашютом памяти» в Code Complete.
Это может быть использовано в случае, если что-то серьезное идет не так, и вы должны прекратить.
Выделение этой памяти заранее обеспечивает вам сеть безопасности, так как вы можете освободить ее и затем использовать доступную память для выполнения следующих действий:
источник
В каждом операторе switch, для которого не задан регистр по умолчанию, я добавляю регистр, который прерывает программу с сообщением об ошибке.
источник
Когда вы обрабатываете различные состояния перечисления (C #):
Тогда внутри какой-то рутины ...
В какой-то момент я добавлю еще один тип учетной записи к этому перечислению. И когда я это сделаю, я забуду исправить это заявление переключателя. Так что
Debug.Fail
ужасно падает (в режиме отладки), чтобы привлечь мое внимание к этому факту. Когда я добавляюcase AccountType.MyNewAccountType:
, ужасный сбой прекращается ... пока я не добавлю еще один тип учетной записи и не забуду обновить случаи здесь.(Да, полиморфизм здесь, вероятно, лучше, но это всего лишь пример из моей головы.)
источник
При распечатке сообщений об ошибках со строкой (особенно, которая зависит от ввода пользователя), я всегда использую одинарные кавычки
''
. Например:Такое отсутствие кавычек
%s
действительно плохо, потому что, скажем, имя файла - пустая строка или просто пробел или что-то в этом роде. Распечатанное сообщение, конечно, будет:Итак, всегда лучше сделать:
Тогда, по крайней мере, пользователь видит это:
Я считаю, что это имеет огромное значение с точки зрения качества отчетов об ошибках, представляемых конечными пользователями. Если вместо чего-то общего звучит странное сообщение об ошибке, подобное этому, то они с большей вероятностью скопируют / вставят его, вместо того, чтобы просто написать «это не откроет мои файлы».
источник
Безопасность SQL
Прежде чем писать какой-либо SQL, который будет изменять данные, я обертываю все это в откат транзакции:
Это предотвращает выполнение неверного удаления / обновления навсегда. И вы можете выполнить все это и проверить разумное количество записей или добавить
SELECT
операторы между вашим SQL и тем,ROLLBACK TRANSACTION
чтобы убедиться, что все выглядит правильно.Когда вы полностью уверены , что это не то , что вы ожидали, изменить ,
ROLLBACK
чтобыCOMMIT
и работать по - настоящему.источник
Для всех языков:
Уменьшите область действия переменных до минимально необходимого. Откажитесь от переменных , которые только что предоставлены, чтобы перенести их в следующий оператор. Переменные, которые не существуют, являются переменными, которые вам не нужно понимать, и вы не можете нести за них ответственность. Всегда используйте лямбды по той же причине.
источник
Если сомневаетесь, бомбите приложение!
Проверяйте каждый параметр в начале каждого метода (независимо от того, явно ли вы его кодируете или используете контрактное программирование, здесь не имеет значения) и бомбите с правильным исключением и / или значимым сообщением об ошибке, если какое-либо предварительное условие для кода не встречал
Мы все знаем об этих неявных предварительных условиях, когда мы пишем код , но если они явно не проверены, мы создаем лабиринты для себя, когда позже что-то идет не так, и стеки из десятков вызовов методов отделяют возникновение симптома и фактическое местоположение где предварительное условие не выполнено (= где проблема / ошибка на самом деле).
источник
param.NotNull("param")
вместоif ( param == null ) throw new ArgumentNullException("param");
В Java, особенно с коллекциями, используйте API, поэтому, если ваш метод возвращает тип List (например), попробуйте следующее:
Не позволяйте ничему сбежать из вашего класса, что вам не нужно!
источник
в Perl все
мне нравиться
Это приводит к смерти кода для любого предупреждения компилятора / среды выполнения. Это в основном полезно при отлове неинициализированных строк.
источник
C #:
источник
В c # проверка string.IsNullOrEmpty перед выполнением любых операций над строкой, таких как length, indexOf, mid и т. Д.
[Править]
Теперь я также могу сделать следующее в .NET 4.0, которая дополнительно проверяет, является ли значение просто пробелом
источник
if (!string.IsNullOrEmpty(myString)) throw new ArgumentException("<something>", "myString"); /* do something with string */
.В Java и C # дайте каждому потоку осмысленное имя. Это включает потоки пула потоков. Это делает дампы стека намного более значимыми. Требуется немного больше усилий, чтобы дать значимое имя даже потокам пула потоков, но если у одного пула потоков есть проблема в долго работающем приложении, я могу вызвать дамп стека (вы знаете о SendSignal.exe , верно? ), возьмите логи, и без прерывания работающей системы я могу сказать, какие потоки ... что угодно. В тупик, протекает, растет, в чем бы проблема.
источник
В VB.NET опции Explicit и Option Strict включены по умолчанию для всей Visual Studio.
источник
C ++
затем замените все ваши вызовы ' delete pPtr ' и ' delete [] pPtr ' на SAFE_DELETE (pPtr) и SAFE_DELETE_ARRAY (pPtr)
Теперь по ошибке, если вы используете указатель «pPtr» после его удаления, вы получите ошибку «нарушение прав доступа». Это гораздо легче исправить, чем случайные повреждения памяти.
источник
delete
. Использованиеnew
- это нормально, пока новый объект становится интеллектуальным указателем.С Java может быть удобно использовать ключевое слово assert, даже если вы запускаете производственный код с отключенными утверждениями:
Даже с отключенными утверждениями, по крайней мере, код документирует тот факт, что параметр должен быть где-то установлен. Обратите внимание, что это частная вспомогательная функция, а не член открытого API. Этот метод может вызываться только вами, поэтому можно делать определенные предположения о том, как он будет использоваться. Для открытых методов, вероятно, лучше создать реальное исключение для неверного ввода.
источник
Я не нашел
readonly
ключевое слово, пока не нашел ReSharper, но теперь я использую его инстинктивно, особенно для классов обслуживания.источник
В Java, когда что-то происходит, и я не знаю почему, я иногда буду использовать Log4J так:
таким образом я получаю трассировку стека, показывающую, как я попал в неожиданную ситуацию, скажем, блокировку, которая никогда не разблокируется, что-то нулевое, которое не может быть нулевым, и так далее. Очевидно, что если выдается настоящее исключение, мне не нужно этого делать. Это когда мне нужно увидеть, что происходит в рабочем коде, не мешая чему-либо еще. Я не хочу бросать исключение, и я его не поймал. Я просто хочу, чтобы трассировка стека была зарегистрирована с соответствующим сообщением, чтобы пометить меня в том, что происходит.
источник
Если вы используете Visual C ++, используйте ключевое слово override всякий раз, когда вы переопределяете метод базового класса. Таким образом, если кто-нибудь когда-нибудь изменит сигнатуру базового класса, он выдаст ошибку компилятора, а не неверный метод, вызываемый без вывода сообщений. Это бы спасло меня несколько раз, если бы существовало раньше.
Пример:
источник
Я узнал в Java, что почти никогда не жду, пока разблокируется блокировка, если только я действительно не ожидаю, что это может занять бесконечно много времени. Если реально, блокировка должна разблокироваться в течение нескольких секунд, тогда я буду ждать только определенное время. Если блокировка не разблокируется, то я жалуюсь и сбрасываю стек в журналы и, в зависимости от того, что лучше для стабильности системы, либо продолжаю, как будто блокировка разблокирована, либо продолжаю, как будто блокировка никогда не разблокируется.
Это помогло выделить несколько условий гонки и псевдоблокировки, которые были таинственными, прежде чем я начал это делать.
источник
C #
sealed
много использую для классов, чтобы избежать введения зависимостей там, где они мне не нужны. Разрешение наследования должно быть сделано явно, а не случайно.источник
Когда вы выдаете сообщение об ошибке, по крайней мере, попытайтесь предоставить ту же информацию, которая была у программы, когда она приняла решение выдать ошибку.
«Отказано в доступе» означает, что была проблема с разрешением, но вы не знаете, почему или где проблема возникла. «Невозможно записать журнал транзакций / мой / файл: файловая система только для чтения», по крайней мере, позволяет узнать основу, на которой было принято решение, даже если оно неправильное, особенно если оно неправильное: неправильное имя файла? открыл неправильно? другая неожиданная ошибка? - и дает вам знать, где вы были, когда у вас возникла проблема.
источник
В C # используйте
as
ключевое слово для приведения.сгенерирует исключение, если obj не является строкой
оставит как ноль, если объект не является строкой
Вам все еще нужно принять во внимание ноль, но это, как правило, проще, чем поиск исключений приведения. Иногда вам нужно поведение типа «брось или взорви», в этом случае
(string)obj
синтаксис предпочтителенВ моем собственном коде я использую
as
синтаксис около 75% времени, а(cast)
синтаксис - около 25%.источник
is/instanceof
приходит на ум.Будьте готовы к любому вводу, и любой неожиданный вывод, дамп в журналы. (В пределах разумного. Если вы читаете пароли от пользователя, не записывайте их в журналы! И не записывайте тысячи сообщений такого рода в журналы в секунду. Причины, связанные с содержанием, вероятностью и частотой, прежде чем регистрировать его .)
Я не просто говорю о проверке пользовательского ввода. Например, если вы читаете HTTP-запросы, которые, как вы ожидаете, содержат XML, будьте готовы к другим форматам данных. Я был удивлен, увидев ответы HTML, где я ожидал только XML - пока я не посмотрел и не увидел, что мой запрос проходил через прозрачный прокси-сервер, о котором я не знал, и что клиент заявил о незнании - и время ожидания прокси-сервера истекло при попытке завершить запрос. Таким образом, прокси-сервер возвратил страницу с ошибкой HTML моему клиенту, запутав его, который ожидал только данные XML.
Таким образом, даже если вам кажется, что вы контролируете оба конца провода, вы можете получить неожиданные форматы данных без какого-либо злодейства. Будьте готовы, защитите код и предоставьте диагностический вывод в случае непредвиденного ввода.
источник
Я стараюсь использовать подход «Дизайн по контракту». Это может быть эмулировано во время выполнения на любом языке. Каждый язык поддерживает «assert», но легко и удобно написать лучшую реализацию, которая позволит вам более эффективно управлять ошибкой.
В « 25 самых опасных ошибках программирования» «Неправильная проверка ввода» является самой опасной ошибкой в разделе «Небезопасное взаимодействие между компонентами».
Добавление предварительных условий в начале методов - это хороший способ убедиться, что параметры согласованы. В конце методов я пишу постусловия , которые проверяют, что вывод - это то, что и должно быть.
Чтобы реализовать инварианты , я пишу метод в любом классе, который проверяет «согласованность классов», который должен вызываться автоматически при помощи макроса предусловия и постусловия.
Я оцениваю библиотеку контрактов .
источник
Ява
Java-API не имеет понятия неизменных объектов, что плохо! Финал может помочь вам в этом случае. Пометьте каждый класс, который является неизменным, финалом и подготовьте класс соответствующим образом .
Иногда полезно использовать final для локальных переменных, чтобы они никогда не меняли свое значение. Я нашел это полезным в некрасивых, но необходимых конструкциях цикла. Просто легко случайно повторно использовать переменную, даже если она исправлена как константа.
Используйте защитное копирование в ваших добытчиках. Если вы не возвращаете примитивный тип или неизменный объект, убедитесь, что вы скопировали объект, чтобы не нарушать инкапсуляцию.
Никогда не используйте клон, используйте конструктор копирования .
Узнайте контракт между equals и hashCode. Это нарушается так часто. Проблема в том, что это не влияет на ваш код в 99% случаев. Люди перезаписывают равно, но не заботятся о hashCode. Существуют случаи, когда ваш код может сломаться или вести себя странно, например, использовать изменяемые объекты в качестве ключей на карте.
источник
Я забыл написать
echo
в PHP слишком много раз:Мне потребовалось бы целую вечность, чтобы попытаться выяснить, почему -> baz () ничего не возвращал, хотя на самом деле я просто не повторял это! Итак, я создал
EchoMe
класс, который можно обернуть вокруг любого значения, которое должно отображаться:А затем для среды разработки я использовал EchoMe, чтобы обернуть вещи, которые должны быть напечатаны:
Используя эту технику, в первом примере, пропущенном через the
echo
, теперь выдается исключение ...источник
AnObject.ToString
вместоWriteln(AnObject.ToString)
?C ++
Когда я набираю новый, я должен немедленно напечатать delete. Особенно для массивов.
C #
Проверьте на нулевое значение перед доступом к свойствам, особенно при использовании шаблона Mediator. Объекты пропускаются (и затем должны быть преобразованы с использованием as, как уже было отмечено), а затем проверяются на нулевое значение. Даже если вы думаете, что оно не будет нулевым, все равно проверьте. Я был удивлен.
источник
Используйте систему ведения журналов, которая позволяет динамически настраивать уровень журнала во время выполнения. Часто, если вам нужно остановить программу, чтобы включить ведение журнала, вы потеряете любое редкое состояние, в котором произошла ошибка. Вы должны иметь возможность включить дополнительную информацию о ведении журнала, не останавливая процесс.
Кроме того, 'strace -p [pid]' в linux покажет, что вы хотите, чтобы системные вызовы выполнял процесс (или поток linux). Поначалу это может показаться странным, но как только вы привыкнете к тому, какие системные вызовы обычно выполняются с помощью вызовов libc, вы найдете это бесценным в диагностике на местах.
источник