19 November 2015

Разработка защищенных банковских приложений: главные проблемы и как их избежать

Positive Technologies corporate blogInformation SecurityWebsite development


В прошлом году злоумышленники совершили на 30 % больше атак на российские банки, чем годом ранее. Пытались вывести около 6 млрд рублей. Часто атака становится возможной из-за недостаточной защищенности финансовых приложений.

По нашей статистике, более половины систем дистанционного банковского обслуживания (54 %) содержали XSS-уязвимости, которые позволяют осуществить MitM-атаку и перехватить доступ к интернет-банкингу. С мобильными банковскими приложениями ситуация выглядит не лучше: 70 % «кошельков» для Android и 50 % для iOS в 2014 году содержали уязвимости, достаточные для получения доступа к счету.

Выявлять уязвимости на ранней стадии гораздо дешевле, чем потом расхлебывать последствия их эксплуатации. В середине октября эксперты Positive Technologies Тимур Юнусов и Владимир Кочетков провели двухдневный мастер-класс по безопасной разработке банковских приложений. Сегодня мы представляем краткий пересказ.

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

Проблемы управления доступом


Такие проблемы возникают главным образом при реализации следующих механизмов управления доступом:

  • идентификация, аутентификация, авторизация;
  • двухфакторные методы аутентификации.

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

Часто корень проблем кроется в неверном использовании криптопротоколов и реализаций криптопримитивов (средств криптографии, встроенных в стандартные библиотеки .NET, Java и т. п.). Здесь также важно отметить, что использование низкоуровневых криптопримитивов в принципе нежелательно, поскольку очень легко допустить ошибку в их конфигурации и тем самым свести на нет все усилия по внедрению криптографии в отдельно взятом приложении.

Одним из самых ярких последствий таких ошибок является уязвимость к атакам Padding Oracle, возникающая при использовании слабых режимов работы блочных шифров. Вместо использования низкоуровневых средств всегда нужно стремиться к использованию высокоуровневых библиотек типа KeyCzar, libsodium.

Еще один пласт проблем связан с подходом security through obscurity. Каждый банк использует криптографию (SSL, TLS и т. п.) и нередко шифрует данные на уровне приложений (L7). Это дает финансовым организациям иллюзию защищенности, и возникает мысль, что на серверной части теперь ничего защищать не надо: все ведь «обернуто» криптографией и атакующий попросту не сможет ничего злонамеренно послать на сервер.

Это, конечно же, не так. Криптография поддается обратной разработке, проверки в мобильных приложениях обходятся, если злоумышленник имеет физический доступ к устройству с установленным банковским приложением. Другими словами, осуществить MitM-атаку на SSL-трафик можно всегда. Более того, иногда уязвимости удается эксплуатировать даже «поверх» криптографии — например, из форм на сайте.

Проблемы управления потоками операций


Среди наиболее популярных и опасных ошибок управления потоками операций — и возможных атак на их основе — можно выделить:

  • недостаточные проверки процесса;
  • race condition и прочие атаки на атомарность;
  • другие уязвимости бизнес-логики;
  • атаки CSRF.

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

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

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

  1. Следует избегать появления в потоке операций рекурсивных путей и циклов.
  2. Необходимо учитывать возможное нарушение целостности данных, разделяемых различными потоками.
  3. Текущее состояние потока необходимо хранить перед границей доверия, а не за ней (применительно к «двухзвенке» — на сервере, а не на клиенте).
  4. Необходимо реализовать строгий контроль аутентичности инициатора перехода между состояниями workflow (неэффективный контроль приводит, например в случае с Вебом, к уязвимости для атак CSRF);
  5. В случае если несколько потоков операций, разделяющих данные, могут работать одновременно, необходимо обеспечить гранулированный доступ ко всем таким данным из всех таких потоков.

Проблемы управления потоками данных


Ошибки в организации управления потоками данных могут приводить к возникновению следующих серьезных проблем:

  • инъекции (SQL, XSS, XML, XXE, XPath, XQuery, Linq и т. п.),
  • внедрение и выполнение произвольного кода на серверной стороне.

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

Существует три подхода к организации предварительной обработки данных:

  • типизация — приведение строковых данных к конкретным типам в терминах ООП и дальнейшее использование в коде уже этих типов (параметризация SQL-запросов, например, является неявной реализацией типизации SQL-литералов);
  • санитизация — преобразование входных строковых данных в вид, безопасный для их использования в качестве выходных (примерами являются всевозможные HtmlEncode, UrlEncode, addslashes и т. п.);
  • валидация — проверка данных на соответствие каким-либо критериям; возможна валидация двух видов: синтаксическая (например, проверка на соответствие регулярному выражению) и семантическая (например, проверка числа на вхождение в определенный диапазон).

Порядок предпочтения этих подходов именно таков. То есть, там, где невозможна типизация, следует рассмотреть возможность санитизации, а там, где невозможна и санитизация, — следует внедрять валидацию. Это необходимо для того, чтобы максимально дистанцироваться от изменения семантики кода. Кроме того, стоит по возможности придерживаться правила: «типизация / валидация на входе (как можно ближе к началу потока выполнения кода), санитизация — на выходе (как можно ближе к месту в коде, в котором данные уходят наружу)».

Рассмотрим несколько примеров применения описанных выше подходов.

Типизация

Предположим, у нас есть следующий код:

var parm = Request.Params["parm1"];
if (Request.Params["cond1"] == "true")
{
    return;
}

if (Request.Params["cond2"] == "true") 
{
    parm = Request.Params["parm2"];
} else {
    parm = "<div>Harmless value</div>";
}

Response.Write("<a href=\"" + parm + "\">");

Здесь в parm записывается опасное значение, что приводит к возникновению уязвимости для атак класса XSS, но контекст его использования позволяет осуществить типизацию.

var typedParm = new Uri(Request.Params["parm2"]);

var parm = Request.Params["parm1"];
if (Request.Params["cond1"] == "true")
{
    return;
}

if (Request.Params["cond2"] == "true") 
{
    parm = typedParm.GetComponents(
        UriComponents.HttpRequestUrl, UriFormat.UriEscaped);
} else {
    parm = "<div>Harmless value</div>";
}

Response.Write("<a href=\"" + parm + "\">");



Санитизация

У нас есть следующий код:

var parm = Request.Params["parm1"];
if (Request.Params["cond1"] == "true")
{
    return;
}

if (Request.Params["cond2"] == "true") 
{
    parm = Request.Params["parm2"];
} else {
    parm = "<div>Harmless value</div>";
}

Response.Write("Selected parameter: " + parm);

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

var parm = Request.Params["parm1"];
if (Request.Params["cond1"] == "true")
{
    return;
}

if (Request.Params["cond2"] == "true") 
{
    parm = HttpUtility.HtmlEncode(Request.Params["parm2"]);
} else {
    parm = "<div>Harmless value</div>";
}

Response.Write("Selected parameter: " + parm);



Валидация

В примере ниже (уязвимость для атак переполнения буфера) осуществить типизацию и санитизацию невозможно, а значит, нужно применить валидацию:

const int BufferSize = 16;

public unsafe struct Buffer
{
    public fixed char Items [BufferSize];
}

static void Main(string[] args)
{
    var buffer = new Buffer();

    var argument = args[0].ToCharArray();

    if (argument.Length < BufferSize) { return; }

    for (var i = 0; i < argument.Length; i++)
    {
        unsafe
        {
            buffer.Items[i] = argument[i];
        }
    }
}

Код с валидацией будет выглядеть так:

const int BufferSize = 16;

public unsafe struct Buffer
{
    public fixed char Items [BufferSize];
}

static void Main(string[] args)
{
    Func<int, int> __ai_bkfoepld_validator = index =>
    {
        if (index >= BufferSize)
        {
            throw new IndexOutOfRangeException();
        }
        return index;
    };

    var buffer = new Buffer();

    var argument = args[0].ToCharArray();

    if (argument.Length < BufferSize) { return; }

    for (var i = 0; i < argument.Length; i++)
    {
        unsafe
        {
            buffer.Items[__ai_bkfoepld_validator(i)] = argument[i];
        }
    }
}



Инфраструктурные проблемы и методы их решения


Существует и целый ряд инфраструктурных проблем, которые могут приводить к успешным атакам на банковские системы. Среди них:

  • application DoS,
  • проблемы окружения,
  • стороннее ПО, модули и плагины.

Случаются и успешные атаки с помощью куда более тривиальных незакрытых FTP, или админок IBM/Tomcat и т. п.

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

  1. Нужно рассматривать каждый компонент инфраструктуры как скомпрометированный.
  2. TLS (не SSL) должен применяться везде, даже внутри инфраструктуры.
  3. Каждый компонент инфраструктуры должен быть развернут и настроен в соответствии с официальным security guide (если есть) или лучшими практиками.
  4. Использование специализированных средств для анализа защищенности и соответствия стандартам (например, MaxPatrol) также позволяет серьезно повысить уровень безопасности.
  5. Весь код должен быть подписан, даже если инфраструктура этого не требует.
  6. Все плагины и сторонние недоверенные модули должны выполняться в выделенных песочницах.

Предметные области банковских приложений


Эксперты также рассказали слушателям о различных банковских приложениях, не относящихся к серверной части систем ДБО, и возможных проблемах с ними:

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

Заключение


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

И сделать это можно без лишних усилий, просто следуя лучшим практикам разработки защищенного софта.
Tags:разработкабезопасная разработкаинформационная безопасностьпрограммированиебанковские приложения
Hubs: Positive Technologies corporate blog Information Security Website development
+10
16.8k 111
Comments 13