파이썬 서버 secret key 이중 삼중 보안
getPass를 사용해 중요정보를 타이핑으로 입력받도록 하면 보안을 더 강화할 수 있습니다.
입력받는 키를 api key 직접으로 하면 기억을 할 수 없으므로,
api key를 암호화해서 .env에 넣어놓고,
그 api key를 복호화할 키를 기억해놨다가 입력하면 됩니다.
복호화할 키와, 그대로 쓸 키를 구분하기 위해서 “ENC_” 로 시작하는 키만 복호화하도록 구성할 수 있습니다.
1
2
3
4
5
6
7
def decrypt_env_value(value: str, key: str) -> str:
if value.startswith("ENC_"):
encrypted = value[len("ENC_") :]
return decrypt_password(encrypted, key)
return value
그리고 저의 경우는 .env도 이중으로 불러오도록 해놓았습니다. 비밀스러운 env는 좀더 접근하기 어려운 곳에 두는거죠.
그리고 저는 config.py와 config_loader.py를 두어서, config에서 Settings 싱글톤 인스턴스를 두고, config_loader는 기능적 메소드만 분리했습니다.
그 결과 다음처럼 작성할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# config_loader.py
def decrypt_env_value(value: str, key: str) -> str:
if value.startswith("ENC_"):
encrypted = value[len("ENC_") :]
return decrypt_password(encrypted, key)
return value
def load_envs(base_dir: Path, decrypt_key: str):
# 1. .env 먼저! 항상 override=True
base_env_path = base_dir / ".env"
if base_env_path.exists():
load_dotenv(dotenv_path=base_env_path, override=True, encoding="utf-8-sig")
print(f"[ENV] .env 로드됨: {base_env_path}")
else:
print(f"[ENV] .env 없음: {base_env_path}")
# 2. SECRET_PATH 다음! 항상 override=True
secret_path = os.getenv("SECRET_PATH")
if secret_path and Path(secret_path).exists():
load_dotenv(dotenv_path=secret_path, override=True, encoding="utf-8-sig")
print(f"[ENV] 비밀 설정 우선 적용됨: {secret_path}")
else:
print(f"[ENV] 비밀 설정 없음 또는 파일 없음: {secret_path}")
# 🔑 복호화키가 주어졌을 때만 복호화 시도
if decrypt_key:
for key, value in list(os.environ.items()):
if value and value.startswith("ENC_"):
try:
plain = decrypt_env_value(value, decrypt_key)
os.environ[key] = plain
print(f"[ENV] 암호화 값 복호화 적용: {key}")
except Exception as e:
print(f"[ENV] 복호화 실패: {key} ({e})")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# config.py
import os
import re
from typing import List, Optional
from pydantic_settings import BaseSettings
from core.config_loader import load_envs
from pathlib import Path
_settings = None
class Settings(BaseSettings):
...
def init_settings(base_dir: Path, decrypt_key: str = None):
global _settings
load_envs(base_dir, decrypt_key=decrypt_key)
_settings = Settings()
def get_settings() -> Settings:
if _settings is None:
raise RuntimeError("Settings not initialized. Call init_settings(base_dir) first.")
return _settings
다음은 제 main.py 입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ===================== 복호화 키 입력 =====================
parser = argparse.ArgumentParser()
parser.add_argument("--decrypt-key", "-k", type=str, help="환경변수 복호화용 키 입력")
args, unknown = parser.parse_known_args()
decrypt_key = args.decrypt_key
if not decrypt_key:
decrypt_key = getpass.getpass("환경변수 복호화 키를 입력하세요: ")
# ===================== 설정 초기화 =====================
project_root = Path(__file__).resolve().parent
init_settings(project_root)
settings = get_settings()
이렇게 설정해두면 서버가 해킹되더라도, api 키를 복호화할 수 있는 키는 제 머릿속에만 있기 때문에 api키를 보호할 수 있습니다.
그리고 저는 그러한 api키를 복호화하는데에 저의 수메리안 암호화 기능을 사용합니다. ㅎㅎ
맘에 드신다면 star 많이 눌러주세요~
https://github.com/southglory/sumerian-aes-vault/blob/main/version2/README.md
sumerian-aes-vault/version2/README.md at main · southglory/sumerian-aes-vault 🏺 Sumerian-AES Vault: Where ancient meets modern cryptography A unique password protection system that combines AES-256 encryption with Sumerian cuneiform visualization. Features GUI interface, bi… 🏺 Sumerian-AES Vault: Where ancient meets modern cryptography A unique password protection system that combines AES-256 encryption with Sumerian cuneiform visualization. Features GUI interface, bi…
크크크크 ㅋㅋ





