Saya sedang menulis skrip Python kecil yang secara berkala akan menarik informasi dari layanan pihak ketiga menggunakan kombinasi nama pengguna dan kata sandi. Saya tidak perlu membuat sesuatu yang 100% antipeluru (apakah 100% benar-benar ada?), Tetapi saya ingin melibatkan ukuran keamanan yang baik sehingga setidaknya akan membutuhkan waktu lama bagi seseorang untuk memecahkannya.
Skrip ini tidak memiliki GUI dan akan dijalankan secara berkala cron
, jadi memasukkan kata sandi setiap kali dijalankan untuk mendekripsi tidak akan benar-benar berfungsi, dan saya harus menyimpan nama pengguna dan kata sandi dalam file terenkripsi atau terenkripsi dalam database SQLite, yang lebih disukai karena saya akan tetap menggunakan SQLite, dan saya mungkin perlu mengedit kata sandi di beberapa titik. Selain itu, saya mungkin akan membungkus seluruh program dalam EXE, karena ini khusus untuk Windows pada saat ini.
Bagaimana saya bisa menyimpan kombinasi nama pengguna dan kata sandi dengan aman untuk digunakan secara berkala melalui cron
pekerjaan?
sumber
Jawaban:
Saya merekomendasikan strategi yang mirip dengan ssh-agent . Jika Anda tidak dapat menggunakan ssh-agent secara langsung, Anda dapat menerapkan sesuatu seperti itu, sehingga kata sandi Anda hanya disimpan di RAM. Tugas cron bisa saja mengonfigurasi kredensial untuk mendapatkan kata sandi sebenarnya dari agen setiap kali dijalankan, gunakan sekali, dan segera hapus referensi dengan menggunakan
del
pernyataan.Administrator masih harus memasukkan kata sandi untuk memulai ssh-agent, pada saat boot atau apa pun, tetapi ini adalah kompromi yang wajar yang menghindari penyimpanan kata sandi teks biasa di mana saja di disk.
sumber
The python keyring perpustakaan terintegrasi dengan
CryptProtectData
API pada Windows (bersama dengan API yang relevan pada Mac dan Linux) yang mengenkripsi data dengan kredensial masuk pengguna.Penggunaan sederhana:
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
Penggunaan jika Anda ingin menyimpan nama pengguna di keyring:
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)
Nanti untuk mendapatkan info Anda dari keyring
# 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)
Item dienkripsi dengan kredensial sistem operasi pengguna, sehingga aplikasi lain yang berjalan di akun pengguna Anda dapat mengakses sandi.
Untuk sedikit mengaburkan kerentanan itu, Anda dapat mengenkripsi / mengaburkan kata sandi dengan beberapa cara sebelum menyimpannya di keyring. Tentu saja, siapa pun yang menargetkan skrip Anda hanya akan dapat melihat sumbernya dan mencari cara untuk membatalkan enkripsi / menyamarkan kata sandi, tetapi Anda setidaknya akan mencegah beberapa aplikasi menyedot semua kata sandi di lemari besi dan mendapatkan milik Anda juga. .
sumber
keyring
mendukung pengambilan nama pengguna dan kata sandi?get_password
untuk nama pengguna. Meskipun, saya pikir Anda harus memulai jawabannya dengan contoh asli yang disederhanakankeyring.set_password()
dankeyring.get_password()
keyring
bukan bagian dari pustaka standar pythonkeyring
dengan aman menghapus kata sandi dari log dan memori setelah kata sandi?Setelah melihat jawaban untuk ini dan pertanyaan terkait, saya telah mengumpulkan beberapa kode menggunakan beberapa metode yang disarankan untuk mengenkripsi dan menyembunyikan data rahasia. Kode ini khusus untuk saat skrip harus dijalankan tanpa campur tangan pengguna (jika pengguna memulainya secara manual, yang terbaik adalah memasukkannya ke dalam kata sandi dan hanya menyimpannya di memori seperti yang disarankan oleh jawaban atas pertanyaan ini). Metode ini tidak super aman; pada dasarnya, skrip dapat mengakses info rahasia sehingga siapa pun yang memiliki akses sistem penuh memiliki skrip dan file yang terkait serta dapat mengaksesnya. Apa yang dilakukannya ini mengaburkan data dari inspeksi biasa dan membiarkan file data itu sendiri aman jika diperiksa satu per satu, atau bersama-sama tanpa skrip.
Motivasi saya untuk ini adalah proyek yang mengumpulkan beberapa rekening bank saya untuk memantau transaksi - saya membutuhkannya untuk berjalan di latar belakang tanpa saya memasukkan kembali kata sandi setiap satu atau dua menit.
Cukup tempelkan kode ini di bagian atas skrip Anda, ubah saltSeed, lalu gunakan store () mengambil () dan require () di kode Anda sesuai kebutuhan:
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
Keamanan metode ini akan ditingkatkan secara signifikan jika izin os ditetapkan pada file rahasia untuk hanya mengizinkan skrip itu sendiri untuk membacanya, dan jika skrip itu sendiri dikompilasi dan ditandai sebagai hanya dapat dieksekusi (tidak dapat dibaca). Beberapa di antaranya bisa otomatis, tapi saya tidak ambil pusing. Ini mungkin memerlukan pengaturan pengguna untuk skrip dan menjalankan skrip sebagai pengguna itu (dan pengaturan kepemilikan file skrip untuk pengguna itu).
Saya menyukai saran, kritik, atau poin kerentanan lainnya yang dapat dipikirkan oleh siapa pun. Saya cukup baru dalam menulis kode kripto jadi apa yang telah saya lakukan hampir pasti dapat ditingkatkan.
sumber
Ada beberapa opsi untuk menyimpan kata sandi dan rahasia lain yang perlu digunakan oleh program Python, terutama program yang perlu dijalankan di latar belakang di mana ia tidak bisa hanya meminta pengguna untuk mengetikkan kata sandi.
Masalah yang harus dihindari:
Opsi 1: SSH
Ini tidak selalu menjadi pilihan, tapi mungkin yang terbaik. Kunci pribadi Anda tidak pernah dikirim melalui jaringan, SSH hanya menjalankan penghitungan matematis untuk membuktikan bahwa Anda memiliki kunci yang benar.
Untuk membuatnya berfungsi, Anda membutuhkan yang berikut:
Opsi 2: Variabel Lingkungan
Yang ini adalah yang paling sederhana, jadi ini mungkin tempat yang baik untuk memulai. Ini dijelaskan dengan baik di Aplikasi Dua Belas Faktor . Ide dasarnya adalah bahwa kode sumber Anda hanya menarik kata sandi atau rahasia lain dari variabel lingkungan, dan kemudian Anda mengkonfigurasi variabel lingkungan tersebut di setiap sistem tempat Anda menjalankan program. Mungkin juga sentuhan yang bagus jika Anda menggunakan nilai default yang akan berfungsi untuk sebagian besar pengembang. Anda harus menyeimbangkannya dengan membuat perangkat lunak Anda "aman secara default".
Berikut adalah contoh yang menarik server, nama pengguna, dan kata sandi dari variabel lingkungan.
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)
Cari tahu cara menyetel variabel lingkungan di sistem operasi Anda, dan pertimbangkan untuk menjalankan layanan dengan akunnya sendiri. Dengan begitu Anda tidak memiliki data sensitif dalam variabel lingkungan saat Anda menjalankan program di akun Anda sendiri. Saat Anda menyiapkan variabel lingkungan tersebut, berhati-hatilah agar pengguna lain tidak dapat membacanya. Periksa izin file, misalnya. Tentu saja setiap pengguna dengan izin root akan dapat membacanya, tetapi itu tidak dapat membantu. Jika Anda menggunakan systemd, lihat unit layanan , dan berhati-hatilah untuk menggunakan
EnvironmentFile
daripadaEnvironment
mencari rahasia.Environment
nilai dapat dilihat oleh setiap pengguna dengansystemctl show
.Opsi 3: File Konfigurasi
Ini sangat mirip dengan variabel lingkungan, tetapi Anda membaca rahasia dari file teks. Saya masih menemukan variabel lingkungan lebih fleksibel untuk hal-hal seperti alat penerapan dan server integrasi berkelanjutan. Jika Anda memutuskan untuk menggunakan file konfigurasi, Python mendukung beberapa format di pustaka standar, seperti JSON , INI , netrc , dan XML . Anda juga dapat menemukan paket eksternal seperti PyYAML dan TOML . Secara pribadi, saya menemukan JSON dan YAML yang paling sederhana untuk digunakan, dan YAML mengizinkan komentar.
Tiga hal yang perlu dipertimbangkan dengan file konfigurasi:
~/.my_app
, dan opsi baris perintah untuk menggunakan lokasi yang berbeda.Opsi 4: Modul Python
Beberapa proyek hanya memasukkan rahasia mereka ke dalam modul Python.
# settings.py db_server = 'dbhost1' db_user = 'my_app' db_password = 'correcthorsebatterystaple'
Kemudian impor modul itu untuk mendapatkan nilainya.
# my_app.py from settings import db_server, db_user, db_password db_connect(db_server, db_user, db_password)
Satu proyek yang menggunakan teknik ini adalah Django . Jelas, Anda tidak boleh berkomitmen
settings.py
pada kontrol sumber, meskipun Anda mungkin ingin memasukkan file bernamasettings_template.py
yang dapat disalin dan dimodifikasi pengguna.Saya melihat beberapa masalah dengan teknik ini:
.gitignore
mengurangi risiko itu.Jika proyek Anda sudah menggunakan teknik ini, mudah untuk beralih ke variabel lingkungan. Pindahkan saja semua nilai pengaturan ke variabel lingkungan, dan ubah modul Python untuk membaca dari variabel lingkungan tersebut.
sumber
os.getenv()
. Bagaimana kita harus melakukan ini jika kode tersebut dibagikan? Jika kode diunduh oleh pengembang lain, bagaimana dia harus memastikan variabel lingkungan sudah ditetapkan untuknya?os.getenv()
, @a_sid, jadi kode setidaknya akan berjalan untuk pengguna yang belum mengatur variabel lingkungan. Jika tidak ada nilai default yang baik, naikkan kesalahan yang jelas saat Anda mendapatkannyaNone
. Selain itu, berikan komentar yang jelas di file pengaturan. Jika saya salah paham, saya sarankan Anda mengajukan pertanyaan terpisah.Tidak ada gunanya mencoba mengenkripsi kata sandi: orang yang Anda coba sembunyikan memiliki skrip Python, yang akan memiliki kode untuk mendekripsinya. Cara tercepat untuk mendapatkan kata sandi adalah dengan menambahkan pernyataan cetak ke skrip Python tepat sebelum menggunakan kata sandi dengan layanan pihak ketiga.
Jadi simpan kata sandi sebagai string dalam skrip, dan base64 menyandikannya sehingga hanya membaca file saja tidak cukup, lalu sebut saja sehari.
sumber
Saya pikir hal terbaik yang dapat Anda lakukan adalah melindungi file skrip dan sistem yang menjalankannya.
Pada dasarnya lakukan hal berikut:
sumber
sistem operasi seringkali memiliki dukungan untuk mengamankan data bagi pengguna. dalam kasus windows, sepertinya itu http://msdn.microsoft.com/en-us/library/aa380261.aspx
Anda dapat memanggil win32 apis dari python menggunakan http://vermeulen.ca/python-win32api.html
Sejauh yang saya mengerti, ini akan menyimpan data sehingga hanya dapat diakses dari akun yang digunakan untuk menyimpannya. jika Anda ingin mengedit data Anda dapat melakukannya dengan menulis kode untuk mengekstrak, mengubah, dan menyimpan nilainya.
sumber
Saya menggunakan Kriptografi karena saya mengalami masalah saat menginstal (mengompilasi) pustaka lain yang biasa disebutkan di sistem saya. (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)
Skrip saya berjalan di sistem / ruangan yang aman secara fisik. Saya mengenkripsi kredensial dengan "skrip enkripsi" ke file konfigurasi. Dan kemudian mendekripsi saat saya perlu menggunakannya. "Encrypter script" tidak ada di sistem sebenarnya, hanya file konfigurasi yang dienkripsi. Seseorang yang menganalisis kode dapat dengan mudah memecahkan enkripsi dengan menganalisis kode tersebut, tetapi Anda masih dapat mengkompilasinya menjadi EXE jika perlu.
sumber