Help us understand the problem. What is going on with this article?

シェルスクリプトの中に安全にパスワードを埋め込む

More than 1 year has passed since last update.

シェルスクリプトの中でcurlやsshなどのコマンドにパスワードを渡したい場合に平文のテキストで書き込むよりは安全にパスワードを埋め込む方法を考えてみた。
コンセプトとしてはシェルスクリプト中にはシステム固有の情報で暗号化したパスワードを埋め込んで、シェルスクリプトが流出しても、システム固有の情報によってしか復号出来ない様にすることです。 (きっと、この方法でパスワードを扱えば、シェルスクリプトを間違えてgitなどのリポジトリに上げてしまっても安心?)

ちなみにパスワードの暗号化と復号にはopensslの暗号化(encオプション)を使い、パスフレーズにはシステム固有のUUID(macOSではHardware UUID、Linuxではsystem-uuid)を使っています。
※ Linuxでsystem-uuidを取得するのに利用しているdmidecodeはroot権限でしか実行できないので、/etc/sudersなどでスクリプトを実行するユーザーに/usr/sbin/dmidecodeの実行権限を与えておくひつようもあります。

/etc/sudoers
somebody    ALL=(ALL)   NOPASSWD:   /usr/sbin/dmidecode
encrypt_password.sh
#!/bin/sh
function encrypt_password() {
  plain_password=$1
  if [ "$(uname)" == 'Darwin' ]; then
    system_uuid=`system_profiler SPHardwareDataType |grep "Hardware UUID: [0-9A-Z]\{8\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{12\}" | sed -e 's/^.*Hardware UUID: \([0-9A-Z]\{8\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{12\}\).*$/\1/g'`
  elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then
    system_uuid=`sudo /usr/sbin/dmidecode -s system-uuid`
  else
    echo Unsupported system.
    exit
  fi
  encrypted_password=`echo "$plain_password" | openssl enc -e -des -base64 -k "$system_uuid"`
  echo $encrypted_password
}

# Usage
echo `encrypt_password $1`

上記のスクリプトを使って暗号化した文字列をシェルスクリプト内に埋め込んで使えば、少なくとも何らかの手違いでシェルスクリプトが外部に流出したとしても暗号化されたパスワードはそのシェルスクリプトが実行されるべきシステム以外では復号できないので安全なはず。 (まあ、システムをクラッキングされてシステムのUUIDをクラッカーに知られてしまえば終わりだけど…)

パスワードを暗号化する
$ encrypt_password.sh mypassword
U2FsdGVkX18m7HJ3Fbn0M3xc2+xG25DRM2b2zOhFUiA=
decrypt_password.sh
#!/bin/sh

function decrypt_password() {
  encrypted_password=$1
  if [ "$(uname)" == 'Darwin' ]; then
    system_uuid=`system_profiler SPHardwareDataType |grep "Hardware UUID: [0-9A-Z]\{8\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{12\}" | sed -e 's/^.*Hardware UUID: \([0-9A-Z]\{8\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{4\}-[0-9A-Z]\{12\}\).*$/\1/g'`
  elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then
    system_uuid=`sudo /usr/sbin/dmidecode -s system-uuid`
  else
    echo Unsupported system.
    exit
  fi
  plain_password=`echo "$encrypted_password" | openssl enc -d -des -base64 -k "$system_uuid"`
  echo $plain_password
}

# Useage:
# echo `decrypt_password $1`

decrypt_password.shの最後の行のコメントアウトを外せば引数に暗号化されたパスワードを渡して試せます。

パスワードを復号する
$ decrypt_password.sh U2FsdGVkX18m7HJ3Fbn0M3xc2+xG25DRM2b2zOhFUiA=
mypassword

例えば「bitbucketのリポジトリのバックアップを取得するスクリプトを書 いてみた」のスクリプトで使うならこんな感じでしょうか。

mirror_bitbucket_repos.sh
#!/bin/bash

SCRIPT_DIR=$(cd $(dirname $0); pwd)

. $SCRIPT_DIR/decrypt_password.sh

BITBUCKET_USER=bitbucket_user@email.address
BITBUCKET_PASSWORD=`decrypt_password U2FsdGVkX18m7HJ3Fbn0M3xc2+xG25DRM2b2zOhFUiA=`
BITBUCKET_OWNER=ownername

bitbucket_repositories=`curl --user $BITBUCKET_USER:$BITBUCKET_PASSWORD https://api.bitbucket.org/2.0/repositories/$BITBUCKET_OWNER | jq -r '.values[].links.clone[].href' | grep "^git"`

for repository_path in $bitbucket_repositories; do
  echo $repository_path
  dir=`echo "$(dirname $repository_path)" | sed -e 's/:/\//g' | sed -e 's/^git@//g'`
  if [ ! -d "$dir" ]; then
    mkdir -p "$dir"
  fi
  repo=$(basename $repository_path)
  if [ ! -d "$dir/$repo" ]; then
    (cd $dir; git clone --mirror $repository_path)
  else
    (cd "$dir/$repo"; git fetch --all)
  fi
done

まあ、いろいろと突っ込みどころもあるとは思いますが、パスワードを生のまま埋め込むよりは精神衛生上よろしいかと…

ただ、この方法の欠点としてはスクリプトは特定のシステムでしか使えないので、複数のシステムが連携して動作するシステムでは使えないのでCIシステムでのデプロイやChefやAnsibleでの設定の時も何らかの工夫が必要となるところでしょうか?

公開鍵暗号を使えばいいのかな?でも、その場合には秘密鍵はどうしよう…

kazuhidet
MZ-80向けのゲーム開発を皮切りに、その後は大手ゲームメーカーでのゲーム開発、某海外企業のCコンパイラ、DBインタープリタ/コンパイラのローカライズ、その後Linuxディストリビューション開発などを経て、現在はスマートフォン向けアプリ開発会社で技術アドバイザーに従事 尊敬する人物はステファン・ゲーリー・ウォズニアック
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした