programing

파이썬에서 사용자 이름과 암호를 안전하게 저장해야 하는데, 옵션은 무엇입니까?

closeapi 2023. 7. 25. 20:58
반응형

파이썬에서 사용자 이름과 암호를 안전하게 저장해야 하는데, 옵션은 무엇입니까?

사용자 이름과 비밀번호 조합을 사용하여 주기적으로 타사 서비스에서 정보를 가져오는 작은 파이썬 스크립트를 작성하고 있습니다.저는 100% 방탄인 것을 만들 필요는 없지만(100%도 존재합니까?), 누군가가 그것을 부수는 데 최소한 오랜 시간이 걸릴 수 있도록 보안에 대한 좋은 조치를 포함하고 싶습니다.

으로 이스를트 GUI포에 의해 실행됩니다.cron따라서 암호를 해독하기 위해 실행할 때마다 암호를 입력하면 실제로 작동하지 않으며, 사용자 이름과 암호를 암호화된 파일이나 SQLite 데이터베이스에 저장해야 합니다. 이 경우 SQLite를 사용하기 때문에 나중에 암호를 편집해야 할 수도 있습니다.또한 이 시점에서는 Windows 전용이므로 전체 프로그램을 EXE로 포장할 것입니다.

정기적으로 사용할 사용자 이름 및 암호 콤보를 안전하게 저장하려면 어떻게 해야 합니까?cron 직업?

python 키링 라이브러리는 사용자의 로그온 자격 증명으로 데이터를 암호화하는 Windows의 API(Mac 및 Linux의 관련 API와 함께)와 통합됩니다.

단순 사용:

import keyring

# the service is just a namespace for your app
service_id = 'IM_YOUR_APP!'

keyring.set_password(service_id, 'dustin', 'my secret password')
password = keyring.get_password(service_id, 'dustin') # retrieve password

사용자 이름을 키링에 저장하려는 경우 사용:

import keyring

MAGIC_USERNAME_KEY = 'im_the_magic_username_key'

# the service is just a namespace for your app
service_id = 'IM_YOUR_APP!'  

username = 'dustin'

# save password
keyring.set_password(service_id, username, "password")

# optionally, abuse `set_password` to save username onto keyring
# we're just using some known magic string in the username field
keyring.set_password(service_id, MAGIC_USERNAME_KEY, username)

나중에 키링에서 정보를 가져옵니다.

# again, abusing `get_password` to get the username.
# after all, the keyring is just a key-value store
username = keyring.get_password(service_id, MAGIC_USERNAME_KEY)
password = keyring.get_password(service_id, username)  

항목은 사용자의 운영 체제 자격 증명으로 암호화되므로 사용자 계정에서 실행 중인 다른 응용 프로그램이 암호에 액세스할 수 있습니다.

이러한 취약성을 조금이라도 숨기려면 암호를 키 링에 저장하기 전에 암호를 암호화/난독화할 수 있습니다.물론 스크립트를 대상으로 하는 사람이라면 누구나 소스를 보고 암호를 암호화/암호 해독 해제하는 방법을 알아낼 수 있지만, 일부 응용 프로그램이 볼트의 모든 암호를 진공 청소하고 사용자의 암호를 가져오는 것은 방지할 수 있습니다.

Python 프로그램이 사용해야 하는 암호 및 기타 암호를 저장하기 위한 몇 가지 옵션이 있습니다. 특히 사용자에게 암호를 입력하도록 요청할 수 없는 백그라운드에서 실행해야 하는 프로그램입니다.

피해야 할 문제:

  1. 다른 개발자 또는 일반인도 볼 수 있는 소스 제어에 암호를 체크인합니다.
  2. 구성 파일 또는 소스 코드에서 암호를 읽는 동일한 서버의 다른 사용자
  3. 사용자가 암호를 편집하는 동안 다른 사용자가 사용자의 어깨 너머로 볼 수 있는 원본 파일에 암호를 저장합니다.

옵션 1: SSH

이것이 항상 선택사항은 아니지만, 아마도 최고일 것입니다.사용자의 개인 키는 네트워크를 통해 전송되지 않으며 SSH는 사용자가 올바른 키를 가지고 있다는 것을 증명하기 위해 수학적 계산을 실행합니다.

이 작업을 수행하려면 다음이 필요합니다.

  • 액세스 중인 데이터베이스 또는 모든 데이터베이스에 SSH로 액세스할 수 있어야 합니다."SSH"와 액세스 중인 모든 서비스를 검색해 보십시오.예를 들어 "sssh postgresql"이 있습니다.데이터베이스의 기능이 아닌 경우 다음 옵션으로 이동합니다.
  • 데이터베이스에 호출할 서비스를 실행할 계정을 만들고 SSH 키를 생성합니다.
  • 호출할 서비스에 공용 키를 추가하거나 해당 서버에 로컬 계정을 만든 후 공용 키를 설치합니다.

옵션 2: 환경 변수

여기가 가장 간단해서 시작하기에 좋을 것 같습니다.그것은 12가지 요소 앱에 잘 설명되어 있습니다.기본적인 개념은 소스 코드가 환경 변수에서 암호나 다른 암호를 가져온 다음 프로그램을 실행하는 각 시스템에서 환경 변수를 구성한다는 것입니다.대부분의 개발자가 사용할 수 있는 기본값을 사용하는 경우에도 유용할 수 있습니다.소프트웨어를 "기본적으로 안전하게" 만드는 것과 비교하여 균형을 맞춰야 합니다.

다음은 환경 변수에서 서버, 사용자 이름 및 암호를 가져오는 예제입니다.

import os

server = os.getenv('MY_APP_DB_SERVER', 'localhost')
user = os.getenv('MY_APP_DB_USER', 'myapp')
password = os.getenv('MY_APP_DB_PASSWORD', '')

db_connect(server, user, password)

운영 체제에서 환경 변수를 설정하는 방법을 찾아보고 자체 계정으로 서비스를 실행하는 것을 고려합니다.이렇게 하면 사용자 계정에서 프로그램을 실행할 때 환경 변수에 중요한 데이터가 포함되지 않습니다.이러한 환경 변수를 설정할 때는 다른 사용자가 읽을 수 없도록 각별히 주의해야 합니다.예를 들어 파일 사용 권한을 확인합니다.물론 루트 권한을 가진 사용자라면 읽을 수 있지만 어쩔 수 없습니다.systemd를 사용하는 경우 서비스 유닛을 살펴보고 사용에 주의하십시오.EnvironmentFileEnvironment어떤 비밀이라도Environment▁user▁can다▁by▁values▁be▁viewed니로 누구나 볼 수 있습니다.systemctl show.

옵션 3: 구성 파일

이는 환경 변수와 매우 유사하지만 텍스트 파일에서 암호를 읽습니다.저는 여전히 환경 변수가 배포 툴 및 지속적인 통합 서버와 같은 것에 더 유연하다고 생각합니다.구성 파일을 사용하기로 결정한 경우 Python은 JSON, INI, netrcXML과 같은 여러 형식을 표준 라이브러리에서 지원합니다. PyYAML 및 TOML과 같은 외부 패키지도 찾을 수 있습니다. 개인적으로 JSON 및 YAML이 가장 사용하기 쉽다고 생각하며, YAML은 주석을 허용합니다.

구성 파일과 관련하여 고려해야 할 세 가지 사항

  1. 파일은 어디에 있습니까?기본 위치는 다음과 같습니다.~/.my_app다른 위치를 사용할 수 있는 명령줄 옵션이 있습니다.
  2. 다른 사용자가 파일을 읽을 수 없는지 확인합니다.
  3. 구성 파일을 소스 코드에 커밋하지 마십시오.사용자가 홈 디렉토리에 복사할 수 있는 템플리트를 커밋할 수 있습니다.

옵션 4: Python 모듈

어떤 프로젝트들은 파이썬 모듈에 그들의 비밀을 바로 집어넣습니다.

# settings.py
db_server = 'dbhost1'
db_user = 'my_app'
db_password = 'correcthorsebatterystaple'

그런 다음 해당 모듈을 가져와 값을 가져옵니다.

# my_app.py
from settings import db_server, db_user, db_password

db_connect(db_server, db_user, db_password)

이 기술을 사용하는 프로젝트 중 하나가 장고입니다.분명히, 당신은 범죄를 저지르면 안 됩니다.settings.py제어를 소스제를위해, 다파커있다수니습라는 수도 .settings_template.py사용자가 복사하고 수정할 수 있습니다.

이 기술에는 몇 가지 문제가 있습니다.

  1. 개발자가 실수로 파일을 소스 제어에 커밋할 수 있습니다..gitignore위험을 줄일 수 있습니다.
  2. 일부 코드가 소스 제어 하에 있지 않습니다.징계를 받고 여기에 줄과 숫자만 넣으면 문제가 되지 않습니다.여기에 로깅 필터 클래스를 쓰기 시작하면 중지하십시오!

프로젝트에서 이미 이 기술을 사용하는 경우 환경 변수로 쉽게 전환할 수 있습니다.모든 설정 값을 환경 변수로 이동하고 환경 변수에서 읽도록 Python 모듈을 변경하기만 하면 됩니다.

이 질문과 관련 질문에 대한 답을 살펴본 후, 저는 비밀 데이터를 암호화하고 모호하게 만드는 몇 가지 제안된 방법을 사용하여 몇 가지 코드를 작성했습니다.이 코드는 특히 스크립트를 사용자 개입 없이 실행해야 할 경우에 사용됩니다(사용자가 수동으로 스크립트를 시작하는 경우에는 이 질문에 대한 대답에 따라 암호를 입력하고 메모리에만 저장하는 것이 가장 좋습니다).이 방법은 매우 안전하지 않습니다. 기본적으로 스크립트는 전체 시스템 액세스 권한을 가진 모든 사용자가 스크립트 및 관련 파일을 가지고 액세스할 수 있도록 비밀 정보에 액세스할 수 있습니다.이렇게 하면 데이터가 일상적인 검사에서 모호해지고 스크립트 없이 개별적으로 또는 함께 검사되는 경우 데이터 파일 자체가 안전하게 유지됩니다.

제가 이것을 하게 된 동기는 거래를 모니터링하기 위해 제 은행 계좌 중 일부를 폴링하는 프로젝트입니다. 저는 한두 분마다 비밀번호를 다시 입력하지 않고 백그라운드에서 실행해야 합니다.

스크립트 맨 위에 이 코드를 붙여넣고 saltSeed를 변경한 다음 필요에 따라 코드에서 store() retrieve() 및 require()를 사용합니다.

from getpass import getpass
from pbkdf2 import PBKDF2
from Crypto.Cipher import AES
import os
import base64
import pickle


### Settings ###

saltSeed = 'mkhgts465wef4fwtdd' # MAKE THIS YOUR OWN RANDOM STRING

PASSPHRASE_FILE = './secret.p'
SECRETSDB_FILE = './secrets'
PASSPHRASE_SIZE = 64 # 512-bit passphrase
KEY_SIZE = 32 # 256-bit key
BLOCK_SIZE = 16  # 16-bit blocks
IV_SIZE = 16 # 128-bits to initialise
SALT_SIZE = 8 # 64-bits of salt


### System Functions ###

def getSaltForKey(key):
    return PBKDF2(key, saltSeed).read(SALT_SIZE) # Salt is generated as the hash of the key with it's own salt acting like a seed value

def encrypt(plaintext, salt):
    ''' Pad plaintext, then encrypt it with a new, randomly initialised cipher. Will not preserve trailing whitespace in plaintext!'''

    # Initialise Cipher Randomly
    initVector = os.urandom(IV_SIZE)

    # Prepare cipher key:
    key = PBKDF2(passphrase, salt).read(KEY_SIZE)

    cipher = AES.new(key, AES.MODE_CBC, initVector) # Create cipher

    return initVector + cipher.encrypt(plaintext + ' '*(BLOCK_SIZE - (len(plaintext) % BLOCK_SIZE))) # Pad and encrypt

def decrypt(ciphertext, salt):
    ''' Reconstruct the cipher object and decrypt. Will not preserve trailing whitespace in the retrieved value!'''

    # Prepare cipher key:
    key = PBKDF2(passphrase, salt).read(KEY_SIZE)

    # Extract IV:
    initVector = ciphertext[:IV_SIZE]
    ciphertext = ciphertext[IV_SIZE:]

    cipher = AES.new(key, AES.MODE_CBC, initVector) # Reconstruct cipher (IV isn't needed for edecryption so is set to zeros)

    return cipher.decrypt(ciphertext).rstrip(' ') # Decrypt and depad


### User Functions ###

def store(key, value):
    ''' Sore key-value pair safely and save to disk.'''
    global db

    db[key] = encrypt(value, getSaltForKey(key))
    with open(SECRETSDB_FILE, 'w') as f:
        pickle.dump(db, f)

def retrieve(key):
    ''' Fetch key-value pair.'''
    return decrypt(db[key], getSaltForKey(key))

def require(key):
    ''' Test if key is stored, if not, prompt the user for it while hiding their input from shoulder-surfers.'''
    if not key in db: store(key, getpass('Please enter a value for "%s":' % key))


### Setup ###

# Aquire passphrase:
try:
    with open(PASSPHRASE_FILE) as f:
        passphrase = f.read()
    if len(passphrase) == 0: raise IOError
except IOError:
    with open(PASSPHRASE_FILE, 'w') as f:
        passphrase = os.urandom(PASSPHRASE_SIZE) # Random passphrase
        f.write(base64.b64encode(passphrase))

        try: os.remove(SECRETSDB_FILE) # If the passphrase has to be regenerated, then the old secrets file is irretrievable and should be removed
        except: pass
else:
    passphrase = base64.b64decode(passphrase) # Decode if loaded from already extant file

# Load or create secrets database:
try:
    with open(SECRETSDB_FILE) as f:
        db = pickle.load(f)
    if db == {}: raise IOError
except (IOError, EOFError):
    db = {}
    with open(SECRETSDB_FILE, 'w') as f:
        pickle.dump(db, f)

### Test (put your code here) ###
require('id')
require('password1')
require('password2')
print
print 'Stored Data:'
for key in db:
    print key, retrieve(key) # decode values on demand to avoid exposing the whole database in memory
    # DO STUFF

스크립트 자체만 읽을 수 있도록 비밀 파일에 os 권한을 설정하고 스크립트 자체를 컴파일하여 실행 파일로만 표시하면(읽을 수 없음) 이 메서드의 보안이 크게 향상됩니다.그 중 일부는 자동화될 수 있지만, 저는 신경쓰지 않았습니다.스크립트에 대한 사용자를 설정하고 스크립트를 해당 사용자로 실행하고 스크립트 파일의 소유권을 해당 사용자로 설정해야 합니다.

저는 누구나 생각할 수 있는 제안, 비판 또는 기타 취약한 점이 있으면 좋겠습니다.저는 암호화 코드를 쓰는 것에 익숙치 않아서 제가 한 일이 거의 확실히 개선될 수 있었습니다.

저는 ssh-agent와 유사한 전략을 추천합니다.만약 당신이 ssh-agent를 직접 사용할 수 없다면, 당신은 그것과 같은 것을 구현할 수 있을 것이고, 그래서 당신의 비밀번호는 오직 RAM에만 유지될 것입니다.cron 작업은 실행될 때마다 에이전트로부터 실제 암호를 가져오도록 자격 증명을 구성하고, 이 암호를 한 번 사용하고, 다음을 사용하여 즉시 참조 해제할 수 있습니다.del진술.

관리자는 부팅 시 또는 기타 방법으로 SSH-에이전트를 시작하려면 여전히 암호를 입력해야 하지만, 이는 일반 텍스트 암호가 디스크에 저장되는 것을 방지하는 합리적인 방법입니다.

암호를 암호화하는 것은 별로 의미가 없습니다. 암호를 숨기려는 사람은 암호를 해독할 코드가 있는 파이썬 스크립트를 가지고 있습니다.비밀번호를 얻는 가장 빠른 방법은 타사 서비스에서 비밀번호를 사용하기 직전에 Python 스크립트에 인쇄문을 추가하는 것입니다.

따라서 암호를 스크립트에 문자열로 저장하고 base64는 파일 읽기만으로는 충분하지 않도록 암호를 인코딩한 다음 하루를 끝냅니다.

실행 중인 스크립트 파일과 시스템을 보호하는 것이 최선이라고 생각합니다.

기본적으로 다음을 수행합니다.

  • 파일 시스템 권한 사용(chmod 400)
  • 시스템의 소유자 계정에 대한 강력한 암호
  • 시스템 손상 가능성 감소(방화벽, 불필요한 서비스 비활성화 등)
  • 필요하지 않은 사용자에 대한 관리/루트/sudo 권한

일반적으로 언급되는 다른 라이브러리를 시스템에 설치(컴파일)하는 데 문제가 있어서 암호화를 사용했습니다.(Win7 x64, Python 3.5)

from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher_suite = Fernet(key)
cipher_text = cipher_suite.encrypt(b"password = scarybunny")
plain_text = cipher_suite.decrypt(cipher_text)

내 스크립트가 물리적으로 안전한 시스템/룸에서 실행되고 있습니다.구성 파일에 대한 "암호화 스크립트"를 사용하여 자격 증명을 암호화합니다.그런 다음 사용해야 할 때 암호를 해독합니다."암호화 스크립트"는 실제 시스템에 없고 암호화된 구성 파일만 있습니다.코드를 분석하는 사용자는 코드를 분석하여 암호화를 쉽게 해제할 수 있지만 필요한 경우 EXE로 컴파일할 수 있습니다.

운영 체제는 종종 사용자를 위한 데이터 보호를 지원합니다.윈도우의 경우 http://msdn.microsoft.com/en-us/library/aa380261.aspx 처럼 보입니다.

http://vermeulen.ca/python-win32api.html 사용하여 파이썬에서 win32 아피스를 호출할 수 있습니다.

제가 이해하기로는, 이것은 데이터를 저장할 것이고, 그것을 저장하는 데 사용된 계정에서만 접근할 수 있도록 할 것입니다. 만약 당신이 데이터를 편집하고 싶다면, 당신은 그 값을 추출하고, 변경하고, 저장하는 코드를 작성하면 됩니다.

언급URL : https://stackoverflow.com/questions/7014953/i-need-to-securely-store-a-username-and-password-in-python-what-are-my-options

반응형