Последнее время пребываю в легком шоке от происходящего. И крупные компании, и Open Source сообщество показали чего стоят либеральные ценности. Риторика и пропаганда с обеих сторон просто поражает своим цинизмом и “человеколюбием”...
Все свои данные срочным порядком перенёс из внешних по отношении к РФ сервисов, сделал локальные бэкапы. Если перенос файлов, документов, почты, репозиториев сложностей не вызывает, то с паролями не так всё просто. Менеджеры паролей удобны, но уровень доверия к сторонним решениям всегда был невысок. При этом “набросать” своё решение вполне реально. Можно считать эту статью краткой инструкцией, поэтому: меньше слов, больше кода.
Crypto API есть и на фронте и на бэке, его подкрепляет множество стандартов. Однако, быстрее всего решить эту задачу через Bash. Должно получиться примерно 100 строк вместе с хэлпом. Суть решения от этого не поменяются, а, в моём случае, на этом можно и остановиться.
Как хранить секреты
Выбираем алгоритм шифрования на свой вкус. В данном случае вполне подойдёт AES-256 в режиме CBC. Про режимы шифрования есть статья на wiki. Получаем следующую команду для шифрования секретов:
$ echo -n "<секрет>" | openssl aes-256-cbc -nosalt -pbkdf2 -base64
Опция -n в данном случае позволяет избавиться от символа перевода строки в конце, в противном случае, перевод строки тоже будет закодирован openssl.
PBKDF2 - стандарт формирования ключа на основе пароля - необходимо указать во избежание назойливого warning.
В момент вызова openssl попросит ввести пароль, который в данном случае является мастер-паролем. На выходе получим зашифрованный секрет в формате Base64, который можно “положить” в любое удобное место. Например, в пользовательскую директорию:
$ echo -n "<секрет>" | openssl aes-256-cbc -nosalt -pbkdf2 -base64 \
> ~/.imn/"<имя секрета>".pub
Копию я закинул на Яндекс.Диск ...
Соответственно, чтобы восстановить зашифрованный секрет достаточно произвести обратную операцию:
$ openssl aes-256-cbc -pbkdf2 -base64 -d < ~/.imn/"<имя секрета>".pub
Опция -d указывает на дешифрование, остальные опции openssl неизменны по понятным причинам. При вызове потребуется ввести мастер-пароль.
Вот так просто и без затей... Осталось несколько нюансов.
Как генерировать секреты
Брать секреты “из головы” не только неудобно (при наличии мастер-пароля), но и небезопасно. Наша утилита должна уметь генерировать криптостойкие секреты, по аналогии с многочисленными собратьями.
И снова openssl помогает решить проблему:
$ openssl rand -base64 "<длина секрета>"
Кратная трём длина секрета позволит избежать знаков равенства в конце генерируемой строки, которые служат в кодировке Base64 для “набивки”.
Удобно совместить операции генерации, шифрования и сохранения секрета:
$ local secret=$(openssl rand -base64 "<длина секрета>")
$ echo -n "$secret" | openssl aes-256-cbc -nosalt -pbkdf2 -base64 \
> ~/.imn/"<имя секрета>".pub
Копирование “свежего” секрета в буфер обмена варьируется от операционной системы. В Linux - это, скорее всего, xlip:
$ echo "$secret" | xclip
В WSL под Windows - это clip.exe:
$ echo "$secret" | clip.exe
Как импортировать секреты из Chrome
Хранить секреты в менеджере паролей Chrome конечно удобно, но... с учетом новых обстоятельств, лучше не стоит.
Перенос секретов из Chrome, ровно как и из других менеджеров паролей - задача тривиальная. Экспортируем секреты в формате CSV. Получится нечто следующее:
# name,url,usename,password
,<https://foo.com/bar,ivanov,qwerty>
,<https://plugh.com/quux,petrov,asdfg>
Первая строка в CSV формате - это перечисление имён колонок. Отбрасываем строку с метаинформацией (парсим начиная со второй +2):
$ cat chrome-secrets.csv | tail -n+2
И парсим при помощи read строка за строкой:
cat chrome-secrets.csv | tail -n+2 | \
while IFS=',' read -r name url username secret; do
echo Имя секрета: "$url@$username"
echo Секрет: "$secret"
done
В качестве уникального имени секрета лучше использовать сочетание URL и логин.
Полный листинг
Чтобы зря не тратить ваше время приведу то, что получилось у меня:
#!/usr/bin/env bash
DIR=~/.imn
ALGO=(aes-256-cbc -pbkdf2 -base64)
mkdir -p "$DIR"
store() {
local name="$1"
shift
local secret
secret=$(rand_secret "$@")
store_secret "$name" "$secret"
}
rand_secret() {
if [[ $# -gt 0 ]]; then
openssl rand "$@"
else
openssl rand -base64 30
fi
}
store_secret() {
local name="$1"
local secret="$2"
shift 2
echo -n "$secret" | openssl "${ALGO[@]}" "$@" > "$DIR"/"$name".pub
print_secret "$secret"
}
restore() {
assert_pub_key "$1"
local name="$1"
local secret
secret=$(openssl "${ALGO[@]}" -d < "$DIR"/"$name".pub)
print_secret "$secret"
}
assert_pub_key() {
local name="$1"
if [[ ! -f "$DIR"/"$name".pub ]]; then
echo Public key "\"$name\"" not found >&2
exit 1
fi
}
print_secret() {
printf '%s\n' "$1"
}
import_chrome_csv() {
# name,url,usename,password
cut -f2-4 -d',' | import_csv "$@"
}
import_csv() {
grep -v password | while IFS=',' read -r url username secret; do
local origin="${url#*://}"
origin="${origin%%/*}"
local name="$origin@$username"
echo "$name"
store_secret "$name" "$secret" "$@" >/dev/null
done
}
case "$1" in
--help|-h)
echo "Generate and store random secret:"
echo " imn.sh store|s <name>"
echo "Restore secret:"
echo " imn.sh [restore|r] <name>"
echo "Import secrets from Chrome's CSV:"
echo " imn.sh chrome <path to CSV> -k <master password>"
;;
chrome)
shift
csv=$1
shift
import_chrome_csv "$@" < "$csv"
;;
store|s|gen|g)
shift
store "$@"
;;
restore|r)
shift
restore "$@"
;;
*)
restore "$@"
;;
esac
Если Вы такой же параноик (в хорошем смысле) как и я, милости прошу в комментарии, всегда рад пообщаться с единомышленниками.
Комментарии (20)
polearnik
22.03.2022 17:28+8используйте keepass и не парьте себе и окружающим мозги. если переживаете о том что изза вашего гражданства пароли могут быть слиты то берите релиз до конца февраля. ИНаче создаете опасное и обманчивое впечатление что работа с криптографией это просто и безопасно.
imbasoft
22.03.2022 17:44+1" Соль в данном случае не ясно для чего может пригодиться. "
Соль в данном случае требуется равно для того же, для чего и всегда, чтобы одинаковые зашифрованные отличались между собой (при условии что используется одинаковый ключ шифрования).
Например, в сайта site.com у вас пароль "1" и для шифрования вы используете ключ, формируемый из пароля 1.
echo -n "1" | openssl aes-256-cbc -nosalt -pbkdf2 -base64
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
fu1Qc8Y+YiB4bZ0UZhWhLA==Если для сайта site.ru вы используете тот же пароль, то зашифрованные данные
fu1Qc8Y+YiB4bZ0UZhWhLA== очевидно будут совпадать.
Тут разумно сказать, что из
fu1Qc8Y+YiB4bZ0UZhWhLA== получить искомый пароль 1 довольно сложно. НО! Если злоумышленник похачит site.com, то он автоматически получит доступ и на site.ru, что не есть здорово.
Безопасность складывается из мелочей, чем больше мелочей отбрасывается, тем больше безопасность превращается в иллюзию безопасности.
radneck Автор
22.03.2022 17:48Это понятно. Согласен. С учетом того, что сам секрет генерится рандомно, этот подход особо ничего не даёт.
Revertis
22.03.2022 17:47Зачем что-то хранить, если всегда можно перегенерировать?
Ritan
22.03.2022 18:05+1Затем, что при таком способе компрометация(а точнее последующая смена) одного пароля требует смены мастер-пароля, а следовательно смены паролей во всех сервисах, пароли которых сгенерированы таким образом
Revertis
22.03.2022 18:13+1Зачем менять весь мастер-пароль? Если на каком-то сервисе произошла утечка, или просто требуют сменить пароль, к мастеру добавляете 1, и весь новый пароль меняется. За все много лет использования мне нужно было такое 2 раза, и уж любой конкретный единичный случай поддаётся запоминанию.
Ritan
22.03.2022 19:03Добавили к мастер-паролю 1 - изменились все сгенерированный пароли. Т.е. теперь варианты: либо хранить где-то список сервисов, для которых происходили утечки, чтобы добавлять единицу(две единицы итд) либо менять пароли всех сервисов при каждой утечке. А утечки сейчас происходят с завидным постоянством
Revertis
22.03.2022 19:05Я вам пишу: список заменённых паролей состоит из 1-2 объектов. Замечательно помещается в памяти среднего человека.
Kirikekeks
22.03.2022 23:25Мне удобнее монтировать файл как luks диск. И уже в шифрованном пространстве вести человеческие пароли, заметки. И я делаю его неудаляемым через chattr, что и в Ваш скрипт добавить - две строки, а польза большая.
kovserg
Чем не устроил pass
radneck Автор
Решений масса, но моя паранойя привела меня к DIY ;)
Люблю всё готовить сам)
mibori
хотя автор совершает ту же ошибку, меня, вот, например, в pass не устраивает то, что владелец хостинга знает, какими сервисами ты пользуешься, либо тебе приходится шифровать/облако репозиторий.
radneck Автор
Тема посильная всем. Поэтому любые ваши идеи вполне можно самостоятельно реализовать, отталкиваясь от данного примитивного решения.