見出し画像

パスワードをハードコーディングせずに運用したい

初ノートの癖に覚書。
Perplexity使いながら、いろいろ調べて、自分なりに考えた方法をまとめておく。
ローカルでAPI使ったアプリとか、自動売買システムとか構築&運用するときに使用するであろう。

概要

  • やりたいこと
    サービスのID&パスワードをハードコーディングすることなく読み出す

  • 背景
    APIを自分のローカルマシンで動かすときに、アカウントとか、パスワードをハードコーディングしたくないなーと思った。

  • 環境
    Python

  • やったこと
    暗号化&復号化

  • 残課題
    開発・デバッグ時に毎回パスワード打つのが不便。
    もっといい方法があれば、是非ご紹介いただきたい。

採用した方法

(1)暗号化したID, パスワードをenvファイルに保存し、(2)運用側はID, パスワードを復号化して使用する。工夫点は以下。

  • パスワードは標準入力とすることでハードコーディングを防止

  • 暗号&復号用キーは自分が記憶できるパスワードから生成する

  • ソルトを使ったハッシュキー化により解読難易度を上げる

(1)ID, パスワードを暗号化し、.envファイルとして保存する

キー生成と暗号化には、secretsとcryptographyを使用した。
envファイルの読み出しは、dotenvを使用した。
pipでcryptographyとpython-dotenvをインストールする。

pip install cryptography python-dotenv

暗号化コードは以下。鍵導出関数はPBKDF2方式を使用している。

import secrets
import base64
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.fernet import Fernet

"""暗号化するためのキー&オブジェクト生成"""
# 標準入力から暗号化用のパスワードを入力する getpassを使用し、伏字入力
input_password = getpass.getpass('password?=')
# キー用にエンコード
password_bytes = input_password.encode()

# ソルト値をランダム生成
salt = secrets.token_bytes(16)
salt_str = base64.urlsafe_b64encode(salt).decode('utf-8')

# PBKDF2を使用してキーを生成
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=100000,
    backend=default_backend()
)
# キーの生成
key = base64.urlsafe_b64encode(kdf.derive(password_bytes))
# Fernetオブジェクトを生成
fernet = Fernet(key)

"""サービスパスワードの暗号化"""
# 標準入力からサービスのID, パスワードを入力する
service_id = input("service_id?=")
service_password = getpass.getpass(prompt='service_password?=')
# farnetオブジェクトでID, パスワードを暗号化
encrypted_service_id = fernet.encrypt(service_id.encode())
print("Encrypted ID= ",encrypted_service_id.decode(encoding='utf-8'))
encrypted_service_password = fernet.encrypt(service_password.encode())
print("Encrypted password=",encrypted_service_password.decode(encoding='utf-8'))

acount_file = "account.env")
with open(account_file,mode="w") as f:
    print(f"SALT={salt_str}", file=f)
    print(f"ENCRYPTED_SERVICE_ID={encrypted_service_id.decode()}", file=f)
    print(f"ENCRYPTED_SERVICE_PASSWORD={encrypted_service_password.decode()}", file=f)

実行結果
account.envに暗号化されたIDとパスワードが保存される。
以下はinput_password=a, service_id=b, service_password=cで実行した結果。

SALT=yuqWJ9nEhjI5iskhCK5F4w==
ENCRYPTED_SERVICE_ID=gAAAAABmvtjhzdQRd8iVdDgs255egzqOSHAyC8rdwnbYGqm3tMPBpTeeTPoIb6oC3RoUYJVa5mpyagUjkhkP8ArdrTDfRi4Z6g==
ENCRYPTED_SERVICE_PASSWORD=gAAAAABmvtjh1ovnwR7ntaJR_iPl-Ogohg-CdMHq6LzficS5R6dgolnvMaFwfSOU2MYyqDU-AeFlH8HourgLLzhXXZKadUAK3w==

(2)envファイルを読み込み、復号化する

復号化コードは以下。

import getpass
import base64
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.fernet import Fernet

# .envファイルを読み込む
env_path = r'config\test.env'
load_dotenv(dotenv_path=env_path)

# 暗号化されたパスワードを読み込む
salt_str = os.getenv("SALT").encode()
encrypted_service_id = os.getenv("ENCRYPTED_SERVICE_ID").encode()
encrypted_service_password = os.getenv("ENCRYPTED_SERVICE_PASSWORD").encode()

# ユーザーからパスワードを入力させる
input_password = getpass.getpass(prompt='Enter your password: ')
# 入力されたパスワードを復号化用のキーとして使用する
password_bytes = input_password.encode()

# saltをbyte文字に戻す
salt = base64.urlsafe_b64decode(salt_str)
# PBKDF2を使用して復号化用のキーを生成
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=100000,
    backend=default_backend()
)
# キーの生成
key = base64.urlsafe_b64encode(kdf.derive(password_bytes))
fernet = Fernet(key)
# 復号化
decrypted_service_id = fernet.decrypt(encrypted_service_id).decode()
decrypted_service_password = fernet.decrypt(encrypted_service_password).decode()

実行結果
復号化したidとパスワードを表示させると、以下になる。うまくいってる。

Decrypted ID= b
Decrypted password= c

思うところ

以下が欠点であり、不便。

  • 結局、解読用のパスワードを自分で記憶する必要がある。

  • 開発・デバッグ時にも毎回パスワードを打つことになる

一度パスワードを打ったら、しばらくは認証してくれるような運用がしたいのだけれど、良い方法はないものか。
この辺が私では限界なので、もっといい方法があれば教えていただきたい。

参考文献


いいなと思ったら応援しよう!