Nascondere una password in uno script Python (solo offuscamento insicuro)


127

Ho uno script Python che sta creando una connessione ODBC. La connessione ODBC viene generata con una stringa di connessione. In questa stringa di connessione devo includere il nome utente e la password per questa connessione.

C'è un modo semplice per oscurare questa password nel file (solo che nessuno può leggere la password quando sto modificando il file)?


16
Ricorda solo che gli utenti che eseguono questo file avranno almeno l'accesso in lettura ad esso e potranno facilmente afferrare le password. Se questi possono essere letti solo da te e sei preoccupato per le persone che lo vedono alle tue spalle, cercalo, ma tieni presente che l'osservatore medio non è in grado di memorizzare le cose abbastanza velocemente da ottenere una password, chiunque abbia accesso alla sceneggiatura e un un po 'di know-how tecnico e una piccola quantità di ambizione saranno in grado di afferrare le tue password. Pensa sempre alla sicurezza con molta attenzione, è importante.
Youarefunny,

Risposte:


117

La codifica Base64 è nella libreria standard e farà per fermare i surfisti della spalla:

>>> import base64
>>>  print(base64.b64encode("password".encode("utf-8")))
cGFzc3dvcmQ=
>>> print(base64.b64decode("cGFzc3dvcmQ=").decode("utf-8"))
password

5
Sono d'accordo. La password codificata in base64 sembra molto più misteriosa.
Ed Haber,

4
Ma non aiuta il fatto che lo script deve essere leggibile dall'utente che lo esegue e la password non deve.
Martin Beckett,

79
Non penso che base64sia meglio offuscare che rot13in questo contesto. Al contrario, base64ha le sue caratteristiche tipiche (segno di parità, ...) ed è quindi più facile da rilevare rispetto ad altri approcci. Qualsiasi offuscamento non ha tuttavia alcun vantaggio pratico. Davvero un peccato che questa risposta sia così apprezzata. Dà solo un falso senso di sicurezza ...
schlamar,

12
Se stai registrando la password in modo che possa essere utilizzata dallo script, chiunque abbia accesso allo script sarà in grado di ottenere la password, indipendentemente dal metodo di crittografia utilizzato. Il requisito qui era solo nascondere la password a qualcuno che stava guardando lo script mentre era aperto. In questo caso base64è preferibile rot13come nella libreria standard di Python.
Dave Webb,

17
base64 NON è la crittografia. è al massimo offuscamento.
csgeek,

52

Douglas F Shearer's è la soluzione generalmente approvata in Unix quando è necessario specificare una password per un accesso remoto.
Aggiungete un'opzione --password-from-file per specificare il percorso e leggere il testo in chiaro da un file.
Il file può quindi trovarsi nell'area dell'utente dell'utente protetta dal sistema operativo. Inoltre, consente a diversi utenti di prelevare automaticamente il proprio file.

Per le password che l'utente dello script non è autorizzato a conoscere, è possibile eseguire lo script con autorizzazione elavata e avere il file delle password di proprietà dell'utente root / admin.


1
Come si esegue esattamente lo script con autorizzazioni elevate senza fornire una password di amministratore o root? È correlato all'impostazione dei bit UID?
Youarefunny,

4
Non importa se l'ho capito. Per chiunque si preoccupi: se uno script ha un bit setuid impostato, il sistema operativo 'passerà' il bit setuid all'interprete. Sfortunatamente, ci sono enormi buchi di sicurezza, quindi la maggior parte delle distribuzioni moderne disattiva setuid per gli script.
Youarefunny,

Non riesco a trovare alcuna informazione sull'opzione --password-from-file. Hai qualche esempio? Grazie!
pyramidface,

@pyramidface - Volevo dire che avresti codificato una caratteristica come questa e aggiunto la possibilità di leggere un passwd da un file
Martin Beckett,

@MartinBeckett ma come hai detto Youarefunny, dovresti alzare setuid su Python per dare allo script root l'accesso al file delle password?
pyramidface,

51

Ecco un metodo semplice:

  1. Crea un modulo Python: chiamiamolo peekaboo.py.
  2. In peekaboo.py, includi sia la password che qualsiasi codice che richieda tale password
  3. Crea una versione compilata - peekaboo.pyc - importando questo modulo (tramite la riga di comando di Python, ecc ...).
  4. Ora elimina peekaboo.py.
  5. Ora puoi importare felicemente peekaboo basandoti solo su peekaboo.pyc. Poiché peekaboo.pyc è compilato in byte, non è leggibile per l'utente casuale.

Questo dovrebbe essere un po 'più sicuro della decodifica base64, sebbene sia vulnerabile a un decompilatore py_to_pyc.


2
Questo ha ancora alcune carenze, ma in realtà è molto vicino a quello che voglio. Mi permetterà di demo di script Python che includono connessioni utente / passwd senza rivelare la password sullo schermo o doverla digitare nel prompt dei comandi. Dopo aver importato peekaboo import peekabola password è disponibile come peekaboo.password(se contenuta peekaboo.py password='secret')
Dannid

8
Se vuoi portare ulteriormente quest'idea, puoi usare Cython per compilare qualsiasi file .py in C e generare un binario specifico per la piattaforma (es: .pyd per windows, .so per macOS, ecc.) ... Con il cythonizingtuo script e condividendo il binario generato avrai il vantaggio di questa risposta + aggiungi un altro livello di offuscamento, perché ora hai il codice C decompilato per arrivare alla password. Questo non è sicuro al 100%, ma ci vorrà molto lavoro per arrivare ai dati sensibili che si desidera nascondere.
Fnord,

ciononizzante, perfetto
Arno,

29

Se stai lavorando su un sistema Unix, approfitta del modulo netrc nella libreria standard di Python. Legge le password da un file di testo separato (.netrc), il cui formato è descritto qui .

Ecco un piccolo esempio di utilizzo:

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

19

La soluzione migliore, supponendo che l'utente e la password non possano essere forniti in fase di esecuzione dall'utente, è probabilmente un file di origine separato contenente solo l'inizializzazione variabile per il nome utente e la password importati nel codice principale. Questo file dovrebbe essere modificato solo quando cambiano le credenziali. Altrimenti, se sei solo preoccupato per i surfisti di spalla con ricordi medi, la codifica base 64 è probabilmente la soluzione più semplice. ROT13 è semplicemente troppo facile da decodificare manualmente, non fa distinzione tra maiuscole e minuscole e conserva troppo significato nel suo stato crittografato. Codifica la password e l'id utente all'esterno dello script Python. Ha la decodifica dello script in fase di esecuzione per l'uso.

Dare credenziali di script per attività automatizzate è sempre una proposta rischiosa. Lo script dovrebbe avere le proprie credenziali e l'account che utilizza non dovrebbe avere accesso diverso da quello necessario. Almeno la password dovrebbe essere lunga e piuttosto casuale.


Risposta molto bella - grazie. Per i piccoli script che sto scrivendo (che sono comunque script di manutenzione - basterà la codifica BASE64)
bernhardrusch,

2
Questo suona bene, ma puoi fare un esempio di implementazione? In questo momento è solo una descrizione di una pratica generale e non altrettanto utile per qualcuno che non l'ha mai fatto prima.
Dannid,

18

Che ne dici di importare il nome utente e la password da un file esterno allo script? In questo modo, anche se qualcuno avesse ottenuto lo script, non avrebbe automaticamente ottenuto la password.


15

base64 è la strada da percorrere per le tue semplici esigenze. Non è necessario importare nulla:

>>> 'your string'.encode('base64')
'eW91ciBzdHJpbmc=\n'
>>> _.decode('base64')
'your string'

2
Che cosa è esattamente sciocco ?! L'intera risposta o la parte non importante?
tzot,

18
Base64 aggiunge solo l'illusione della sicurezza.
FlySwat,

13
Jonathan, sembra che tu non abbia letto la domanda. Riguarda l' oscurità (e molto temporanea), non la sicurezza , quindi non capisco perché consideri la mia risposta non utile.
dal

1
Non sapevo che avresti potuto farlo invece di dover usare il modulo base64. E ci sono anche molte codifiche come zlib ... divertimento :)
Kiv

1
@Dennis L'utilizzo del modulo base64 è il modo preferito al giorno d'oggi. Quest'ultimo non funziona più nelle versioni più recenti di Python.
Jeyekomon,

5

per python3 l' offuscamento usando base64è fatto diversamente:

import base64
base64.b64encode(b'PasswordStringAsStreamOfBytes')

che risulta in

b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM='

notare la rappresentazione informale della stringa, la stringa effettiva è tra virgolette

e la decodifica torna alla stringa originale

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

per utilizzare questo risultato in cui sono richiesti oggetti stringa, l'oggetto byte può essere tradotto

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

per ulteriori informazioni su come python3 gestisce i byte (e le stringhe di conseguenza), consultare la documentazione ufficiale .


4

Questo è un problema abbastanza comune. In genere il meglio che puoi fare è farlo

A) creare un qualche tipo di funzione di cifratura ceasar per codificare / decodificare (semplicemente non rot13) o

B) il metodo preferito è utilizzare una chiave di crittografia, a portata di mano del programma, codificare / decodificare la password. In cui è possibile utilizzare la protezione dei file per proteggere l'accesso alla chiave.

Allo stesso modo, se la tua app viene eseguita come servizio / demone (come un server web), puoi inserire la tua chiave in un archivio chiavi protetto da password con l'immissione della password come parte dell'avvio del servizio. Ci vorrà un amministratore per riavviare l'app, ma avrai una buona pretesa per le tue password di configurazione.


2

Il sistema operativo probabilmente fornisce funzionalità per crittografare i dati in modo sicuro. Ad esempio, su Windows è disponibile DPAPI (API per la protezione dei dati). Perché non chiedere all'utente le proprie credenziali la prima volta che si esegue, quindi eliminarle crittografate per le esecuzioni successive?


2

Appraoch più familiare invece di convertire autenticazione / password / nome utente in dettagli codificati. FTPLIB è solo l'esempio. " pass.csv " è il nome del file CSV

Salva la password in CSV come di seguito:

nome utente

password utente

(Senza intestazione di colonna)

Leggere il CSV e salvarlo in un elenco.

Utilizzo degli elenchi di elementi come dettagli di autenticazione.

Codice completo.

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])

2

Ecco il mio frammento di tale cosa. Fondamentalmente importate o copiate la funzione nel vostro codice. getCredentials creerà il file crittografato se non esiste e restituisce una dizione e updateCredential si aggiornerà.

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()

1

Inserire le informazioni di configurazione in un file di configurazione crittografato. Richiedi queste informazioni nel tuo codice usando una chiave. Posiziona questa chiave in un file separato per ambiente e non archiviarla con il tuo codice.


1

Conosci pit?

https://pypi.python.org/pypi/pit (solo py2 (versione 0.3))

https://github.com/yoshiori/pit (funzionerà su py3 (versione 0.4 attuale))

test.py

from pit import Pit

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

Correre:

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

~ / .Pit / default.yml:

section-name:
  password: my-password
  username: my-name

3
Pit non ha alcuna documentazione
successhawk il

Come ha notato @successhawk - Non vedo NESSUNA documentazione in quei collegamenti github / pypi per "pit" - ma la descrizione sopra è chiara - e nel complesso mi piace questa soluzione per "nascondere" le credenziali da una facile visualizzazione ...
netdesignate

Sono riluttante a usare un modulo che non viene mantenuto e ricevo errori quando provo ad usarlo come indicato:/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

Se eseguito su Windows, è possibile utilizzare la libreria win32crypt. Consente l'archiviazione e il recupero di dati protetti (chiavi, password) da parte dell'utente che esegue lo script, pertanto le password non vengono mai archiviate in testo chiaro o in formato offuscato nel codice. Non sono sicuro che ci sia un'implementazione equivalente per altre piattaforme, quindi con l'uso rigoroso di win32crypt il tuo codice non è portatile.

Credo che il modulo possa essere ottenuto qui: http://timgolden.me.uk/pywin32-docs/win32crypt.html


1

Un modo in cui l'ho fatto è il seguente:

Alla shell di 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=='

Quindi, crea un modulo con il seguente codice:

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')

Una volta fatto, puoi importare direttamente mypassword oppure puoi importare il token e la crittografia per decrittografare secondo necessità.

Ovviamente, ci sono alcune carenze in questo approccio. Se qualcuno ha sia il token che la chiave (come farebbe se avessero lo script), possono decodificare facilmente. Tuttavia offusca, e se compili il codice (con qualcosa come Nuitka) almeno la tua password non apparirà come un semplice testo in un editor esadecimale.


0

Questo non risponde esattamente alla tua domanda, ma è correlato. Stavo per aggiungere come commento ma non mi era permesso. Ho avuto a che fare con lo stesso problema e abbiamo deciso di esporre lo script agli utenti utilizzando Jenkins. Questo ci consente di archiviare le credenziali del database in un file separato crittografato e protetto su un server e non accessibile ai non amministratori. Ci consente anche un po 'di scorciatoia per la creazione di un'interfaccia utente e l'esecuzione della limitazione.


0

È inoltre possibile considerare la possibilità di archiviare la password all'esterno dello script e di fornirla in fase di esecuzione

ad es. fred.py

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

che può essere eseguito come

$ PASSWORD=password123 python fred.py
fred password123

Ulteriori livelli di "sicurezza attraverso l'oscurità" possono essere raggiunti usando base64(come suggerito sopra), usando nomi meno ovvi nel codice e allontanando ulteriormente la password effettiva dal codice.

Se il codice si trova in un repository, è spesso utile archiviare i segreti al di fuori di esso , quindi è possibile aggiungerlo a ~/.bashrc(o a un vault o uno script di avvio, ...)

export SURNAME=cGFzc3dvcmQxMjM=

e passare fred.pya

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

quindi accedere nuovamente e

$ python fred.py
fred password123

0

Perché non avere un semplice xor?

vantaggi:

  • sembra un dato binario
  • nessuno può leggerlo senza conoscere la chiave (anche se è un singolo carattere)

Arrivo al punto in cui riconosco anche semplici stringhe b64 per parole comuni e rot13. Xor lo renderebbe molto più difficile.



-1

Esistono diverse utility ROT13 scritte in Python su 'Net - solo Google per loro. ROT13 codifica la stringa offline, la copia nella sorgente, la decodifica nel punto di trasmissione.

Ma questa è una protezione davvero debole ...


si prega di includere un collegamento o un codice di esempio per rendere questa risposta più utile
Micah Stubbs
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.