Ausblenden eines Passworts in einem Python-Skript (nur unsichere Verschleierung)

127

Ich habe ein Python-Skript, das eine ODBC-Verbindung herstellt. Die ODBC-Verbindung wird mit einer Verbindungszeichenfolge generiert. In diese Verbindungszeichenfolge muss ich den Benutzernamen und das Passwort für diese Verbindung einfügen.

Gibt es eine einfache Möglichkeit, dieses Passwort in der Datei zu verschleiern (nur dass niemand das Passwort lesen kann, wenn ich die Datei bearbeite)?

bernhardrusch
quelle
16
Denken Sie daran, dass die Benutzer, die diese Datei ausführen, zumindest Lesezugriff darauf haben und die Kennwörter problemlos abrufen können. Wenn Thins nur von Ihnen gelesen werden können und Sie sich Sorgen machen, dass die Leute es über Ihre Schulter sehen, versuchen Sie es, aber seien Sie gewarnt, während der durchschnittliche Beobachter sich die Dinge nicht schnell genug merken kann, um ein Passwort zu erhalten, jeder, der Zugriff auf das Skript hat, und ein Mit ein wenig technischem Know-how und ein wenig Ehrgeiz können Sie Ihre Passwörter abrufen. Denken Sie immer sehr sorgfältig über Sicherheit nach, es ist wichtig.
Youarefunny

Antworten:

117

Die Base64-Codierung befindet sich in der Standardbibliothek und kann Schulter-Surfer stoppen:

>>> import base64
>>>  print(base64.b64encode("password".encode("utf-8")))
cGFzc3dvcmQ=
>>> print(base64.b64decode("cGFzc3dvcmQ=").decode("utf-8"))
password
Dave Webb
quelle
5
Genau. Das Base64-codierte Passwort sieht viel mysteriöser aus.
Ed Haber
4
Es hilft aber nicht, dass das Skript vom Benutzer, der es ausführt, lesbar sein muss und das Kennwort nicht.
Martin Beckett
79
Ich denke nicht, dass base64das besser verschleiert als rot13in diesem Zusammenhang. Im Gegenteil, base64hat seine typischen Eigenschaften (Gleichheitszeichen, ...) und ist somit leichter erkennbar als andere Ansätze. Eine Verschleierung hat jedoch keinen praktischen Nutzen. Wirklich schlecht, dass diese Antwort so hoch bewertet ist. Es gibt nur ein falsches Gefühl der Sicherheit ...
Schlamar
12
Wenn Sie das Kennwort so aufzeichnen, dass es vom Skript verwendet werden kann, kann jeder, der Zugriff auf das Skript hat, das Kennwort abrufen, unabhängig davon, welche Verschlüsselungsmethode Sie verwenden. Die Anforderung hier war nur, das Passwort vor jemandem zu verbergen, der sich das Skript nur ansieht, während es geöffnet ist. In diesem Fall base64ist es vorzuziehen, rot13wie es in der Python-Standardbibliothek ist.
Dave Webb
17
base64 ist KEINE Verschlüsselung. es ist bestenfalls Verschleierung.
Csgeek
52

Douglas F Shearer's ist die allgemein anerkannte Lösung unter Unix, wenn Sie ein Kennwort für eine Remote-Anmeldung angeben müssen.
Sie fügen die Option --password-from-file hinzu , um den Pfad anzugeben und Klartext aus einer Datei zu lesen.
Die Datei kann sich dann im eigenen Bereich des Benutzers befinden, der vom Betriebssystem geschützt wird. Außerdem können verschiedene Benutzer automatisch ihre eigene Datei abrufen.

Für Kennwörter, die der Benutzer des Skripts nicht kennen darf, können Sie das Skript mit der angegebenen Berechtigung ausführen und die Kennwortdatei diesem Root- / Administratorbenutzer zuweisen.

Martin Beckett
quelle
1
Wie genau führen Sie das Skript mit erhöhten Berechtigungen aus, ohne ein Root- oder Administratorkennwort anzugeben? Bezieht es sich auf das Setzen von UID-Bits?
Youarefunny
4
Egal, ich habe es herausgefunden. Denn wer sonst jemand kümmert sich: Wenn ein Skript ein setuid - Bit das Betriebssystem gesetzt hat ‚passieren‘ das setuid Bit an den Interpreter. Leider gibt es massive Sicherheitslücken, so dass die meisten modernen Distributionen Setuid für Skripte deaktivieren.
Youarefunny
Ich kann keine Informationen zur Option --password-from-file finden. Hast du irgendwelche Beispiele? Vielen Dank!
Pyramidengesicht
@pyramidface - Ich meinte, dass Sie eine Funktion wie diese codieren und die Möglichkeit hinzufügen würden, ein Passwort aus einer Datei zu lesen
Martin Beckett
@MartinBeckett, aber wie Youarefunny sagte, müssten Sie setuid auf Python erhöhen, um dem Skript Root-Zugriff auf die Kennwortdatei zu gewähren?
Pyramidengesicht
51

Hier ist eine einfache Methode:

  1. Erstellen Sie ein Python-Modul - nennen wir es peekaboo.py.
  2. Geben Sie in peekaboo.py sowohl das Kennwort als auch den Code an, der dieses Kennwort benötigt
  3. Erstellen Sie eine kompilierte Version - peekaboo.pyc -, indem Sie dieses Modul importieren (über die Python-Befehlszeile usw.).
  4. Löschen Sie jetzt peekaboo.py.
  5. Sie können Peekaboo jetzt problemlos importieren, indem Sie sich nur auf peekaboo.pyc verlassen. Da peekaboo.pyc bytekompiliert ist, ist es für den Gelegenheitsbenutzer nicht lesbar.

Dies sollte etwas sicherer sein als die Base64-Dekodierung - obwohl es für einen py_to_pyc-Dekompiler anfällig ist.

manyPartsAreEdible
quelle
2
Dies hat immer noch einige Mängel, aber es ist tatsächlich sehr nah an dem, was ich will. Damit kann ich Python-Skripte testen, die Benutzer- / Kennwortverbindungen enthalten, ohne das Kennwort auf dem Bildschirm anzuzeigen oder es in die Eingabeaufforderung eingeben zu müssen. Nach dem Import von Peekaboo ist import peekabodas Passwort verfügbar als peekaboo.password(falls peekaboo.py enthalten password='secret')
Dannid
8
Wenn Sie diese Idee einen Schritt weiter gehen möchten, können Sie verwenden Cython jede Py - Datei in C zu kompilieren und eine Plattform - spezifische binäre erzeugen (zB: .pyd für Fenster, .so für Mac OS, etc) ... Von cythonizingIhrem Skript Wenn Sie die generierte Binärdatei freigeben, profitieren Sie von dieser Antwort. Fügen Sie eine weitere Verschleierungsebene hinzu, da Sie jetzt den C-Code dekompilieren müssen, um zum Kennwort zu gelangen. Dies ist nicht 100% sicher, aber es erfordert viel Arbeit, um zu den vertraulichen Daten zu gelangen, die Sie verbergen möchten.
Fnord
cythonizing, perfekt
Arno
29

Wenn Sie auf einem Unix-System arbeiten, nutzen Sie das netrc-Modul in der Standard-Python-Bibliothek. Es liest Passwörter aus einer separaten Textdatei (.netrc), deren Format hier beschrieben ist .

Hier ist ein kleines Anwendungsbeispiel:

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
quelle
19

Die beste Lösung, vorausgesetzt, der Benutzername und das Kennwort können vom Benutzer zur Laufzeit nicht angegeben werden, ist wahrscheinlich eine separate Quelldatei, die nur eine variable Initialisierung für den Benutzernamen und das Kennwort enthält, die in Ihren Hauptcode importiert werden. Diese Datei muss nur bearbeitet werden, wenn sich die Anmeldeinformationen ändern. Andernfalls ist die Base 64-Codierung wahrscheinlich die einfachste Lösung, wenn Sie sich nur um Schulter-Surfer mit durchschnittlichen Erinnerungen sorgen. ROT13 ist einfach zu einfach manuell zu dekodieren, unterscheidet nicht zwischen Groß- und Kleinschreibung und behält im verschlüsselten Zustand zu viel Bedeutung. Codieren Sie Ihr Passwort und Ihre Benutzer-ID außerhalb des Python-Skripts. Lassen Sie das Skript zur Laufzeit zur Verwendung dekodieren.

Das Angeben von Anmeldeinformationen für Skripte für automatisierte Aufgaben ist immer ein riskanter Vorschlag. Ihr Skript sollte über eigene Anmeldeinformationen verfügen und das verwendete Konto sollte keinen anderen Zugriff haben als genau das, was erforderlich ist. Zumindest sollte das Passwort lang und eher zufällig sein.

tduehr
quelle
Sehr schöne Antwort - danke. Für die kleinen Skripte, die ich schreibe (die sowieso Wartungsskripte sind - die BASE64-Codierung wird ausreichen)
bernhardrusch
2
Das hört sich gut an, aber können Sie eine Beispielimplementierung geben? Im Moment ist es nur eine Beschreibung einer allgemeinen Praxis und nicht so nützlich für jemanden, der dies noch nicht getan hat.
Dannid
18

Wie wäre es mit dem Importieren des Benutzernamens und des Passworts aus einer Datei außerhalb des Skripts? Auf diese Weise würde jemand, der das Skript erhalten würde, das Kennwort nicht automatisch erhalten.

Douglas F Shearer
quelle
15

base64 ist der richtige Weg für Ihre einfachen Bedürfnisse. Es muss nichts importiert werden:

>>> 'your string'.encode('base64')
'eW91ciBzdHJpbmc=\n'
>>> _.decode('base64')
'your string'
tzot
quelle
2
Was genau ist dumm?! Die ganze Antwort oder der nicht importierende Teil?
tzot
18
Base64 fügt nur die Illusion von Sicherheit hinzu.
FlySwat
13
Jonathan, es scheint, als hättest du die Frage nicht gelesen. Es geht um Dunkelheit (und eine sehr vorübergehende), nicht um Sicherheit , deshalb verstehe ich nicht, warum Sie meine Antwort für nicht hilfreich halten.
tzot
1
Ich wusste nicht, dass Sie dies tun können, anstatt das base64-Modul verwenden zu müssen. Und es gibt auch viele Codierungen wie zlib ... Spaß :)
Kiv
1
@Dennis Die Verwendung des base64-Moduls ist heutzutage der bevorzugte Weg. Letzteres funktioniert in neueren Versionen von Python nicht mehr.
Jeyekomon
5

Für die Python3- Verschleierung erfolgt die Verwendung base64anders:

import base64
base64.b64encode(b'PasswordStringAsStreamOfBytes')

was in ... endet

b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM='

Beachten Sie die informelle Zeichenfolgendarstellung. Die tatsächliche Zeichenfolge steht in Anführungszeichen

und Dekodieren zurück zum ursprünglichen String

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

Um dieses Ergebnis zu verwenden, wenn Zeichenfolgenobjekte erforderlich sind, kann das Byteobjekt übersetzt werden

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

Weitere Informationen darüber, wie Python3 mit Bytes (und Zeichenfolgen entsprechend) umgeht, finden Sie in der offiziellen Dokumentation .

Jitter
quelle
4

Dies ist ein ziemlich häufiges Problem. In der Regel ist das Beste, was Sie tun können, entweder

A) Erstellen Sie eine Art Ceasar-Verschlüsselungsfunktion zum Codieren / Decodieren (nur nicht rot13) oder

B) Die bevorzugte Methode besteht darin, einen Verschlüsselungsschlüssel zu verwenden, der in Reichweite Ihres Programms das Kennwort verschlüsselt / entschlüsselt. In dem Sie den Dateischutz verwenden können, um den Zugriff auf den Schlüssel zu schützen.

In diesem Sinne können Sie, wenn Ihre App als Dienst / Dämon (wie ein Webserver) ausgeführt wird, Ihren Schlüssel in einen kennwortgeschützten Schlüsselspeicher mit der Kennworteingabe als Teil des Dienststarts einfügen. Es wird einen Administrator benötigen, um Ihre App neu zu starten, aber Sie haben wirklich gute Voraussetzungen für Ihre Konfigurationskennwörter.

Mikhail_Sam
quelle
2

Ihr Betriebssystem bietet wahrscheinlich Möglichkeiten zum sicheren Verschlüsseln von Daten. Unter Windows gibt es beispielsweise DPAPI (Data Protection API). Fragen Sie den Benutzer beim ersten Ausführen nach seinen Anmeldeinformationen und entfernen Sie sie dann verschlüsselt für nachfolgende Ausführungen.

Jamie Eisenhart
quelle
2

Mehr hausgemachte Vorgehensweise als Konvertierung von Authentifizierung / Passwörtern / Benutzernamen in verschlüsselte Details. FTPLIB ist nur das Beispiel. "" pass.csv " ist der Name der CSV-Datei

Speichern Sie das Passwort in CSV wie folgt:

Nutzername

Benutzer-Passwort

(Ohne Spaltenüberschrift)

Lesen Sie die CSV und speichern Sie sie in einer Liste.

Verwenden von Listenelementen als Authentifizierungsdetails.

Vollständiger Code.

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])
Einsame Seele
quelle
2

Hier ist mein Ausschnitt für so etwas. Sie importieren oder kopieren die Funktion grundsätzlich in Ihren Code. getCredentials erstellt die verschlüsselte Datei, falls sie nicht vorhanden ist, und gibt ein Wörterbuch zurück. updateCredential wird aktualisiert.

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
quelle
1

Platzieren Sie die Konfigurationsinformationen in einer verschlüsselten Konfigurationsdatei. Fragen Sie diese Informationen in Ihrem Code mit einem Schlüssel ab. Platzieren Sie diesen Schlüssel in einer separaten Datei pro Umgebung und speichern Sie ihn nicht mit Ihrem Code.

FlySwat
quelle
1

Kennst du Grube?

https://pypi.python.org/pypi/pit (nur py2 (Version 0.3))

https://github.com/yoshiori/pit (es funktioniert auf py3 (aktuelle Version 0.4))

test.py

from pit import Pit

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

Lauf:

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

~ / .pit / default.yml:

section-name:
  password: my-password
  username: my-name
TakesxiSximada
quelle
3
Pit hat keine Dokumentation
successhawk
Wie @successhawk bemerkt hat - ich sehe KEINE Dokumentation in diesen Github / Pypi-Links für "pit" - aber die obige Beschreibung ist klar - und insgesamt gefällt mir diese Lösung zum "Verstecken" von Anmeldeinformationen vor der einfachen Ansicht ...
netdesignate
Ich zögere es, ein Modul zu verwenden, das nicht gewartet wird, und es werden Fehler /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))
angezeigt
1

Wenn Sie unter Windows ausgeführt werden, können Sie die win32crypt-Bibliothek verwenden. Es ermöglicht dem Benutzer, der das Skript ausführt, das Speichern und Abrufen geschützter Daten (Schlüssel, Kennwörter). Daher werden Kennwörter in Ihrem Code niemals im Klartext oder im verschleierten Format gespeichert. Ich bin nicht sicher, ob es eine gleichwertige Implementierung für andere Plattformen gibt. Mit der strikten Verwendung von win32crypt ist Ihr Code also nicht portierbar.

Ich glaube, das Modul ist hier erhältlich: http://timgolden.me.uk/pywin32-docs/win32crypt.html

VilleLipponen
quelle
1

Ich habe dies folgendermaßen getan:

An der Python-Shell:

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

Erstellen Sie dann ein Modul mit dem folgenden Code:

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

Sobald Sie dies getan haben, können Sie entweder mein Passwort direkt importieren oder das Token und die Verschlüsselung importieren, um sie nach Bedarf zu entschlüsseln.

Offensichtlich weist dieser Ansatz einige Mängel auf. Wenn jemand sowohl das Token als auch den Schlüssel hat (wie wenn er das Skript hätte), kann er leicht entschlüsseln. Es verschleiert sich jedoch, und wenn Sie den Code kompilieren (mit etwas wie Nuitka), wird Ihr Passwort zumindest nicht als einfacher Text in einem Hex-Editor angezeigt.

Dr_Z2A
quelle
0

Dies beantwortet Ihre Frage nicht genau, ist aber verwandt. Ich wollte als Kommentar hinzufügen, war aber nicht erlaubt. Ich habe mich mit demselben Problem befasst, und wir haben beschlossen, das Skript den Benutzern mit Jenkins zur Verfügung zu stellen. Auf diese Weise können wir die Datenbankanmeldeinformationen in einer separaten Datei speichern, die verschlüsselt und auf einem Server gesichert ist und für Nicht-Administratoren nicht zugänglich ist. Es ermöglicht uns auch eine Abkürzung zum Erstellen einer Benutzeroberfläche und zum Drosseln der Ausführung.

Joe Hayes
quelle
0

Sie können auch die Möglichkeit in Betracht ziehen, das Kennwort außerhalb des Skripts zu speichern und zur Laufzeit bereitzustellen

zB fred.py

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

das kann wie laufen

$ PASSWORD=password123 python fred.py
fred password123

Zusätzliche Schichten von "Sicherheit durch Dunkelheit" können durch Verwendung erreicht werden base64 (wie oben vorgeschlagen) weniger offensichtliche Namen im Code verwendet werden und das tatsächliche Kennwort weiter vom Code entfernt wird.

Wenn sich der Code in einem Repository befindet, ist es oft nützlich, Geheimnisse außerhalb des Repositorys zu speichern , sodass man dies hinzufügen kann ~/.bashrc(oder einem Tresor oder einem Startskript, ...).

export SURNAME=cGFzc3dvcmQxMjM=

und wechseln fred.pyzu

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

dann erneut anmelden und

$ python fred.py
fred password123
Jalanb
quelle
0

Warum nicht ein einfaches xor haben?

Vorteile:

  • sieht aus wie Binärdaten
  • Niemand kann es lesen, ohne den Schlüssel zu kennen (auch wenn es sich um ein einzelnes Zeichen handelt).

Ich komme an den Punkt, an dem ich einfache b64-Zeichenfolgen für gebräuchliche Wörter und auch rot13 erkenne. Xor würde es viel schwieriger machen.

SL
quelle
0
import base64
print(base64.b64encode("password".encode("utf-8")))
print(base64.b64decode(b'cGFzc3dvcmQ='.decode("utf-8")))
Pradyot
quelle
Dies ist die Lösung aus der akzeptierten Antwort .
Sergey Shubin
-1

Es gibt mehrere ROT13-Dienstprogramme, die in Python im Internet geschrieben sind - googeln Sie einfach nach ihnen. ROT13 codiert die Zeichenfolge offline, kopiert sie in die Quelle und decodiert sie am Übertragungspunkt.

Aber das ist wirklich schwacher Schutz ...

Kevin Little
quelle
Bitte fügen Sie einen Link oder einen Beispielcode hinzu, um diese Antwort nützlicher zu machen
Micah Stubbs