Контроль уязвимостей в программных приложениях

    Дефекты программного кода


    ИТ-индустрия – самая быстро развивающаяся отрасль в истории человечества, опережающая радио, телевидение и телефонию. Динамичность изменений приводит к тому, что современная система образования просто не успевает готовить квалифицированные ИТ-кадры – программистов, архитекторов, аналитиков. Требования к компетенциям таких специалистов растут быстрее, нежели они успевают обучаться. Отсюда – постоянный кадровый голод на этом рынке, как следствие, нередко результаты работы ИТ-команд имеют дефекты. Сегодня уже никого не удивляет, что программные продукты даже самых известных производителей часто бывают несовершенны. Мы принимаем это как данность и работаем с тем ПО, которое нам выдают разработчики. В свою очередь они, как и другие ИТ-специалисты, прилагают все усилия, чтобы недостатков становилось меньше.

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

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

    Самый распространенный дефект – это ошибки программистов при разработке кода. Зачастую причиной их появления является дефицит времени или потеря внимания во время работы. Например, программист разрабатывает код, который в определенных условиях должен выполнять одни действия, а во всех остальных случаях – другие. Программист уделяет большое внимание разработке кода, который будет выполняться в 90% случаях исполнения ПО. Когда же дело доходит до обработки альтернативы, он устает, его внимание рассеивается, и какие-то важные аспекты, такие как оператор, определяющий выполнение данного фрагмента кода, только если условие выполнения основного фрагмента не выполнилось, теряются.

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

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

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

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

    Самые неприятные с точки зрения возможности обнаружения дефекты – недекларированные возможности ПО (НДВ). Недекларированные возможности – это правильный код с точки зрения и функциональности, и информационной безопасности, поэтому его трудно обнаружить автоматическими методами. Однако этот код реализует функциональность, которая не была задумана заказчиком – ее для своих целей привнес разработчик. Обычно НДВ разделяют на закладки и секретный вход (back door).

    Закладка – это функциональность, которая выполняется при наступлении определенных условий и выполняет действия, задуманные разработчиком. Часто закладки используются для того чтобы неявно манипулировать программным обеспечением. Один из наиболее известных случаев наличия закладки – история разработчика City Bank. Программист не знал, что делать с разницей, возникающей при округлении результатов арифметических операций при начислении процентов на вклады клиентов, и не придумал ничего лучше, чем накапливать ее на своем персональном счете.

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

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

    Откуда берутся дефекты в программном коде?


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

    Зачастую требования к программному обеспечению меняются быстрее, чем ИТ-команда успевает их реализовывать. Дается одно техническое задание, оно прорабатывается архитектором, конструкторами и передается в работу. Но в процессе заказчик разработки понимает: новые условия рынка требуют, чтобы разрабатываемое ПО выполняло другой функционал. Несмотря на возражения ИТ-команды, вносятся изменения в проектную документацию, а зачастую и непосредственно в код, ломая при этом проработанную архитектуру.

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

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

    Появление в коде недекларированных возможностей носит исключительно личностный характер и с трудом контролируется посредством внедрения современных технологий разработки ПО. Хотя практика перекрестного контроля разработки (прежде чем код попадает в основную ветку разработки, его обязательно проверяет другой программист) и другие организационные меры имеют определенный успех. Обнаружить недекларированные возможности в коде, который разрабатывался на заказ и куда НДВ были внедрены специально, можно только посредством его анализа.

    Можно ли контролировать наличие дефектов в программном коде?


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

    Дефекты в программном обеспечении, которое разрабатывается самостоятельно, рекомендуется уменьшать и контролировать посредством реализации практики разработки надежного ПО (Security Development Lifecycle, SDL), разработанной компанией Microsoft. Одним из необходимых условий ее применения является наличие инструментальных средств анализа кода в цикле разработки. Так, на этапе разработки программисты должны использовать инструментальное средство статического анализа кода, позволяющее обнаруживать уязвимости непосредственно во время написания кода. На этапе тестирования, помимо проведения регрессионного тестирования и тестирования на случайных данных, рекомендуется использовать инструментальные средства динамического анализа кода, позволяющие проверять программный продукт с помощью тестов на проникновение.

    На сегодняшний день на рынке представлено несколько инструментальных систем анализа кода, как статического (по исходному коду методом «белого ящика»), так и динамического (без исходного кода методом «черного ящика»). Помимо этого, ведущие инструментальные системы анализа кода от таких производителей, как HP и IBM, предлагают комбинированный статический и динамический анализ, который позволяет отображать результаты динамического анализа на исходный код. Можно сказать, что в настоящее время наиболее эффективным инструментальным средством анализа кода, которое предлагает статический, динамический, а также гибридный анализ в сочетании с удобным интерфейсом и хорошей библиотекой правил поиска уязвимостей, является инструментальное средство HP Fortify.

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

    Контролировать уязвимости в программном обеспечении, которое поставляется на заказ, сложно, так как информация о наличии дефектов проходит долгий путь проверок, а затем следует не менее длительный процесс их устранения. Однако это не значит, что такое ПО не нужно проверять и стоит эксплуатировать как «кота в мешке». Рекомендуется выполнять контроль уязвимостей посредством правильной настройки защиты периметра эксплуатации.

    Залог успешного бизнеса – это не эффективное решение проблем, а предотвращение их появления. Проактивное управление уже давно завоевало мир, доказав свою эффективность не только в теории, но и на практике. Придерживаясь этих принципов, мы предоставляем полный спектр услуг по анализу программных продуктов по требованиям информационной безопасности:
    • обнаружение НДВ;
    • обнаружение уязвимостей;
    • разработка рекомендаций по устранению выявленных уязвимостей;
    • разработка рекомендаций по защите, пока разработчики устраняют выявленные уязвимости;
    • построение полного цикла разработки защищенного кода (SDL).

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


    Автор статьи: Екатерина Трошина
    Инфосистемы Джет
    Системный интегратор

    Комментарии 16

      +1
      Недекларированные возможности – это правильный код с точки зрения и функциональности, и информационной безопасности,

      Вторая часть звучит несколько шокирующе. Я бы сказал, что вторая половина должна быть прямо противоположным утверждением.
        0
        Фраза вырвана из контекста, но имеется ввиду, что НДВ могут быть реализованы посредством использования языковых конструкций, которые не имеют побочных эффектов. Например, у вас может быть вполне безобидный код, который проверяет IP адрес машины, с которой выполняется запрос, но, если этот IP == <some_var> программа предоставляет особенный доступ или пересылает особенные данные. Такую проблему можно идентифицировать только в полуавтоматическом режиме. Для эксплуатации злоумышленник должен знать о том, что в коде такое реализовано.
        Под правильным кодом с точки зрения информационной безопасности понимается код, выполнение которого не имеет побочного эффекта.
          0
          В программировании термин «не имеет побочного эффекта» имеет вполне определенный смысл — он обозначает функцию без внутреннего состояния, то есть такую f(x), что всегда f(x)=f(x). Пример такой функции — все математические функции вроде синуса и косинуса.
          Контрпример — функция, которая открывает файл, fopen("a.txt","r") может выдать разные дескрипторы раз от раза, пример еще проще — функция rand().

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

          Очень возможно, что под «побочным эффектом» мы с вами понимаем разное.

          Но я таки согласен, что функции без побочного эффекта с точки зрения ИБ почти безопасны — изменить ими состояние информационной системы невозможно, но можно загрузить ее бессмысленной работой.
        +1
        Используют ли в Инфосистемы Джет статические анализаторы кода? Если да, то какие?
          0
          В Джете мы используем разные инструментальные средства статического анализа кода. Сейчас все больше работаем с SonarQube, для которого у нас есть большая библиотека проприетарных правил. Так же из наиболее часто используемых ИС SCA можно назвать HP Fortify. Вообще, по запросу Заказчика выполняем SCA тем ИС SCA, на который договариваемся по тем или иным причинам (особенность разработки, пожелания Заказчика, наличие у Заказчика ИС SCA)
          0
          Просто какой-то рассказ школьника о выученном уроке. Программист может делать всё правильно, и его программа делать всё правильно, но только если окружение ведёт себя так, как ожидает программист. А оно себя ведёт не так, как ожидает программист, и тут начинается.

          А полностью задать в ТЗ программисту окружение — никаких ресурсов не хватит. Это из тех багов, что невозможны для отлова посредством анализа самого кода.

          Простейший пример: многотредовое приложение: ожидает ли это приложение, что один из его тредов слетит из-за oom'а (вызванного другим тредом)? А что его притормозят из-за сожранной квоты на процессор? А что IO может никогда не закончиться (nfs сервер перестал отвечать)?
            0
            Все примеры из последнего абзаца программист обязан отслеживать и адекватно парировать. Или вы не считаете нужным проверять коды ответов от системных вызовов?
              0
              Расскажите мне про программиста, который адекватно парирует OOM. Я так себе и представляю «userspace VS kernel» в борьбе с ужасающим OOM killer'ом.
                0
                Ядро с удовольствием делится со всеми желающими статистикой, поэтому репрессии со стороны ядра можно предупредить и в лог отправить предупреждение.

                Если же таки уничтожился поток, этот факт нужно хотя бы отразить в логах, а поток попытаться перезапустить.

                А еще, можно попытаться проектировать аппаратно-программный комплекс так, чтобы ему OOM не грозило. Тем более, что нынче виртуализация позволяет под каждую задачу/приложение иметь отдельный экземпляр ОС.
                  0
                  Вы собираетесь статистику поллить каждую милисекунду? Потому что при дефолтном оверкоммите по памяти oom приходит не в момент аллокации памяти, а в произвольный момент времени, когда соседнему процессу захотелось-таки воспользоваться предварительно аллоцированным гигом памяти, которая последние -цать дней висела в чистом virt'е.

                  А можно попытаться проектировать аппаратно-программный комплекс на базе инновационных национальных нанотехнологических суверенных технологиях. Можно.

                  А «отдельный экземпляр ОС» будет ровно так же снесён OOM'ом если хосту что-то не понравится (kvm) или принудить гостя прибить init из-за припавшего xenballoon и запаниковать (xen).

                  Кстати, вы так и не сказали, как должна реагировать программа, когда ей на запрос IO ничего не ответили. То есть программа делает любое телодвижение, а в ответ треду не возвращается управление в ближайшие 15 минут.
                    0
                    Возможно, у нас с вами возникло недопонимание — под «парированием» я имел в виду адекватную реакцию на системные ошибки, которая должна быть вместо падения по необработанному исключению.

                    Рассмотрим случай с OOM — это явно аварийная ситуация, и программист здесь несет явно малую долю ответственности — он перед нехваткой памяти бессилен. Ответственность программиста состоит в том, что он:
                    1. сообщает — «на таких-то данных программе может потребоваться x байт памяти»
                    2. реализует в программе адекватное журналирование ситуаций, связанных с дефицитом памяти


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

                    Проблема с отсутствием реакции на IO близка к проблеме с OOM — программист не в силах починить упавший линк. Он может попытаться еще раз (записав в журнал предупреждение), или, если все это уже потеряло смысл — аккуратно записать журналы и завершить работу.
                      0
                      Извините, но вы просто не понимаете, как работает oom killer. Он никого не предупреждает. Он логгирует происходящее в dmesg в процессе исстребления первых попавшихся (по эвристике).
                        0
                        Ваш вывод неверен, я знаю о поведении OOM. Я также знаю, что никто не запрещает программе перед выполнением тяжелой по памяти операции поискать себя же в dmesg (на предмет поиска прошлых неудач) и сделать выводы. Чем вам не предупреждение?

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

                          OOM killer приходит не в момент выполнения «тяжёлой операции». И вовсе убивает не того, кто «создал проблему». И даже не того, у кого сейчас «тяжёлая операция».

                          OOM killer приходит в тот момент, когда какая-то программа (например, cron) записывает байты в страницу, которая раньше была r/o, а теперь стала r/w, но для которой не нашлось подходящей физической страницы (на самом деле даже чуть раньше — подсистема управления памятью следит за некоторым резервом для самого ядра). А убивается то приложение, которое «подвернулось под руку». То есть у вас сервер сидит себе, никого не трогает, epoll, всё такое. Вдруг, соседний сервис что-то сделал, другой сервис что-то сделал, и ваш процесс, который ни сном ни духом, чпок-чпок, и больше нету.

                          Как вы это отлавливать будете на уровне приложения? Никак. Как это отлавливать на уровне сервиса? watchdog, изменение oom_adj при необходимости.

                          Рассказывать мне про программиста, который точно специфицировал, сколько ему нужно памяти — это рассказывать сказочки про таких д'Артаньянов, которые стоят себе в белом, все такие красивые, а остальные вокруг не правы. Как программист может предсказать, сколько памяти съест используемая им библиотека, с которой он линкуется в динамическом режиме, не зная точно её версии?

                          Я как раз и говорю, что сказки про «программист, если его долго бить по голове, может сделать так, что в его программе будет предусмотрены все ошибочные ситуации» — это сказки и не более.

                          Реальность software engineering такова, что код не стремится быть безошибочным, а стремится следовать индустриальным стандаратм. В этом случае оно будет падать там, где падают все остальные, так же, как падают все остальные и будет соответствовать принципу наименьшего wtf для сисадмина.

                          Плюс тесты, плюс опыт эксплуатации и фидбэк от эксплуатирующих. Другого пути нет. Хотя есть — надо научиться решать задачу занятых бобров за полиноминальное время, и все проблемы будут решены.
                            0
                            Спасибо за рассказ об OOM, ничего нового для себя из него я не узнал.

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


            Ни дня без перлов :)

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое