Menyembunyikan kata sandi dalam skrip python (hanya kebingungan yang tidak aman)

127

Saya sudah mendapatkan skrip python yang sedang membuat koneksi ODBC. Koneksi ODBC dihasilkan dengan string koneksi. Dalam string koneksi ini saya harus memasukkan nama pengguna dan kata sandi untuk koneksi ini.

Apakah ada cara mudah untuk mengaburkan kata sandi ini di file (hanya saja tidak ada yang bisa membaca kata sandi saat saya mengedit file)?

bernhardrusch
sumber
16
Ingatlah bahwa pengguna yang menjalankan file ini setidaknya akan memiliki akses untuk membacanya dan dapat dengan mudah mengambil kata sandi. Jika hanya bisa dibaca oleh Anda dan Anda khawatir orang-orang yang melihatnya dari balik bahu Anda melakukannya, tetapi diperingatkan sementara pengamat biasa tidak dapat menghafal sesuatu dengan cukup cepat untuk mengambil kata sandi, siapa pun yang memiliki akses ke skrip dan sedikit pengetahuan teknis dan sedikit ambisi akan dapat mengambil kata sandi Anda. Selalu pikirkan keamanan dengan sangat hati-hati, ini penting.
Youarefunny

Jawaban:

117

Pengkodean Base64 ada di perpustakaan standar dan akan dilakukan untuk menghentikan peselancar bahu:

>>> import base64
>>>  print(base64.b64encode("password".encode("utf-8")))
cGFzc3dvcmQ=
>>> print(base64.b64decode("cGFzc3dvcmQ=").decode("utf-8"))
password
Dave Webb
sumber
5
Saya setuju. Kata sandi yang disandikan base64 terlihat jauh lebih misterius.
Ed Haber
4
Tetapi tidak membantu fakta bahwa skrip harus dapat dibaca oleh pengguna yang menjalankannya dan kata sandi tidak boleh.
Martin Beckett
79
Saya tidak berpikir itu base64mengaburkan lebih baik daripada rot13dalam konteks ini. Sebaliknya, base64memiliki ciri khasnya (tanda sama dengan, ...) dan karenanya lebih mudah dideteksi daripada pendekatan lain. Kebingungan apa pun tidak memiliki manfaat praktis. Sangat buruk bahwa jawaban ini sangat diperingkatkan. Itu hanya memberikan perasaan aman yang salah ...
schlamar
12
Jika Anda merekam kata sandi sehingga dapat digunakan oleh skrip, siapa pun yang memiliki akses ke skrip akan dapat memperoleh kata sandi, tidak peduli metode enkripsi yang Anda gunakan. Syaratnya di sini adalah hanya untuk menyembunyikan kata sandi dari seseorang yang hanya melihat skrip saat sedang dibuka. Dalam hal base64ini lebih disukai rot13seperti di perpustakaan standar Python.
Dave Webb
17
base64 BUKAN enkripsi. itu kebingungan yang terbaik.
csgeek
52

Douglas F Shearer's adalah solusi yang disetujui secara umum di Unix ketika Anda perlu menentukan kata sandi untuk login jarak jauh.
Anda menambahkan opsi --password-from-file untuk menentukan path dan membaca plaintext dari file.
File kemudian dapat berada di area pengguna sendiri yang dilindungi oleh sistem operasi. Ini juga memungkinkan pengguna yang berbeda untuk secara otomatis mengambil file mereka sendiri.

Untuk kata sandi yang tidak boleh diketahui pengguna skrip - Anda dapat menjalankan skrip dengan izin yang telah dihapus dan memiliki file kata sandi yang dimiliki oleh pengguna root / admin tersebut.

Martin Beckett
sumber
1
Bagaimana tepatnya Anda menjalankan skrip dengan izin tinggi tanpa memberikan root atau kata sandi admin? Apakah ini terkait dengan mengatur bit UID?
Youarefunny
4
Sudahlah saya menemukan jawabannya. Untuk siapa pun yang peduli: Jika sebuah skrip memiliki bit setuid set OS akan 'meneruskan' bit setuid ke penerjemah. Sayangnya, ada lubang keamanan besar yang menganga sehingga sebagian besar distro modern mematikan setuid untuk skrip.
Youarefunny
Saya tidak dapat menemukan informasi tentang opsi --password-from-file. Apakah Anda punya contoh? Terima kasih!
pyramidface
@pyramidface - Maksud saya Anda akan membuat kode fitur seperti ini dan menambahkan kemampuan untuk membaca passwd dari file
Martin Beckett
@ MartinBeckett tetapi seperti kata Youarefunny, Anda harus meningkatkan setuid pada python untuk memberikan akses root skrip ke file kata sandi?
pyramidface
51

Ini adalah metode sederhana:

  1. Buat modul python - sebut saja peekaboo.py.
  2. Di peekaboo.py, masukkan kata sandi dan kode apa pun yang membutuhkan kata sandi itu
  3. Buat versi yang dikompilasi - peekaboo.pyc - dengan mengimpor modul ini (via python commandline, dll ...).
  4. Sekarang, hapus peekaboo.py.
  5. Anda sekarang dapat dengan senang hati mengimpor peekaboo hanya dengan mengandalkan peekaboo.pyc. Karena peekaboo.pyc dikompilasi dengan byte, itu tidak dapat dibaca oleh pengguna biasa.

Ini harus sedikit lebih aman daripada decoding base64 - meskipun rentan terhadap dekompiler py_to_pyc.

manyPartAreEdible
sumber
2
Ini masih memiliki beberapa kekurangan, tetapi sebenarnya sangat dekat dengan apa yang saya inginkan. Ini akan memungkinkan saya untuk mendemonstrasikan skrip python yang menyertakan koneksi pengguna / sandi tanpa mengungkapkan layar sandi, atau harus mengetikkannya ke command prompt. Setelah mengimpor peekaboo import peekabokata sandi tersedia sebagai peekaboo.password(jika peekaboo.py terkandung password='secret')
Dannid
8
Jika Anda ingin mengambil ide ini satu langkah lebih jauh, Anda dapat menggunakan Cython untuk mengkompilasi file .py apa pun ke dalam C dan menghasilkan biner khusus platform (mis: .pyd untuk windows, .so untuk macOS, dll) ... Dengan cythonizingskrip Anda dan berbagi biner yang dihasilkan Anda akan mendapat manfaat dari jawaban ini + menambahkan lapisan kebingungan lainnya, karena sekarang Anda telah mendekompilasi kode C untuk mendapatkan kata sandi. Ini tidak 100% aman, tetapi akan membutuhkan banyak tenaga untuk sampai ke data sensitif yang ingin Anda sembunyikan.
Fnord
cythonizing, perfect
Arno
29

Jika Anda bekerja pada sistem Unix, manfaatkan modul netrc di pustaka Python standar. Bunyinya kata sandi dari file teks terpisah (.netrc), yang formatnya diuraikan di sini .

Berikut adalah contoh penggunaan kecil:

import netrc

# Define which host in the .netrc file to use
HOST = 'mailcluster.loopia.se'

# Read from the .netrc file in your home directory
secrets = netrc.netrc()
username, account, password = secrets.authenticators( HOST )

print username, password
Jonasberg
sumber
19

Solusi terbaik, dengan asumsi nama pengguna dan kata sandi tidak dapat diberikan pada saat runtime oleh pengguna, mungkin merupakan file sumber terpisah yang hanya berisi inisialisasi variabel untuk nama pengguna dan kata sandi yang diimpor ke kode utama Anda. File ini hanya perlu diedit ketika kredensial berubah. Jika tidak, jika Anda hanya mengkhawatirkan peselancar bahu dengan memori rata-rata, pengkodean basis 64 mungkin merupakan solusi termudah. ROT13 terlalu mudah untuk didekode secara manual, tidak peka huruf besar-kecil dan mempertahankan terlalu banyak makna dalam keadaan terenkripsi. Enkode kata sandi dan ID pengguna Anda di luar skrip python. Apakah dia skrip decode saat runtime untuk digunakan.

Memberikan kredensial skrip untuk tugas otomatis selalu merupakan proposal yang berisiko. Skrip Anda harus memiliki kredensial sendiri dan akun yang digunakannya tidak boleh memiliki akses selain dari apa yang diperlukan. Setidaknya kata sandi harus panjang dan agak acak.

tduehr
sumber
Jawaban yang sangat bagus - terima kasih. Untuk skrip kecil yang saya tulis (yang merupakan skrip pemeliharaan - pengkodean BASE64 sudah mencukupi)
bernhardrusch
2
Ini kedengarannya bagus, tetapi dapatkah Anda memberikan contoh implementasi? Sekarang ini hanya deskripsi praktik umum, dan tidak berguna bagi seseorang yang belum pernah melakukan ini sebelumnya.
Dannid
18

Bagaimana dengan mengimpor nama pengguna dan kata sandi dari file eksternal ke skrip? Dengan begitu, bahkan jika seseorang memegang skrip, mereka tidak akan secara otomatis mendapatkan kata sandi.

Douglas F Shearer
sumber
15

base64 adalah cara untuk memenuhi kebutuhan sederhana Anda. Tidak perlu mengimpor apa pun:

>>> 'your string'.encode('base64')
'eW91ciBzdHJpbmc=\n'
>>> _.decode('base64')
'your string'
tzot
sumber
2
Apa itu konyol ?! Seluruh balasan, atau bagian yang tidak mengimpor?
tzot
18
Base64 hanya menambahkan ilusi keamanan.
FlySwat
13
Jonathan, sepertinya kamu tidak membaca pertanyaan itu. Ini tentang ketidakjelasan (dan yang sangat sementara), bukan keamanan , jadi saya tidak mengerti mengapa Anda menganggap jawaban saya tidak membantu.
tzot
1
Saya tidak tahu Anda bisa melakukan ini daripada harus menggunakan modul base64. Dan ada banyak pengkodean juga seperti zlib terlalu ... menyenangkan :)
Kiv
1
@ Dennis Menggunakan modul base64 adalah cara yang disukai saat ini. Yang terakhir tidak berfungsi lagi di versi Python yang lebih baru.
Jeyekomon
5

untuk kebingungan menggunakan python3base64 dilakukan secara berbeda:

import base64
base64.b64encode(b'PasswordStringAsStreamOfBytes')

yang mengakibatkan

b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM='

perhatikan representasi string informal, string aktual dalam tanda kutip

dan decoding kembali ke string asli

base64.b64decode(b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM=')
b'PasswordStringAsStreamOfBytes'

untuk menggunakan hasil ini di mana objek string diperlukan, objek byte dapat diterjemahkan

repr = base64.b64decode(b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM=')
secret = repr.decode('utf-8')
print(secret)

untuk informasi lebih lanjut tentang bagaimana python3 menangani byte (dan string yang sesuai) silakan lihat dokumentasi resmi .

naik opelet
sumber
4

Ini adalah masalah yang cukup umum. Biasanya yang terbaik yang dapat Anda lakukan adalah melakukannya

A) membuat semacam fungsi cipher ceasar untuk menyandikan / mendekode (tidak rot13) atau

B) metode yang disukai adalah menggunakan kunci enkripsi, dalam jangkauan program Anda, meng-encode / mendekodekan kata sandi. Di mana Anda dapat menggunakan perlindungan file untuk melindungi akses kunci.

Sejalan dengan itu jika aplikasi Anda berjalan sebagai service / daemon (seperti server web), Anda dapat memasukkan kunci ke dalam keystore yang dilindungi kata sandi dengan input kata sandi sebagai bagian dari startup layanan. Diperlukan admin untuk memulai kembali aplikasi Anda, tetapi Anda akan mendapatkan pretection yang sangat bagus untuk kata sandi konfigurasi Anda.

Mikhail_Sam
sumber
2

Sistem operasi Anda mungkin menyediakan fasilitas untuk mengenkripsi data dengan aman. Misalnya, pada Windows ada DPAPI (API perlindungan data). Mengapa tidak meminta kredensial mereka kepada pengguna pada saat pertama kali Anda menjalankan kemudian membuat mereka dienkripsi untuk proses selanjutnya?

Jamie Eisenhart
sumber
2

Lebih banyak appraoch buatan sendiri daripada mengonversi otentikasi / kata sandi / nama pengguna menjadi detail terenkripsi. FTPLIB hanyalah contohnya. " pass.csv " adalah nama file csv

Simpan kata sandi dalam CSV seperti di bawah ini:

nama pengguna

kata sandi pengguna

(Tanpa judul kolom)

Baca CSV dan simpan ke daftar.

Menggunakan daftar elemen sebagai detail authetntication.

Kode lengkap.

import os
import ftplib
import csv 
cred_detail = []
os.chdir("Folder where the csv file is stored")
for row in csv.reader(open("pass.csv","rb")):       
        cred_detail.append(row)
ftp = ftplib.FTP('server_name',cred_detail[0][0],cred_detail[1][0])
Jiwa yang kesepian
sumber
2

Ini cuplikan saya untuk hal semacam itu. Anda pada dasarnya mengimpor atau menyalin fungsi ke kode Anda. getCredentials akan membuat file terenkripsi jika tidak ada dan mengembalikan kamus, dan updateCredential akan memperbarui.

import os

def getCredentials():
    import base64

    splitter='<PC+,DFS/-SHQ.R'
    directory='C:\\PCT'

    if not os.path.exists(directory):
        os.makedirs(directory)

    try:
        with open(directory+'\\Credentials.txt', 'r') as file:
            cred = file.read()
            file.close()
    except:
        print('I could not file the credentials file. \nSo I dont keep asking you for your email and password everytime you run me, I will be saving an encrypted file at {}.\n'.format(directory))

        lanid = base64.b64encode(bytes(input('   LanID: '), encoding='utf-8')).decode('utf-8')  
        email = base64.b64encode(bytes(input('   eMail: '), encoding='utf-8')).decode('utf-8')
        password = base64.b64encode(bytes(input('   PassW: '), encoding='utf-8')).decode('utf-8')
        cred = lanid+splitter+email+splitter+password
        with open(directory+'\\Credentials.txt','w+') as file:
            file.write(cred)
            file.close()

    return {'lanid':base64.b64decode(bytes(cred.split(splitter)[0], encoding='utf-8')).decode('utf-8'),
            'email':base64.b64decode(bytes(cred.split(splitter)[1], encoding='utf-8')).decode('utf-8'),
            'password':base64.b64decode(bytes(cred.split(splitter)[2], encoding='utf-8')).decode('utf-8')}

def updateCredentials():
    import base64

    splitter='<PC+,DFS/-SHQ.R'
    directory='C:\\PCT'

    if not os.path.exists(directory):
        os.makedirs(directory)

    print('I will be saving an encrypted file at {}.\n'.format(directory))

    lanid = base64.b64encode(bytes(input('   LanID: '), encoding='utf-8')).decode('utf-8')  
    email = base64.b64encode(bytes(input('   eMail: '), encoding='utf-8')).decode('utf-8')
    password = base64.b64encode(bytes(input('   PassW: '), encoding='utf-8')).decode('utf-8')
    cred = lanid+splitter+email+splitter+password
    with open(directory+'\\Credentials.txt','w+') as file:
        file.write(cred)
        file.close()

cred = getCredentials()

updateCredentials()
Mahmoud Alhyari
sumber
1

Tempatkan informasi konfigurasi dalam file konfigurasi terenkripsi. Permintaan informasi ini dalam kode Anda menggunakan kunci. Tempatkan kunci ini dalam file terpisah per lingkungan, dan jangan menyimpannya dengan kode Anda.

FlySwat
sumber
1

Apakah Anda tahu lubang?

https://pypi.python.org/pypi/pit (hanya py2 (versi 0.3))

https://github.com/yoshiori/pit (ini akan bekerja pada py3 (versi saat ini 0.4))

test.py

from pit import Pit

config = Pit.get('section-name', {'require': {
    'username': 'DEFAULT STRING',
    'password': 'DEFAULT STRING',
    }})
print(config)

Lari:

$ python test.py
{'password': 'my-password', 'username': 'my-name'}

~ / .pit / default.yml:

section-name:
  password: my-password
  username: my-name
TakesxiSximada
sumber
3
Pit tidak memiliki dokumentasi apa pun
successhawk
Seperti yang dicatat oleh @successhawk - Saya tidak melihat dokumentasi APAPUN di tautan github / pypi tersebut untuk "pit" - tetapi uraian di atas jelas - dan secara keseluruhan saya menyukai solusi ini untuk "menyembunyikan" kredensial dari tampilan yang mudah ...
netdesignate
Saya enggan menggunakan modul yang tidak dipelihara, dan saya mendapatkan kesalahan ketika saya mencoba menggunakannya seperti yang diperintahkan:/usr/lib/python3.7/site-packages/pit.py:93: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details. return yaml.load(open(Pit._config))
netdesignate
1

Jika berjalan pada Windows, Anda dapat mempertimbangkan menggunakan pustaka win32crypt. Ini memungkinkan penyimpanan dan pengambilan data yang dilindungi (kunci, kata sandi) oleh pengguna yang menjalankan skrip, sehingga kata sandi tidak pernah disimpan dalam teks yang jelas atau format yang dikaburkan dalam kode Anda. Saya tidak yakin apakah ada implementasi yang setara untuk platform lain, jadi dengan penggunaan win32crypt yang ketat, kode Anda tidak portabel.

Saya percaya modul dapat diperoleh di sini: http://timgolden.me.uk/pywin32-docs/win32crypt.html

VilleLipponen
sumber
1

Cara yang telah saya lakukan ini adalah sebagai berikut:

Di shell python:

>>> from cryptography.fernet import Fernet
>>> key = Fernet.generate_key()
>>> print(key)
b'B8XBLJDiroM3N2nCBuUlzPL06AmfV4XkPJ5OKsPZbC4='
>>> cipher = Fernet(key)
>>> password = "thepassword".encode('utf-8')
>>> token = cipher.encrypt(password)
>>> print(token)
b'gAAAAABe_TUP82q1zMR9SZw1LpawRLHjgNLdUOmW31RApwASzeo4qWSZ52ZBYpSrb1kUeXNFoX0tyhe7kWuudNs2Iy7vUwaY7Q=='

Kemudian, buat modul dengan kode berikut:

from cryptography.fernet import Fernet

# you store the key and the token
key = b'B8XBLJDiroM3N2nCBuUlzPL06AmfV4XkPJ5OKsPZbC4='
token = b'gAAAAABe_TUP82q1zMR9SZw1LpawRLHjgNLdUOmW31RApwASzeo4qWSZ52ZBYpSrb1kUeXNFoX0tyhe7kWuudNs2Iy7vUwaY7Q=='

# create a cipher and decrypt when you need your password
cipher = Fernet(key)

mypassword = cipher.decrypt(token).decode('utf-8')

Setelah melakukan ini, Anda dapat mengimpor kata sandi langsung atau Anda dapat mengimpor token dan sandi untuk mendekripsi sesuai kebutuhan.

Jelas, ada beberapa kekurangan dalam pendekatan ini. Jika seseorang memiliki token dan kunci (seperti yang mereka lakukan jika mereka memiliki skrip), mereka dapat mendekripsi dengan mudah. Namun itu mengaburkan, dan jika Anda mengkompilasi kode (dengan sesuatu seperti Nuitka) setidaknya kata sandi Anda tidak akan muncul sebagai teks biasa di hex editor.

Dr_Z2A
sumber
0

Ini tidak tepat menjawab pertanyaan Anda, tetapi terkait. Saya akan menambahkan sebagai komentar tetapi tidak diizinkan. Saya telah menangani masalah yang sama ini, dan kami telah memutuskan untuk mengekspos skrip tersebut kepada pengguna yang menggunakan Jenkins. Ini memungkinkan kami untuk menyimpan kredensial db dalam file terpisah yang dienkripsi dan diamankan di server dan tidak dapat diakses oleh non-admin. Ini juga memungkinkan kita sedikit jalan pintas untuk membuat UI, dan mempercepat eksekusi.

Joe Hayes
sumber
0

Anda juga dapat mempertimbangkan kemungkinan menyimpan kata sandi di luar skrip, dan memasoknya saat runtime

mis. fred.py

import os
username = 'fred'
password = os.environ.get('PASSWORD', '')
print(username, password)

yang bisa dijalankan seperti

$ PASSWORD=password123 python fred.py
fred password123

Lapisan tambahan "keamanan melalui ketidakjelasan" dapat dicapai dengan menggunakan base64(seperti yang disarankan di atas), menggunakan nama yang kurang jelas dalam kode dan lebih jauh menjauhkan kata sandi yang sebenarnya dari kode.

Jika kode ada di repositori, seringkali berguna untuk menyimpan rahasia di luarnya , sehingga orang dapat menambahkan ini ke ~/.bashrc(atau ke lemari besi, atau skrip peluncuran, ...)

export SURNAME=cGFzc3dvcmQxMjM=

dan ubah fred.pyke

import os
import base64
name = 'fred'
surname = base64.b64decode(os.environ.get('SURNAME', '')).decode('utf-8')
print(name, surname)

lalu masuk kembali dan

$ python fred.py
fred password123
jalanb
sumber
0

Mengapa tidak memiliki xor sederhana?

Keuntungan:

  • terlihat seperti data biner
  • tidak ada yang bisa membacanya tanpa mengetahui kuncinya (meskipun hanya satu karakter)

Saya sampai pada titik di mana saya mengenali string b64 sederhana untuk kata-kata umum dan rot13 juga. Xor akan membuatnya jauh lebih sulit.

SL
sumber
0
import base64
print(base64.b64encode("password".encode("utf-8")))
print(base64.b64decode(b'cGFzc3dvcmQ='.decode("utf-8")))
pradyot
sumber
Ini adalah solusi dari jawaban yang diterima .
Sergey Shubin
-1

Ada beberapa utilitas ROT13 yang ditulis dengan Python di 'Net - hanya google untuk mereka. ROT13 menyandikan string secara offline, menyalinnya ke sumber, mendekode pada titik transmisi.

Tapi ini benar - benar perlindungan yang lemah ...

Kevin Little
sumber
harap sertakan tautan atau kode sampel untuk menjadikan jawaban ini lebih bermanfaat
Micah Stubbs