Представляю вам сайт bin.so — сервис для обмена текстовой информацией. Отличается от других функциями защиты данных:
- Каждому загруженному тексту выделяется уникальный случайный URL
- Если указать пароль, то информация шифруется прямо в браузере и на сервер сохраняется только зашифрованная версия данных. Для просмотра данных нужно ввести пароль, чтобы расшифровать в браузере данные, полученные с сервера.
- Поисковым системам запрещено индексировать содержимое сайта
- HTTPS везде
Шифрование данных в браузере обеспечено библиотекой SJCL. Данные шифруются AES алгоритмом в режиме GCM. Исходный код доступен на github. Серверная часть написана на python, веб-фреймворк Bottle, доступ к данным через peewee. База данных, по-умолчанию, sqlite.
Пользователи heroku могут запустить копию сайта в пару кликов мышки. Склонируйте репу на github. Через веб-интерфейс heroku создайте новое приложение и подключите github-репу.
Комментарии (77)
mwizard
07.10.2016 00:34+1Каково происхождение файла /static/sjcl.js? Он существенно отличается от официальной минифицированной версии. Как получить вашу версию из официальной?
itforge
07.10.2016 00:38+2Файл был скачан с cdnjs.com. Конкретно, вот ссылка на файл: https://cdnjs.cloudflare.com/ajax/libs/sjcl/1.0.6/sjcl.min.js
itforge
07.10.2016 00:36+1Вы правы. Удалил код счётчика. Придётся парсить логи каким-нибудь вебаналайзером, чтобы хоть какую-то статсу иметь по посещаемости и источниках трафика.
eternal_sorrow
07.10.2016 15:35неужели не осталось возможности как раньше, добавить на страницу пиксель счётчика чтобы получать минимальную статистику? все счётчики тянут с собой длинный js?
itforge
07.10.2016 15:36Осталось возможности. Например, liveinternet. Но я решил вообще убрать JS статистику.
vsb
07.10.2016 00:38+6Проблема шифрования в браузере в том, что я не могу быть уверен, что именно мне именно для моего браузера вы не отдадите чуточку модифицированные скрипты. И как эту проблему можно решить — непонятно.
itforge
07.10.2016 00:41+2Думаю — никак. Единственное решение, поднять копию сервиса на своём сервере.
rumkin
07.10.2016 03:32Думаю, можно сделать standalone-версию в виде архива с md5-хэшем содержимого. А тем кому "на постоянку" в electron завернуть.
itforge
07.10.2016 04:01standalone-версия чего? Исходного кода? А разве любой отдельной взятый commit в github репозитории не явялется такой standalone-версией?
dom1n1k
07.10.2016 00:45Написать для браузера расширение, которое при каждом заходе будет ломиться на гитхаб, брать оттуда эталонные копии и сравнивать контрольные суммы :)
zartdinov
07.10.2016 01:30+3Возможно будут интересны механизмы уже встроенные в последние версии браузеров, такие как Subresource Integrity, Content Security Policy и тд.
<script src="https://example.com/example-framework.js" integrity="sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7" crossorigin="anonymous"></script>
mwizard
07.10.2016 01:32+1Интегрити защищает от того, что CDN вместо ожидаемого jquery выдаст jquery с бонусом, собирающим пароли. Но интегрити не защищает от того, что владелец сайта выдаст вредоносный скрипт с правильным интегрити. Либо просто добавит еще один скрипт в страницу, который перезаписывает часть объектов и функций.
rumkin
07.10.2016 03:40Гипотетически для этого есть HTTP-заголовок Content-MD5 + исходники на github с GPG подписью. Но с автоматической проверкой будут проблемы.
itforge
08.10.2016 16:27На данный момент:
* сторонний JS грузится только через CDN с заданным integrity
* я зарефакторил client-side часть приложения в один HTML/JS файл. При желании можно сверять SHA-хэш содержимого страницы (то что изначально выдаёт веб-сервер по GET запросу) с хэшем файла на github.
mwizard
07.10.2016 01:31Проблема в общем случае заключается в том, чтобы указать, откуда именно брать эталонные копии (читай — чью подпись на скрипте проверять). Если доверить самому сайту указывать ориджин для каждого подключенного ресурса — чем это будет отличаться от существующего подхода, где владелец может выдавать что угодно? А если нет — то как?
rumkin
07.10.2016 03:46Я предполагаю что решить это можно с помощью локального прокси, который на все html-ки будет вычислять хеши и проверять в какой-нибудь распределенной БД с подписями для версий. Но это подходит для SPA и статических сайтов. Так же снизится скорость при первой загрузке. Ну и придется эту распределенную БД сделать и поддерживать )
yamatoko
07.10.2016 04:52+1зачем тогда сторонний сервис, не проще ли тогда свой сделать, вместо того, чтобы столько заморачиться?
mwambanatanga
07.10.2016 09:41Как вариант, решается путём использования механизма, существующего в нескольких реализациях. Например, PGP есть как в виде традиционного софта, так и в виде JS (например, https://github.com/openpgpjs/openpgpjs). Существование выбора позволяет — при наличии сомнений в подлинности скрипта — полностью отключить в браузере скрипты, шифровать текст оффлайновой программой и вставлять готовые кракозябры в поле ввода.
dcc0
07.10.2016 00:52Интересная идея. Т.е. вполне можно поднять в корпоративной сети и хранить копии документов.
InfiniteCode
07.10.2016 02:22Мне кажется, что такой идее, лучше было бы полностью хоститься на статическом сервисе — например S3. Ведь можно в принципе сообщения после криптовки ложить в ЮРЛ и отдавать человеку, либо в минимизатор. А он потом переходить по ссылке и декодирует используя пароль. Хотя конечно ограничение будет по длинне, но пок крайней мере тогда любой может такой поднять у себя создав просто бакет на амазоне.
Сейчас тот факт, что оно хранится на бекенде, уже не особо.itforge
08.10.2016 16:33Интересная идея, но думаю будет очень много проблем из-за ограничений браузеров, а также всяких промежуточных прокси-серверов. Кстати, придумал забавный вариант кодирования информации, можно настроить * сабдомен для домена и запихивать всю информацию в поддомены, разбивая на 63 символа (максимальная длинна сегмента в доменном имени).
Hacksli
07.10.2016 02:49Отличная идея. Чуть подшамашить с дизайном и вообще не плохо. Но все же идея больше для личного сервера чем для общественного, ибо те кто так заморачиваются сохранностью данных — не будут доверять сторонним сервисам. По реализации на питоне ничего не могу сказать ибо я пишу бекенд на php или nodejs. Поетому сам бы воспользовплся если бы оно было хотябы на php. А так добавлю в заметки идею. Может когда-то что-то подобное сделаю
itforge
08.10.2016 16:35Я зарефакторил код приложения, сейчас это один html/js файл (на riotjs) + парочка API вызовов, которые совсем не сложно будет сделать на PHP или nodejs
mwizard
07.10.2016 03:25+3Автор, я посмотрел немного на вашу реализацию, и у меня есть вопросы к тому, что вы делаете, и почему так.
Зашифрованные данные хранятся в виде base64, в котором json, в котором параметры шифра в base64. Зачем так сложно? У вас шифротексты распухают в несколько раз по сравнению с открытыми данными, причем без малейшего увеличения безопасности. Почему не использовать бинарную структуру с номером версии в первом байте?
Для того, чтобы сгенерировать новый адрес для дампа, вы десять раз генерируете случайное число в пределах 263 и пытаетесь создать запись в БД с таким первичным ключом. Если не получилось десять раз, то все, все пропало. Что это, и зачем? Откуда число 10? Почему вы не используете uuid + sha3 контента по вкусу?
Насколько вообще тут нужен SQLite, да и вообще, любая RDBMS, учитывая, что все виды запросов сводятся к записи по первичному ключу и к чтению по первичному ключу. Ни поисков, ни подсчетов. Зачем тут БД, которая еще и не масштабируется?
itforge
07.10.2016 03:59-3Я это всё по приколу за два дня написал. Плюс криптографию я знаю на уровне «Криптография? Не, не слышал». Поэтому есть простор для оптимизации :)
1) JSON запихал в base64 для красоты, чтобы не было видно потроха JSON. Насчёт размера данных не думал пока. Бинарная структура, как её встроить в страницу? Ведь при просмотре дампа данные должны быть как-то встроены в страницу перед тем, как можно будет их расшифровать с помощью пароля.
2) Число 10 от балды. Как именно из «uuid + sha3» получить число в диапазоне 1-2^63? Чем это будет отличаться от генерации случайного числа?
3) По-умолчанию, используется sqlite, чтобы можно было легко запустить приложение и начать с ним играться. Конкретно на bin.so сейчас postgres используется. В целом, вопрос не понял. Чем вам не нравится использование RDBMS и какую альтернативу вы предлагаете.ZyXI
07.10.2016 11:25+4Я бы сначала просто сохранял в файловой системе файлы вида
static/paste/614c9128/8c65/11e6/ba31/50465d597777
, отдавая их напрямую веб?сервером. И никаких БД вообще, пока с таких подходом не появятся (если вообще появятся) проблемы, которые решаются введением БД.itforge
07.10.2016 15:58У меня нет никаих проблем с БД, зачем мне заменять её на файловую систему?
mwizard
07.10.2016 15:59БД, как правило, не предназначены для использования в качестве ФС. В качестве доказательства своей правоты я загружу вам сотню дампов размером в гигабайт-два каждый. Для чего нужно использовать БД — для хранения соответствия какого-то внутреннего идентификатора и имени соответствующего файла. Так как у вас внутренний идентификатор и имя файла маппятся один в другой тривиальным образом, то БД не нужна.
itforge
07.10.2016 16:18Хорошо, для начала соглашусь с вами. Однако есть нюансы, о которых я не говорил. Я планирую запретить грузить дампы больше нескольких мегабайт на сервер. Как раз для того, чтобы никто не грузил туда сотню дампов размером в гигабайт-два :) Мой сервис не предназначен для обмена файлами, он предназначен для обмена текстовой информацией небольшого размера.
Думаю, размер дампа сейчас ограничен дефолтным значением nginx настройки client_max_body size, равной 1мб.
Можно я теперь буду таки использовать базу данных? :)mwizard
07.10.2016 16:22Конечно — вы можете хоть hdfs и кассандру для такой цели поднять, кто ж вам запретит. Просто вы пока не научились правильно выбирать инструмент под задачу. Когда у вас появится проблема, которую нужно решать при помощи БД, то тогда нужно использовать БД. Пока у вас такой проблемы нет, а если взять определение задачи — то и не появится.
mwizard
07.10.2016 13:44+4бинарную структуру можно встроить в страницу тем же base64, но при этом увеличение объема составит только 30%, либо запросить ее по сети с адреса вида
http://bin.so/raw/<paste-id>
, и тогда увеличения объема не будет вовсе, плюс не придется парсить<pre>
???р?????е???г??????у??л????я???рк?????ами?????.
- зачем вам число в диапазоне 1-2^63? Вам нужно получить уникальное число, которым нужно описать контент. Желательно, с первой попытки. UUID4 справляется с этим сам по себе, но если мы хотим быть совсем перестраховщиками, мы кроме случайной компоненты учитываем еще и хэш контента. В этом вроде как на первый взгляд нет практической необходимости, но у сервера может быть не очень качественный источник энтропии, и таким образом мы это немного исправляем. Для справки, UUID4 имеет размер 122 бита, SHA3 имеет размер 512 бит.
Предположим, каждую наносекунду на вашем сервисе содается один миллиард записей. В год это будет 31'536'000'000'000'000'000'000'000 записей, или, округляя вверх, 285. Вероятность того, что за год у нас будет сгенерировано два одинаковых идентификатора, составляет:
- только для UUID4 = 1 к 3 * 1011
- только для SHA3 = 1 к 7 * 10128
- для UUID4 + SHA3 = 1 к 4 * 10165
Википедия утверждает, что годовая вероятность того, что в отдельно взятого человека прямой наводкой упадет метеорит, составляет 1 к 6 * 1011, что примерно дает понимание масштаба указанных цифр. Для понимания масштаба количества записок в такой нотации, если каждая записка займет 1 байт, то за год их соберется 32 йобибайта (32768 зебибайта = 33554432 эксбибайта = 34359738368 пебибайта), или, если пересчитать на 15 ТБ SSD-модули от Samsung, где-то 24 мегатонны чистого кремния.
3) как верно заметил ZyXI комментарием ниже, файловая система конкретно для этого использования подходит намного лучше, т.к. у вас нет ничего такого, для чего понадобилась бы база данных.
И да, это классический совет, который не теряет своей актуальности. Если с криптографией вы знакомы на уровне "не, не слышал" — пожалуйста, не разрабатывайте свои криптоприложения, либо если очень хочется — не позиционируйте их как "защищенные", потому что это не так. Я еще не смотрел на реализацию шифрования у вас, есть шанс, что там будет тоже много забавного.
itforge
07.10.2016 15:54-2По поводу базы данных. У меня нет ничего такого, для чего бы мне понадобилось использовать файловую систему. База данных вполне решает проблему чтения-сохранения данных.
itforge
07.10.2016 16:11Число 2^64 это примерно 10 позиций числа в 52-ричной системе счисления. Откуда 52? Это латинские буквы в двух регистра плюс цифры минус буквы и цифры, которые похожи на другие буквы и цифры. То есть я выкинул i, l, 1, o, 0 и т.д. Так вот если у нас урл состоит из 10 буквоцифр, то его можно на бумажку записать. И уникальность вполне себе хорошая. Если взять uuid, то буквоцифр будет в два раза больше и записывать на бумажку можно, конечно, но уже труднее. И тут вопрос, для чего использовать длинные уникальные урлы, если можно использовать короткие уникальные урлы и это будет удобнее.
>>> math.log(2**64, 52)
11.227204069245088
>>> math.log(2**122, 52)
21.40185775699845mwizard
07.10.2016 16:21Ну тогда вам следовало назвать ваш сервис "удобным сервисом с короткими урлами", а не "защищенным с уникальными случайными урлами". Еще удобнее просто увеличивать на единичку каждую новую запись — компактность на высоте.
itforge
07.10.2016 16:47-2Я не понимаю, что вам не нравится. В данный момент сервис генеририрует случайные урлы для каждого дампа. Я объяснил, почему хочу, чтобы размер урла был около 10 байт. Пересчитайте это в мегатонны чистого кремния и россыпи SSD-дисков — достаточная же уникальность?
azsx
07.10.2016 04:34Спасибо, интересно.
1. Не верно работают пароли в кирилице.
2. Если ввести текст, пароль, нажать шифрование, а затем нажать кнопку в браузере назад — то поведение сайта становится не очевидным.itforge
07.10.2016 04:401. Попробовал пароли из чистой кириллицы, а также смесь кириллицы и латиницы — у меня работает всё.
stargrave2
07.10.2016 08:17Написано что используется режим шифрования SGM, но в коде я вижу «This script works only with data encrypted in AES GCM mode». Опечатка?
Antelle
07.10.2016 09:26+4Шифрование данных в браузере обеспечено библиотекой SJCL.
Советую посмотреть в сторону WebCrypto, он и быстрее, и вопросов не будет, чем вы шифруете. http://caniuse.com/#feat=cryptography
Домен в .so — хорошо. Ещё хорошо бы написать в FAQ, абузоустойчивый сервис или нет, удаляете ли пасту по запросу. Ещё самоудаляемые пасты по времени (или при первом просмотре) были бы полезны такому сервису.itforge
07.10.2016 16:30Удаление по времени я хочу сделать. Самоуничтожение по первому просмотру мне кажется ненадёжной функцией, хотя я затрудняюсь сформулировать, что там ненадёжного. Ну, например, я создал такое сообщение, а потом нечаянно его открыл ещё раз (нажал F5 в браузере) и не заметил. Послал ссылку человеку, а у того уже 404 отображается т.к. сообщение удалилось.
Сервис не абузоустойчивый. А такие вообще бывают? Ну, допустим, можно купить за 100-200 баков крайне лояльный к абузам хостинг, но ведь абузы могут и регистратору слать.
> http://caniuse.com/#feat=cryptography
Смущает, например, что на мобилках с андроидом 4.4 не будет работать.mihmig
07.10.2016 23:02+1Одноразовость прочтения решается установкой cookie в браузере автора, тогда хоть занажимайтесь F5
Но возникает проблема предпросмотра ссылок мессенджерами…prefrontalCortex
07.10.2016 23:18Можно юзерагент смотреть, например.
А мессенджеры нынче как на отправленные в них ссылки ходят? Тупо GET'ом берут и идут? Даже HEAD не пытаются?
mihmig
07.10.2016 15:53Раньше использовал как онлайн хранитель паролей keymemo.com
Для случаев, когда не на своём компе а надо записать сточно и даже карандаша нет использую
http://piratepad.net/ — в нём можно создавать свой URL, например http://piratepad.net/задача1
(но шифровать заметки вроде бы нельзя)
dom1n1k
08.10.2016 22:17Я бы переверстал страницу — все ссылки и настройки свалил бы в левую колонку в столбик, а textarea на ~75% ширины и 100% высоты.
itforge
08.10.2016 22:21С целью?
dom1n1k
09.10.2016 00:29С целью более удобного использования пространства. Сейчас textarea избыточно широка, но мала по высоте.
itforge
09.10.2016 02:40Я подумаю. А какая у вас ширина браузера? Я за стареньким 19" квадратным моником сижу :)
dom1n1k
09.10.2016 13:05FullHD у меня. И думаю, что сейчас это уже близко к минимуму для сегмента проф пользователей.
Если говорить о массовой публике, то там очень популярны ноуты 1366x768 — но в любом случае, это тоже широкий экран.
Квадратных мониторов сейчас, вроде бы, менее 10%.
dom1n1k
09.10.2016 15:23И настоятельно советую сменить Courier на нормально читаемый шрифт, благо такие давно есть. Как-то так примерно:
font-family: Consolas, Monaco, Andale Mono, Roboto Mono, DejaVu Mono, monospace;
mwizard
09.10.2016 15:50Или даже так, т.к. Menlo визуально плотнее Monaco.
font-family: Menlo, Consolas, Monaco, Andale Mono, Roboto Mono, DejaVu Mono, monospace;
mwizard
Немного, самую капельку, смущает webvisor от Яндекса, который отправляет в Яндекс все нажатия клавиш (включая контент и пароли), и адреса секретных страниц.
Reeze
Идея хорошая, НО…
В свете этой новости: https://geektimes.ru/post/281188/
Домен в зоне пиратского Сомали тоже не внушает.
Всё это поправимо.
itforge
По startcom ничего не скажу сейчас, надо разбираться.
По поводу .so хочу услышать подробности, как доменное имя влияет на защиту данных.
Reeze
Если сервис позиционируется действительно как защищённый, то сейчас бы надо принять хоть какие-нибудь действия, например, по временной замене сертификата до окончания «разборок» и чтения статьи выше.
По доменному имени моё чисто субъективное мнение.
itforge
Я правильно понял, что мне нужно поменять центр сертификации ДО чтения статьи? Потому что вы так сказали сделать? Я правильно понял? :)
Reeze
Вы статью выше прочитали?
Вообще, мне нравится ваш подход.
«Защищённый сервис, которому можно доверять»
Я завершаю комментирование в этой теме.
itforge
Статью читал, и не только её. Центр сертификации скорее всего поменяю.
antonwork
Слышал звон, да не знает, где он