Любимые (Умные) Лучшие Практики Защитного Программирования

Если бы вам пришлось выбирать свои любимые (умные) методы для защитного кодирования, какими бы они были? Хотя мои текущие языки - 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, которые ведут себя таким образом, поскольку он делает различные операции, включая итерацию элементов, совместимой с массивами, где индексы имеют значение от нуля (включительно) до длины массива (исключая).

Так какие у тебя любимые защитные практики?

Обновление: если вы еще этого не сделали, не стесняйтесь, принимайте участие. Я даю шанс получить больше ответов, прежде чем я выберу официальный ответ.

67 ответов

Включите высокоуровневую обработку исключений, как подробно описано здесь

Обработка исключений верхнего уровня в приложениях Windows Forms

Мой Program.cs будет выглядеть так

    static class Program
    {
    [STAThread]
    static void Main()
    {
        Application.ThreadException += 
            new ThreadExceptionEventHandler(new ThreadExceptionHandler().ApplicationThreadException);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }

    public class ThreadExceptionHandler
    {
        public void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)
        {
            MessageBox.Show(e.Exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
}

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

Это не относится ко всему, но в некоторых случаях это очень аккуратная идея.

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

Например, у вас есть такой цикл:

while(1)
{
  // some codes here

  if(keypress == escape_key || keypress == alt_f4_key 
     || keypress == ctrl_w_key || keypress == ctrl_q_key) break;

  // some codes here
}

Если вы хотите поместить условие в заголовок цикла, вместо того, чтобы бороться с языком за отсутствие конструкции before, просто скопируйте дословно условие и поставьте восклицательный знак:

while(! (keypress == escape_key || keypress == alt_f4_key 
     || keypress == ctrl_w_key || keypress == ctrl_q_key) )
{ 
    // some codes here
}

На языках, производных от C, пока нет конструкции, поэтому просто делайте это выше, в противном случае сделайте это (возможно в C/C++, используйте #define;-)

until(keypress == escape_key || keypress == alt_f4_key 
     || keypress == ctrl_w_key || keypress == ctrl_q_key)
{ 
    // some codes here
}

Скорее тогда var.equals("что угодно") в Java я делаю "что угодно".equals(var). Таким образом, если var равен null, мне не нужно беспокоиться об исключении nullpointer. Это прекрасно работает при работе с такими вещами, как параметры URL и т. Д.

Языковая независимость: проблема: отчетность и работа с частями целого. Всякий раз, когда отображаются расчеты и проценты, я всегда сохраняю промежуточную сумму, и для последней записи ее значение вычисляется не как остальные, а вычитается из 100,00 промежуточной суммы. Таким образом, если какая-то заинтересованная сторона решит сложить все проценты компонентов, они добавят ровно 100,00

Вряд ли умная, но приличная практика, пожалуй. В C/C++:

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

int MyIntReturningFuction(char *importantPointer)
{
    int intToReturn = FAILURE;
    if (NULL == importantPointer)
    {
        return FAILURE;
    }
    // Do code that will set intToReturn to SUCCESS (or not).
    return intToReturn;
}

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

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

  • При выполнении запросов SQL из вашего кода всегда используйте заполнители
  • MySQL имеет полезное нестандартное расширение DELETE заявление: DELETE FROM sometable WHERE name IS LIKE 'foo%' LIMIT 1, Таким образом, вы не будете стирать всю таблицу в случае ошибки.
Другие вопросы по тегам