Muszę bezpiecznie przechowywać nazwę użytkownika i hasło w Pythonie, jakie są moje opcje?
Piszę mały skrypt Pythona, który będzie okresowo pobierał informacje z usługi 3rd party za pomocą kombinacji nazwy użytkownika i hasła. Nie muszę tworzyć czegoś, co jest w 100% kuloodporne (czy 100% w ogóle istnieje?), ale chciałbym zaangażować w to dobry środek bezpieczeństwa, więc przynajmniej długo by to trwało, zanim ktoś go złamie.
Ten skrypt nie będzie miał GUI i będzie uruchamiany okresowo przez cron
, więc wpisywanie hasła za każdym razem, gdy jest uruchamiany, Aby odszyfrować rzeczy tak naprawdę nie będzie działać, i będę musiał przechowywać nazwę użytkownika i hasło w zaszyfrowanym pliku lub zaszyfrowanym w bazie danych SQLite, co byłoby lepsze, ponieważ będę używać SQLite i tak, i Może trzeba edytować hasło w pewnym momencie. Ponadto, prawdopodobnie będę owijać cały program w EXE, ponieważ jest to wyłącznie dla Windows w tym momencie.
Jak mogę bezpiecznie przechowywać kombinację nazwy użytkownika i hasła, które mają być okresowo używane w zadaniu cron
?
7 answers
Polecam strategię podobną do ssh-agent . Jeśli nie możesz użyć ssh-agent bezpośrednio można zaimplementować coś takiego, tak, że hasło jest przechowywane tylko w pamięci RAM. Zadanie cron mogło mieć skonfigurowane poświadczenia, aby pobierać rzeczywiste hasło od agenta za każdym razem, gdy jest uruchamiane, używać go raz i natychmiast odwoływać się do niego za pomocą instrukcji del
.
Administrator musi jeszcze wprowadzić hasło, aby uruchomić ssh-agent, w czasie rozruchu lub cokolwiek innego, ale jest to rozsądne kompromis, który pozwala uniknąć zapisywania hasła w dowolnym miejscu na dysku.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-10 18:03:51
Biblioteka Python keyring integruje się z CryptProtectData
API w systemie Windows (wraz z odpowiednimi API w systemach Mac i Linux), które szyfruje dane za pomocą poświadczeń logowania użytkownika.
Proste użycie:
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
Użycie jeśli chcesz zapisać nazwę Użytkownika na breloku:
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)
Później, aby uzyskać informacje z breloka
# 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)
Elementy są szyfrowane za pomocą poświadczeń systemu operacyjnego użytkownika, dzięki czemu inne aplikacje uruchomione na twoim koncie użytkownika będą dostęp do hasła.
Aby zasłonić tę lukę, możesz w jakiś sposób zaszyfrować/zaciemnić hasło przed zapisaniem go na breloku. Oczywiście każdy, kto celował w twój skrypt, byłby w stanie spojrzeć na źródło i dowiedzieć się, jak odszyfrować/odszyfrować hasło, ale przynajmniej zapobiegniesz odkurzaniu wszystkich haseł w skarbcu przez jakąś aplikację i uzyskaniu twojego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-16 16:19:18
Po zapoznaniu się z odpowiedziami na to i powiązane pytania, ułożyłem trochę kodu przy użyciu kilku sugerowanych metod szyfrowania i zasłaniania tajnych danych. Ten kod jest specjalnie przeznaczony do sytuacji, gdy skrypt musi działać bez interwencji użytkownika(jeśli użytkownik uruchamia go ręcznie, najlepiej jest umieścić je w haśle i zachować je tylko w pamięci, jak sugeruje odpowiedź na to pytanie). Ta metoda nie jest super-bezpieczne; zasadniczo, skrypt może uzyskać dostęp do tajnych informacji, więc każdy, kto ma pełny dostęp do systemu, ma skrypt i powiązane z nim pliki i może uzyskać do nich dostęp. To, co robi id, zasłania dane przed przypadkową inspekcją i pozostawia same pliki danych bezpieczne, jeśli są badane pojedynczo lub razem bez skryptu.
Moją motywacją do tego jest projekt, który sprawdza niektóre moje konta bankowe w celu monitorowania transakcji - potrzebuję go, aby działał w tle bez ponownego wprowadzania haseł co minutę lub dwie.
Po prostu wklej ten kod w górnej części skryptu Zmień saltSeed, a następnie użyj store() retrieve() I require () w kodzie w razie potrzeby:
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
Bezpieczeństwo tej metody zostałoby znacznie poprawione, gdyby Uprawnienia systemu operacyjnego były ustawione na tajnych plikach tak, aby skrypt mógł je odczytać, a sam skrypt został skompilowany i oznaczony jako wykonywalny (nie do odczytania). Część z nich może być zautomatyzowana, ale nie zawracałem sobie głowy. Prawdopodobnie wymagałoby to skonfigurowania użytkownika dla skryptu i uruchomienie skryptu jako tego użytkownika (i ustawienie własności plików skryptu na tego użytkownika).
Chciałbym, aby wszelkie sugestie, krytyka lub inne punkty wrażliwości, które każdy może wymyślić. Jestem całkiem nowy w pisaniu kodu kryptograficznego, więc to, co zrobiłem, prawie na pewno można poprawić.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-24 04:53:10
Myślę, że najlepiej jest chronić plik skryptu i system, na którym jest uruchomiony.
Zasadniczo wykonaj następujące czynności:
- Użyj uprawnień systemu plików (chmod 400)
- silne hasło do konta właściciela w systemie
- zmniejsz możliwość naruszenia systemu (zapora sieciowa, wyłączenie niepotrzebnych usług itp.)
- Usuń uprawnienia administratora/roota / sudo dla tych, którzy tego nie potrzebują
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-10 17:24:26
Nie ma sensu próbować zaszyfrować hasła: osoba, przed którą próbujesz je ukryć, ma skrypt Pythona, który będzie miał kod do jego odszyfrowania. Najszybszym sposobem uzyskania hasła będzie dodanie instrukcji print do skryptu Pythona tuż przed użyciem hasła w usłudze innej firmy.
Więc zachowaj hasło jako ciąg znaków w skrypcie i base64 Zakoduj je tak, aby samo odczytanie pliku nie wystarczyło, a następnie nazwij to dniem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-10 17:18:21
Systemy operacyjne często mają wsparcie dla zabezpieczenia danych dla użytkownika. w przypadku windows wygląda na to, że to http://msdn.microsoft.com/en-us/library/aa380261.aspx
Możesz wywołać API win32 z Pythona używając http://vermeulen.ca/python-win32api.html
O ile rozumiem, będzie to przechowywać dane tak, że można uzyskać do nich dostęp tylko z konta używanego do ich przechowywania. jeśli chcesz edytować dane, możesz to zrobić pisząc kod do wyodrębnienia, zmiany i zapisz wartość.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-10 17:38:57
Użyłem kryptografii ponieważ miałem problemy z instalacją (kompilacją) innych powszechnie wymienionych bibliotek w moim systemie. (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)
Mój skrypt działa w fizycznie zabezpieczonym systemie / pokoju. Szyfruję poświadczenia za pomocą "skryptu szyfrującego" do pliku konfiguracyjnego. A potem odszyfruj, kiedy będę musiał ich użyć. "Encrypter script" nie jest w prawdziwym systemie, tylko zaszyfrowany plik konfiguracyjny jest. Ktoś, kto analizuje kod, może łatwo złamać szyfrowanie, analizując kod, ale w razie potrzeby możesz go skompilować do EXE.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-27 07:49:52