Призыв писать компактное ПО, версия 2024 года (с примером кода)

в 10:51, , рубрики: electron, Node, автономная система, зависимости, качество кода, качество по, хаки
Призыв писать компактное ПО, версия 2024 года (с примером кода) - 1

Этот пост посвящён памяти Никлауса Вирта, первопроходца в сфере вычислительных наук, ушедшего от нас 1 января этого года. В 1995 году он написал важную статью A Plea for Lean Software, и в своём посте я постараюсь воспроизвести её почти тридцать лет спустя, с учётом современных кошмаров разработки ПО.

Очень короткая версия поста: современные способы разработки/сборки ПО смехотворны, они приводят к созданию пакетов на 350 МБ для рисования графиков, а простые продукты импортируют 1600 зависимостей неизвестного происхождения. Уровень безопасности ПО ужасен, ведь он зависит и от качества кода, и от его объёма. Многие из нас понимают, что ситуация нерациональна. К сожалению, многие программисты (и их руководство) никогда не работали как-то иначе. А остальным редко выделяют время, чтобы выполнять работу качественно.

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

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

Ситуация с ПО

Чтобы никто не мог сказать, что этот пост — воплощение мема «старик (48 лет) ругается на облако», позвольте мне ещё раз сформулировать очевидное. Ситуация с ПО ужасна. Достаточно лишь взглянуть на прошлый год: если вы работаете с популярным ПО наподобие IvantiMoveITOutlookConfluenceBarracuda Email Security GatewayCitrix NetScaler ADC и NetScaler Gateway, то есть вероятность, что вас взломали. Даже компании, обладающие практически безграничными ресурсами (например, Apple и Google) совершают тривиальные ошибки безопасности, ставящие под угрозу их пользователей. А мы всё равно продолжаем зависеть от этих продуктов.

Сегодня ПО считается (и вполне обоснованно) настолько опасным, что мы советуем всем не запускать его самостоятельно. Рекомендуется доверить всё это поставщику software as a service или отправить «в облако». Это похоже на гипотетическую ситуацию, когда вероятность возгорания автомобиля настолько высока, что вам советуют не ездить на нём самому, а оставить это профессионалам, которых всегда сопровождают опытные пожарные.

Предполагается, что «облако» каким-то образом способно превратить небезопасное ПО в безопасное. Однако даже за последний год мы узнали, что платформа электронной почты Microsoft глубоко хакнута, вплоть до засекреченной правительственной почты (дополнение: это случилось ещё раз!). Кроме того, существуют обоснованные беспокойства о безопасности облака Azure. Тем временем, любимица отрасли Okta, предоставляющая решения для реализации логинов, была взломана. И это второй взлом компании за один год. Кроме того, за этим последовала подозрительная лавина хаков пользователей Okta.

Нам нужно более качественное ПО, и это очевидно.

Пока в ЕС существует три законодательных акта в этой сфере (NIS2 для важных сервисов, Cyber Resilience Act почти для всего коммерческого ПО и устройств с разъёмами, а также пересмотренная Product Liability Directive, распространяющаяся на всё ПО). Процесс законотворчества всегда сложен, нам лишь остаётся надеяться, что они сделают всё правильно. Но очевидно то, что безопасность ПО сегодня ужасна настолько, что требует вмешательства закона.

Почему ПО настолько ужасно

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

Безопасность ПО зависит от двух факторов — плотности проблем с безопасностью в исходном коде и общего объёма кода. Как любили говорить военные США в 1980-х, количество само по себе переходит в качество. Для ПО справедливо обратное — чем его больше, тем больше возникает рисков.

Пример: пользователи Apple iPhone многократно и много лет подвергались хакам из-за огромной поверхности атаки, раскрытой приложением iMessage. Пользователю устройства Apple можно отправить непрошенное сообщение iMessage, а телефон сразу же обработает его для предварительного просмотра. Проблема в том, что Apple в своей мудрости решила, что такие непрошенные сообщения должны поддерживать огромное количество форматов изображений, в том числе и PDF со странными встроенными сжатыми шрифтами, использующими древний формат, по сути, включающий в себя язык программирования [перевод статьи на Хабре].

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

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

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

Apple могла бы избавиться от огромной доли мучений, просто раскрыв нападающим меньшее количество кода. Кстати, EU Cyber Resilience Act явным образом указывает поставщикам минимизировать поверхность атаки.

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

Мы не можем писать более качественный код?

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

Но многие проблемы с безопасностью заключаются не столько в плохом коде, сколько в плохой логике. Недавний пример — это огромная проблема с безопасностью в GitLab: его аккаунты можно было запросто захватить при помощи функции восстановления забытого пароля. Аналогично, эксплойт Barracuda вызван использованием сторонней библиотеки, исполняющей код в отсканированных страницах Excel. Недавний эксплойт Ivanti тоже связан с логикой (и при этом крайне постыден).

В сфере улучшения ситуации с логическими багами происходит меньше прогресса, чем в области безопасности кода.

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

Ситуация с выпуском ПО

Сегодня ПО стало ОГРОМНЫМ. Очень печально читать статью Никлауса Вирта A Plea for Lean Software, написанную в 1995 году, в которой он жалуется, что ПО 1995 года требует целых мегабайтов, а затем описывает созданную им операционную систему Oberon, которая требовала всего 200 КБ и включала в себя редактор и компилятор. А в современных проектах один только YAML занимает больше 200 КБ.

Типичное современное ПО создано на основе Electron JS, в который встроен и Chromium (Chrome), и Node.JS. Судя по прочитанному мной, из этого следует, что с учётом зависимостей в приложении используется не менее 50 миллионов строк кода. Возможно, и больше. Приложение при этом подтягивает сотни или тысячи модулей Node. Кроме того, по умолчанию также используется множество фреймворков, сливающих информацию о пользователях рекламодателям и другим брокерам данных. Зависимости подтягивают другие зависимости, включаемое в сборку может меняться ежедневно, и никто не знает точно, что же там есть внутри.

Если это приложение управляет какими-то устройствами в вашем доме, то оно также подключается через стек ПО к Amazon, вероятно, тоже под управлением Node.JS, и снова подтягивает множество зависимостей. И, как обычно, никто даже не представляет, что конкретно оно подтягивает из-за ежедневных обновлений.

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

Так как ПО выполнялось на компьютере, совершенно отличавшемся от того, на котором его разрабатывали, людям приходилось знать и продумывать, что же они выпускают. И иногда это не срабатывало. Ходила даже такая шутка: разработчик говорит отделу эксплуатации: «Ну, на моей системе всё работает», а ему отвечают: «сделай бэкап своей почты, мы забираем твой ноутбук в продакшен!».

Когда-то это было шуткой, но сегодня мы часто выпускаем ПО в виде контейнеров (Docker или иных), что может подразумевать фактический выпуск полного образа компьютера. То есть в сборку включается всё. Это тоже сильно увеличивает объём развёртываемого кода. Стоит отметить, что с Docker можно делать и хорошие вещи (см. ниже), но в Docker Hub есть куча образов размером больше 350 МБ.

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

Но даже если все добавленные зависимости идеальны, уверены ли мы, что их обновления безопасности доберутся до приложения для открывания гаражных дверей? Я задаюсь вопросом, сколько сейчас приложений Electron по-прежнему выпускается с уязвимой версией libwebp. Мы этого никогда не узнаем.

Однако хорошо известно, что все эти зависимости неидеальны. В экосистеме Node.js есть комедийные истории о том, как репозитории захватывают, похищают или восстанавливают под тем же именем другие люди, имеющие планы компрометации вашей безопасности. От подобных проблем страдал и PyPI. Зависимости всегда требуют внимательности, но нельзя ожидать, что человек сможет часто проверять тысячи зависимостей. Однако мы предпочитаем не думать об этом, вводим npm install и наблюдаем, как подтягиваются 1600 зависимостей.

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

Подведём итог. Я утверждаю, что мы 1) выпускаем слишком много кода 2) не знаем, что именно мы выпускаем и 3) недостаточно внимательно изучаем то, что выпускаем.

Trifecta

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

Решившись воспроизвести в небольшом масштабе проект Oberon Вирта, я тоже написал код в доказательство своей точки зрения, а ещё чтобы ещё раз убедиться, что я действительно понимаю, о чём говорю и пишу. Можно ли сегодня писать полезное и современное ПО «старомодным» образом?

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

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

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

И вот, что получилось в конечном итоге:

Призыв писать компактное ПО, версия 2024 года (с примером кода) - 2

Для сравнения: другое решение для обмена изображениями поставляется в виде образа Docker на 311 МБ, хотя, разумеется, оно выглядит лучше и имеет больше функций. Но они не настолько хороши, чтобы оправдывать лишние 308 МБ. Ещё один пример — это решение для обмена изображениями на основе Node, насчитывающее примерно 1600 зависимостей и суммарно содержащее больше четырёх миллионов строк JavaScript.

Trifecta — это автономное решение с небольшим количеством зависимостей, предоставляющее вам сайт обмена изображениями с полным набором функций:

  • Полное управление пользователями и сессиями

  • Возможность одновременного перетаскивания нескольких изображений

  • Посты могут содержать множество изображений

    • Каждый пост может иметь заголовок (необязательный), каждое изображение можно подписать

    • Посты могут быть публичными или временно публичными

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

    • Процесс восстановления пароля через электронную почту

  • Один куки, ограниченный только этим сайтом

  • Поставляется в виде исходного кода, двоичных файлов, docker, .deb или .rpm

  • Исходный код настолько мал, что его целиком можно прочитать за один день

  • Также исходный код можно использовать для других веб-фреймворков

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

Реакция

Она была довольно интересной. Как говорилось выше, мы совершенно сбрендили, если нам нужно 50 с лишним миллионов строк кода, чтобы открыть дверь в гараже. В том, что мы считаем это чем-то нормальным, определённо есть какая-то патология.

Несколько лет назад я делал в местном университете доклад по кибербезопасности под названием «Мы все сошли с ума?». Он стоит прочтения и сегодня, потому что мы действительно коллективно сошли с ума.

Пока чаще всего о Trifecta мне говорили, что для его развёртывания нужна целая куча сервисов AWS. Это чрезвычайно странная реакция на проект, чётко заявленная цель которого — создание автономного ПО, не зависящего от внешних сервисов. Я не совсем понимаю эту ситуацию.

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

Этот пост мне хотелось бы завершить наблюдениями из статьи Никлауса Вирта.

  • «Некоторые путают сложность с мощью. (…) Более того, люди всё больше путают сложность с совершенством, что удивляет меня — непостижимость должна вызывать подозрения, а не восхищение».

Я тоже замечал, что некоторые люди предпочитают сложные системы. Когда-то давно Тони Хоар отметил: «Существуют две методики проектирования ПО. Одна из них — сделать программу настолько простой, что в ней очевидно не будет ошибок. Другая — сделать её настолько сложной, что в ней не будет очевидных ошибок». Если вы не можете реализовать первый вариант, то, вероятно, второй начинает казаться ужасно привлекательным.

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

Зачем тратить недели на уменьшение объёмов ПО, если можно просто выпустить целый образ с предустановленной операционной системой, который уже работает?

  • «Чума разрастания ПО — это не "закон природы". Её можно избежать, и борьба с ней — задача разработчика ПО».

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

Подведём итог

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

Trifecta, как и проект Oberon Вирта, служит доказательством того, что можно обеспечить высокую функциональность на основе ограниченного объёма кода и зависимостей.

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

Автор:
PatientZero

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js