Просмотров: 6140

Kung-Fu Nemerle или общая теория безопасного кода. Часть 1


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

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

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

Ответам на эти вопросы и посвящена эта статья в двух частях.

«Внедрение безопасности в ваш продукт полностью зависит от вас. И никто другой не решит все проблемы безопасности за вас, никакое волшебное средство или язык программирования. <...> Это можете сделать только вы сами.»

© Майкл Ховард, «8 простых правил для разработки более безопасного кода», Microsoft MSDN


Nemerle безопасное программирование код уязвимости

Дисклаймер

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

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

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

Nemerle — гибридный язык высокого уровня со статической типизацией, сочетающий в себе возможности функционального и объектно-ориентированного программирования, для платформ .NET и Mono (язык компилируется в CIL и является CLS-совместимым). Главная особенность языка — развитая система метапрограммирования. Более подробно о нем можно почитать здесь или ещё здесь.

Так о чем пойдет речь?

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

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

Nemerle безопасное программирование код уязвимости

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

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

Во избежание разбухания объема и без того немаленькой заметки, рассматриваемые угрозы не описываются в ней сколь-нибудь подробно. За развернутым описанием каждой из них, можно обратиться к классификации угроз по версии WASC или по версии OWASP.

Переполнения, форматные строки

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

Добиться переполнения буфера, к примеру, в C#, все же можно, слегка ошибившись в коде с unsafe-блоками, которые позволяют ностальгирующим по C++ получить все его прелести «не выходя из домика».

Вот пример переполнения буфера на C#, взятый отсюда:

// C#
public unsafe struct testo
{
    public fixed int items[16];
    public int after;
}
 
testo x = new testo();
x.after = 1;
for (int i = 0; i <= 16; ++i)
{
    unsafe
    {
        x.items[i] = 99;
    }
}
Console.WriteLine(x.after);

Данный пример выведет на консоль число 99 из-за того, что обращение к несуществующему x.items[16] фактически перезатрет значение x.after . Кто-то может возразить, что нужно сильно постараться, чтобы наступить на такие грабли, программируя на C#. На это, я могу лишь заметить, что постараться тут нужно только в плане появления в проекте unsafe-блоков. Как только это произойдет, стараний потребуется не более, чем при разработке на том же C++.

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

Впрочем, как сказал Влад Чистяков (лидер проекта по разработке компилятора Nemerle) в ответ на вопрос, поддерживает ли этот язык unsafe: «Нет. Но может использовать intrerop (PInvoke и COM-intrerop ). С их помощью, к сожалению тоже можно легко привести программу к неработоспособности», что лишний раз подтверждает тезис об отсутствии теоретической возможности у языков программирования, стать когда-нибудь серебряными пулями безопасности.

Nemerle безопасное программирование код уязвимости

Что касается форматных строк, то в Nemerle реализована как поддержка традиционных для .NET форматных строк с блэкджеком со StringBuilder ’ом и плейсхолдерами, так и ряд средств, облегчающих форматирование строк, и являющихся по сути синтаксическим сахаром для традиционного подхода. В первую очередь, это так называемые splice-строки, позволяющие передать в строку значения и даже выражения на манер PHP:

// Nemerle
def name = "Vladimir";
WriteLine($"Hello, $name!");
WriteLine($"Goodbye, $(name.ToUpper())!");

Nemerle также поддерживает рекурсивные строки вида:

// Nemerle
def name = "Vladimir";
WriteLine(
$<#Hello $name!
Goodbye $(name.ToUpper())!
#>
);

Данный пример является абсолютным аналогом предыдущего. Кроме того, в стандартной макробиблиотеке Nemerle, в пространстве имен Nemerle.IO определен макрос printf , являющийся аналогом одноименной функции форматного вывода языков C/C++:

// Nemerle
printf("Value = %d", 2 + 2)
// Выведет на консоль число 4

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

Межсайтовый скриптинг aka XSS

Инъекции данных в целом и ;межсайтовый скриптинг (в простонародье — «XSS») в частности, являлись и продолжают являться настоящим бичом современных web-приложений. Об этом красноречиво говорит статистика уязвимых приложений в архивах XSSed.com. О борьбе с подобными инъекциями в ASP.NET и смежных с ним фреймворках написаны тонны полезных статей и нет никаких причин для того, чтобы не воспользоваться ими при разработке приложений под ASP.NET, ASP.NET MVC или NRails. В первую очередь, это официальное руководство по искоренению XSS от Microsoft, и использование библиотеки AntiXSS для более эффективного экранирования данных из недоверенных источников перед помещением их в тело HTML или XML документов.

Но в наборе стандартных библиотек, поставляемых с Nemerle есть еще кое-что, что позволяет немного облегчить жизнь разработчику. Не так уж и редко возникает задача генерации какого-либо фрагмента HTML (или XML, в общем случае) на лету, в runtime, на основе входных данных, полученных от пользователя. Возьмем, к примеру, задачу отображения его анкетных данных, ограничившись для простоты только именем.

В этом случае, C#-разработчик, еще не знающий о необходимости экранирования данных, напишет нечто подобное:

// C#
var html = String.Format(@"
    <html>
        <head>
            <title>
                User page
            </title>
        </head>
        <body>
            Name: {0}
        </body>
    </html>
", name);

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

<html>
    <head>
        <title>
            User page
        </title>
    </head>
    <body>
        Name: <script>alert(document.cookie);</script>
    </body>
</html>

обработка которого браузером повлечет за собой выполнение внедренного javascript-кода.

Все, что должен сделать в данном случае C#-разработчик, чтобы устранить уязвимость — это экранировать переменную name перед вставкой в шаблон документа вызовом HttpUtility.HtmlEncode(name) .

Выше подразумевается случай, когда данные вставляются между тегами. Если данные попадают внутрь тега, внедренного клиентского сценария или стиля, то следует воспользоваться библиотекой AntiXSS, либо соответствующими методами класса HttpUtility из пространства имен System.Web .

И тут уместны вопросы: а если данных, подставляемых в шаблон — несколько десятков? А если шаблон собирается из нескольких фрагментов, еще и вложенных друг в друга?

Nemerle безопасное программирование код уязвимости

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

Между тем, все, что нужно разработчику, чтобы безопасно решить эту задачу на Nemerle, это написать так:

// Nemerle
def html = xml <#
    <html>
        <head>
            <title>
                User page
            </title>
        </head>
        <body>
            Name: $name
        </body>
    </html>
#>

указав в своей сборке референс на макросборку . Nemerle.Xml.Macro ..

В этом случае, если атакующий попытается передать в name строку, приведенную выше, наш код создаст в переменной html документ, чье текстовое представление выглядит так:

<html>
    <head>
        <title>
            User page
        </title>
    </head>
    <body>
        Name: <script>alert(document.cookie);</script>
    </body>
</html>

... что совершенно идентично использованию HtmlEncode() . Почему? Потому что xml <#...#> - это макрос из подключенной нами макробиблиотеки, обеспечивающий поддержку так называемых XML-литералов, аналогичных реализованным в языках Scala и Visual Basic.

На первый взгляд, может показаться, что этот макрос реализует возможность встраивания фрагментов XML непосредственно в код, оперируя их текстовым представлением, но это не так. На самом деле, он является синтаксическим сахаром для построения XElement-дерева — объектной модели XML-узла, позволяя декларативно описывать эту структуру и предоставляя ряд синтаксических примитивов для передачи в эту структуру данных извне, которые вполне корректно экранируются при преобразовании дерева в строку.

Окончание этой статьи читайте здесь.

twitter.com facebook.com vkontakte.ru odnoklassniki.ru mail.ru ya.ru pikabu.ru blogger.com liveinternet.ru livejournal.ru google.com bobrdobr.ru yandex.ru del.icio.us

Подписка на обновления блога → через RSS, на e-mail, через Twitter
Теги: , , , , ,
Эта запись опубликована: Воскресенье, 29 января 2012 в рубрике Программирование.

Оставьте комментарий!

Не регистрировать/аноним

Используйте нормальные имена. Ваш комментарий будет опубликован после проверки.

Зарегистрировать/комментатор

Для регистрации укажите свой действующий email и пароль. Связка email-пароль позволяет вам комментировать и редактировать данные в вашем персональном аккаунте, такие как адрес сайта, ник и т.п. (Письмо с активацией придет в ящик, указанный при регистрации)

(обязательно)


⇑ Наверх
⇓ Вниз