Недавно слышал я о следующей ситуации. Украинская сторона выпустила некое видеообращение, претендующее на актуальность, а российская сторона утверждала, что оно было записано заранее. И я задумался, а как можно сделать так, чтобы можно было надёжно проверить, когда было создано то или иное видеосообщение. Конечно, есть очень простой способ, известный очень давно. Покупаешь свежую газету и записываешь своё сообщение, держа в руках и показывая зрителям её передовицу. Таким образом можно быть уверенным, что автор создал своё сообщение не ранее, чем был опубликован этот экземпляр газеты. Но в случае, если сообщение записывается правительством, как в примере выше, остаются некоторые сомнения. Правительство обладает определённым влиянием на местные газеты и может заставить их печатать нужную статью. Конечно, эта проблема решается достаточно просто. Можно попросить своего сторонника, скажем, в Америке сфотографировать передовицу какой-либо известной американской газеты, переслать её вам и показывать зрителям уже это фото. Но мне, как программисту, интересно, можно ли решить эту проблему так сказать программным путём. И в процессе это может быть возникнут некоторые идеи о том, как можно повысить доверие к используемым нами программным системам. Давайте пофантазируем, как может выглядеть система, решающая нашу задачу. Вместо передовицы Times автор сообщения показывает экран компьютера, на котором открыт браузер, показывающий определённый сайт (скажем clock.org). На этом сайте показано текущее время в UTC (например, 4 марта 2022 года 16:47) и некая последовательность букв и цифр (например 1G34HF4JH3). Для краткости  назовём эту последовательность кодом. И этот код меняется раз в минуту. Теперь перейдём на сторону зрителя. Пусть он смотрит это видео через неделю. Он видит, какое время и какой код присутствуют на записи. Желая убедиться, что видео было сделано именно в это время, он отправляется на другую страницу сайта clock.org, вводит так указанное время и код и сайт выдаёт ему, действительно ли этот код был показан в это время. В принципе для этой цели можно было бы использовать сайт какого-нибудь новостного агентства. Но во-первых, его содержимое может меняться недостаточно часто для наших целей, а во-вторых, у них обычно нет средств предоставляющих содержимое сайта на определённый момент времени в прошлом. Можно, конечно, попробовать вытащить это содержимое из какого-нибудь кэша Google, но вообще такой кэш не предназначен для нашей задачи. Данного содержимого может не быть или оно может быть удалено, чтобы освободить диск. Да и вообще, в независимость современных новостных агентств верится с трудом. Хотелось бы избежать всего этого. "Ну а твоему-то сайту можно ли доверять?" - спросите вы. "Может ты вошёл в сговор с автором сообщения и твой сайт показывает то, что нужно ему...". Что ж, пора разобраться с этой задачей. Мой сайт имеет некоторый исходный код. Если пользователь не знает этого кода, то у него нет оснований доверять ему. Мало ли что там крутится. Может я зашил туда какой-нибудь хитрый алгоритм генерации кодов по времени и сообщил этот алгоритм автору видеосообщения. И теперь он может генерировать эти коды для любого времени. Но что, если код сайта известен? Предположим, что я выложил его на GitHub. Сложно подозревать GitHub в том, что я подкупил его авторов. Да и не нужно. Любой может скачать код оттуда на свой компьютер. "Ну и что?" - спросите вы. "Как ты докажешь, что на сайте выполняется именно этот код?". А вот как. Во-первых, мне потребуется компилятор, который компилирует исходный код в исполняемый файл однозначно. Т. е. если вы возьмёте исходный код и 1000 раз выполните компиляцию, то все 1000 раз вы получите абсолютно одинаковые исполняемые файлы. Это значит, никаких меток времени компиляции, никаких внедрённых Guid для соответствия с отладочной информацией, ... Но технически не вижу никаких проблем. Кроме того, существуют некоторые языки (например, PHP), где и компилировать-то ничего не нужно. Достаточно исходного кода, который будет интерпретироваться на стороне hosting-провайдера. А теперь давайте перейдём к нему, к hosting-провайдеру. Нам потребуется от него небольшая помощь. Я хочу попросить его посчитать хэш для нескольких моих файлов. Я имею в виду те файлы, которые собственно и выполняют мой сайт на стороне провайдера. Это могут быть скомпилированные сборки или файлы исходного кода. Данный хэш провайдер будет показывать всем, кому это интересно. Выглядит это так. Вы заходите на сайт провайдера (не на clock.org, а на сайт провайдера, который хостит clock.org), вводите там в поле ввода clock.org и вам показывается указанный хэш, а также полные имена (с путями) всех файлов, по которым этот хэш посчитан. Чуть позже я объясню, зачем это нужно. Для полноты нам нужен ещё один кусочек. Мой сайт (clock.org) предоставляет также информацию о GitHub-репозитории, из которого он создан и идентификатор commit'а в нём. Итак, соберём всё вместе. Путь я хочу убедиться, что на сайте clock.org выполняется именно тот код, который заявляет автор. Я отправляюсь на clock.org и получаю там имя репозитория и идентификатор commit'а. Затем я иду на GitHub и скачиваю оттуда именно эту конкретную версию кода. После этого я осуществляю его компиляцию. Теперь я отправляюсь на сайт hosting-провайдера и получаю там хэш и список файлов, по которому построен этот хэш. Теперь на своём компьютере я считаю хэш по тем же файлам. Если хэши совпали, то всё в порядке. Если нет... То и доверия нет. Как видите, мы перешли от необходимости доверять владельцу сайта к необходимости доверять hosting-провайдеру. Но и это требование можно ослабить. Если мне очень нужно доверие пользователей, я могу разместить версии моего сайта по всему миру у различных провайдеров, находящихся в разных странах под разными юрисдикциями. Предположение о том, что я вошёл в сговор со всеми ними, будет уже немного чересчур. Ещё пара вопросов. Почему мы должны выбирать те файлы, по которым провайдер считает хэш? Почему бы не считать хэш по всей корневой папке моего сайта, рекурсивно обрабатывая все файлы и подпапки? Просто обычно сайт что-нибудь да пишет туда, хотя бы логи. Любые изменения будут менять и хэш, и система работать не будет. Лучше уж указать несколько неизменяемых файлов, по которым и будет считаться хэш. Конечно, в этот список файлов должно входить всё, что действительно запускается провайдером: index.php, web.config, ... Это может накладывать некоторые ограничения на то, что именно может храниться в этих файлах. Ведь они же должны быть загружены в GitHub и видны всем. Но, думаю, это не такое серьёзное ограничение. Всю конфиденциальную информацию можно передавать через переменные окружения. Я обещал рассказать, зачем провайдер должен показывать полные пути к тем файлам, по которым он считает хэш. В противном случае я мог бы пойти на такую уловку. Я залью провайдеру совершенно произвольный код. Но кроме того, я создам отдельную папочку, в которую положу скомпилированный код с GitHub, и попрошу провайдера вычислять хэш по файлам из этой папки. Т. е. выполняться будет один код, а хэш считаться по совершенно другому. Знание полных путей защищает от таких проблем. Теперь у нас по миру разбросано несколько копий моего сайта, установленных у разных провайдеров. Автор видео может открыть несколько из них и продемонстрировать, что все они показывают одно и то же время в UTC и один и тот же код. Зрители же смогут проверить любой из них в зависимости от того, какому провайдеру они больше доверяют. Стоп! Один и тот же код?! "Ну вот я тебя и поймал!" - скажете вы. "Совершенно очевидно, что твоему сайту нужна система хранения информации, какая-то база данных, в которой ты хранишь соответствие между временем и кодом. А у кого есть доступ к этой базе? Правильно, у тебя. Т. е. если ты захочешь, то сможешь внести в неё любую информацию, поменять любые значения на что угодно. Ну и как этому доверять?" Да, серьёзный вопрос. Во-первых скажу, что каждый из экземпляров моего сайта в принципе мог бы содержать своё отдельное независимое хранилище. Например, файл SQLite, лежащий в корневом каталоге. Но в таком случае я не знаю, как решить проблему доверия. Лучше я сниму этот груз с себя и переложу его на кого-нибудь другого. На провайдера БД. Все мои сайты будут общаться с одной и той же базой данных, доступ к которой через Интернет предоставляет мне провайдер БД. Адрес для доступа к базе данных, её имя, а так же имена всех используемых таблиц/коллекций (а возможно и имя пользователя) будут жёстко зашиты в мой код, так что любой с помощью уже описанной процедуры мог проверить, что я действительно обращаюсь к одной и той же базе данных, к одним и тем же таблицам, что я незаметно ничего не подменил. Через переменную окружения на провайдере устанавливается только пароль. "Ну и что это тебе дало? Ты по-прежнему способен вносить любые изменения в эту базу данных." - скажете вы. Да. Если только... Если только сама база данных не ограничивает то, что я могу сделать. Представьте себе базу данных, которая не позволяет вносить изменения в уже записанные данные и не позволяет удалять записи. Я могу только добавлять новые данные. Кроме того, существует запрет на создание нескольких записей с одним и тем же ключом. В данном случае в качестве ключа выступает время UTC с точностью до минуты. Но и это ещё не всё. Предположим я договорился с автором видео о том, что он хочет сегодня записать сообщение, которое должно выглядеть так, как будто оно записано через неделю. Для этого я вношу в базу данных несколько записей, соответствующих времени через неделю. Это ничему пока не противоречит. Я ничего не меняю и не удаляю, только вношу новые данные. Я не создаю конфликтов ключа. Они могут возникнуть впоследствии, когда мой код попытается внести в базу новые записи за то же время. Но такие конфликты легко обработать программно. Так что моя база данных должна ещё и гарантировать последовательность ключей. В каждый момент времени я могу добавить запись только со следующим ключом (только на следующую минуту за последней, уже внесённой в базу). Есть ещё один важный вопрос, который следует обсудить. А именно, кто вносит изменения в мою базу данных. Представьте себе, что экземпляры моего сайта содержат программный код, который вносит новые записи в нашу базу данных. Каждый из экземпляров раз в минуту генерирует новый код для следующей минуты и пытается записать его в базу данных. Только одному из них это удаётся (из-за ограничений ключа), остальные же получают сообщения о возникшем конфликте и считывают записанное значение кода из базы, чтобы всем всегда показывать один и тот же код для одного и того же времени. Достаточно ли этого для того, чтобы быть уверенным в том, что наши коды генерируются честно? К сожалению, нет. У меня дома может иметься некий Excel-файл, в котором все коды расписаны на несколько лет вперёд. Поскольку я обладаю ключом к базе данных, я могу где-нибудь запустить свою программку, которая будет записывать в базу данных значения из этого Excel-файла чуть раньше, чем это успевают делать экземпляры моих сайтов. Таким образом, я буду знать все коды на несколько лет вперёд, и могу воспользоваться этим знанием. Что же можно сделать, чтобы преодолеть эту проблему? Во-первых, можно на провайдере базы данных ограничить список IP, с которых можно подключаться к нашей базе данных. Конечно, для обеспечения доверия провайдер должен показывать всем желающим этот список, чтобы они могли убедиться, что изменения в базу данных можно вносить только с тех же IP, на которых развёрнуты наши сайты. Однако при этом необходимо как-то гарантировать, что я не сумел рядом с сайтом (т. е. на той же машине с тем же IP) запустить фоновый процесс, который занимается своей записью в базу данных. Мне кажется, что это сделать уже сложнее. Другой подход заключается в том, что наш сайт может предоставлять пользователям своеобразный журнал, в котором написано, удалось ли ему внести новое значение в базу данных, или он столкнулся с конфликтом и ему пришлось перечитать значение из базы. Тогда люди смогут (по крайней мере теоретически) обойти все экземпляры моего сайта и посмотреть, смог ли один из них записать код для данного времени, или все они столкнулись с конфликтом, что означало бы, что код записал кто-то другой. Но как хранить такой журнал? Если в виде файла на диске, то как удостовериться, что я не внёс в него нужные мне изменения? Ведь постоянно меняющийся журнал не поместить под хэш. Если же хранить его в памяти сервера, мы могли бы быть уверены в его правильности, поскольку доверяем исходному коду программы. Но такое хранилище было бы ограниченным по размеру. Его пришлось бы обрезать со временем. Это значит, что через определённый промежуток времени записи удалялись бы из него, и мы уже не могли бы проверить их. Кроме того, при перезагрузке экземпляра сайта по любой причине, журнал был бы потерян. Что же делать? На самом деле, уже довольно давно существует технология, позволяющая организовать нужное нам хранение. Я говорю о блокчейне. Он предоставляет неизменяемое (только для добавления новых записей) хранилище и валидацию новых блоков всем сообществом участников. Для наших целей блокчейн может быть организован следующим образом. Во-первых, блок с кодом для следующей минуты может сгенерировать любой участник. Во-вторых, процесс валидации проверяет что: Блок сгенерирован именно для следующей за последним уже имеющимся блоком минуты, а не на какое-то время в удалённом будущем. Блок содержит код, который не встречался ранее (или не встречался какое-то продолжительное время). Блок сгенерирован участником, который не генерировал ни одного из последних N блоков в цепочке. Эта проверка призвана исключить ситуацию, когда один участник сможет сгенерировать несколько последовательных блоков в цепочке. Конечно, и этот подход не лишён недостатков. Я всё же могу вставить в систему блок с произвольным кодом, который может быть известен мне заранее. Таким образом, я могу использовать его, если моё видеосообщение занимает меньше минуты. Кроме того, блоки должны генерироваться раз в минуту. При большом числе участников это может приводить к конфликтам, которые вызывают временные форки. Из-за этого наши сайты могут читать данные не из "правильной" ветки цепочки, что сделает систему бесполезной. Можно, конечно, попытаться генерировать блоки заранее, например, за день, в надежде на то, что за это время сообщество уже решит, какую из веток принять за правильную. Но в таком случае автор видеосообщения сможет записать его как бы на этот день вперёд. И, несомненно, остаётся вопрос о стимуле для генерации новых блоков. С какой стати множество людей бросится генерировать блоки с кодами для нашей системы? Нужен некоторый экономический стимул, как в случае с биткоином, но я его здесь не вижу. В общем вопросов остаётся ещё много. В своей статье я рассмотрел вопрос создания гипотетической программной системы, результатам работы которой можно было бы доверять, не обладая доверием к её авторам и владельцам. Выводы, которые я сделал для себя, следующие. Организовать доверие к программному коду сайта можно. Но на самом деле мы заменяем доверие к автору сайта доверием к hosting-провайдеру. Для надёжности можно разместить сайт в нескольких экземплярах у нескольких провайдеров, расположенных в разных странах и относящихся к разным юрисдикциям. Организация такого доверия требует некоторых усилий со стороны провайдеров, но не чрезмерных. С данными всё намного сложнее. Убедиться в том, что владелец сайта не вносил в них никаких изменений, очень сложно. Даже перенос некоторых проверок на сторону провайдера баз данных не решает проблему полностью. Возможно, что использование систем типа блокчейн и может в некоторых случаях решать проблему с доверием. Но такие системы показывают данные всем желающим, а это не всегда допустимо. Надеюсь, вы хотя бы развлеклись, путешествуя вместе со мной по миру доверия к программным системам. Если это наведёт вас на полезные мысли по этой теме, буду рад. Удачи вам! Программист