DIY Хранение Паролей

от автора

Последнее время пребываю в легком шоке от происходящего. И крупные компании, и 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 -nosalt -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 -nosalt -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

Если Вы такой же параноик (в хорошем смысле) как и я, милости прошу в комментарии, всегда рад пообщаться с единомышленниками.


ссылка на оригинал статьи https://habr.com/ru/post/656915/