Скрытие пароля в скрипте Python (только небезопасная обфускация)

127

У меня есть скрипт Python, который создает соединение ODBC. Соединение ODBC создается с помощью строки подключения. В этой строке подключения я должен указать имя пользователя и пароль для этого подключения.

Есть ли простой способ скрыть этот пароль в файле (просто чтобы никто не мог прочитать пароль, когда я редактирую файл)?

bernhardrusch
источник
16
Просто помните, что пользователи, запустившие этот файл, будут иметь как минимум доступ для чтения к нему и могут легко получать пароли. Если вы можете прочитать его только вам, и вы беспокоитесь о том, что люди увидят его через плечо, сделайте это, но имейте в виду, что средний наблюдатель не может запоминать вещи достаточно быстро, чтобы захватить пароль, любой, у кого есть доступ к сценарию и небольшие технические ноу-хау и небольшое количество амбиций позволят получить ваши пароли. Всегда очень тщательно продумывайте безопасность, это важно.
Youarefunny

Ответы:

117

Кодировка Base64 находится в стандартной библиотеке и подойдет, чтобы остановить серферов по плечу:

>>> import base64
>>>  print(base64.b64encode("password".encode("utf-8")))
cGFzc3dvcmQ=
>>> print(base64.b64decode("cGFzc3dvcmQ=").decode("utf-8"))
password
Дэйв Уэбб
источник
5
Я согласен. Пароль в кодировке base64 выглядит гораздо более загадочным.
Эд Хабер,
4
Но не помогает тот факт, что скрипт должен быть доступен для чтения пользователю, запускающему его, а пароль - нет.
Мартин Беккет,
79
Я не думаю, что base64это лучше запутать, чем rot13в данном контексте. Напротив, base64имеет свои типичные характеристики (знак равенства, ...) и поэтому его легче обнаружить, чем другие подходы. Однако всякая обфускация не имеет практической пользы. Действительно плохо, что этот ответ так высоко оценен. Это просто дает ложное чувство безопасности ...
schlamar
12
Если вы записываете пароль, чтобы его можно было использовать в сценарии, любой, у кого есть доступ к сценарию, сможет получить пароль, независимо от того, какой метод шифрования вы используете. Здесь требовалось просто скрыть пароль от кого-либо, просто смотрящего на скрипт, пока он открыт. В этом случае base64предпочтительнее, rot13чем в стандартной библиотеке Python.
Дэйв Уэбб
17
base64 НЕ является шифрованием. в лучшем случае это обфускация.
csgeek
52

Douglas F Shearer's - это обычно одобренное решение в Unix, когда вам нужно указать пароль для удаленного входа в систему.
Вы добавляете параметр --password-from-file, чтобы указать путь и прочитать открытый текст из файла.
После этого файл может находиться в собственной области пользователя, защищенной операционной системой. Это также позволяет различным пользователям автоматически выбирать свой собственный файл.

Для паролей, которые пользователю сценария не разрешено знать - вы можете запустить сценарий с повышенным разрешением и иметь файл паролей, принадлежащий этому пользователю root / admin.

Мартин Беккет
источник
1
Как именно запустить сценарий с повышенными правами без указания пароля root или администратора? Это связано с установкой битов UID?
Youarefunny
4
Неважно, я понял это. Для всех , кто заботится: Если сценарий имеет УИП установлен бит ОС будет «проходить» в УИП немного переводчику. К сожалению, есть огромные дыры в безопасности, поэтому большинство современных дистрибутивов отключают setuid для скриптов.
Youarefunny
Я не могу найти никакой информации о параметре --password-from-file. У вас есть примеры? Спасибо!
pyramidface
@pyramidface - я имел в виду, что вы закодируете такую ​​функцию и добавите возможность читать пароль из файла
Мартин Беккет,
@MartinBeckett, но, как сказал Youarefunny, вам придется поднять setuid на python, чтобы предоставить скрипту root-доступ к файлу паролей?
pyramidface
51

Вот простой способ:

  1. Создайте модуль python - назовем его peekaboo.py.
  2. В peekaboo.py укажите как пароль, так и любой код, требующий этого пароля.
  3. Создайте скомпилированную версию - peekaboo.pyc - путем импорта этого модуля (через командную строку python и т. Д.).
  4. Теперь удалите peekaboo.py.
  5. Теперь вы можете успешно импортировать peekaboo, полагаясь только на peekaboo.pyc. Поскольку peekaboo.pyc компилируется побайтно, он не читается обычным пользователем.

Это должно быть немного безопаснее, чем декодирование base64, хотя оно уязвимо для декомпилятора py_to_pyc.

manyPartsAreEdible
источник
2
У этого все еще есть некоторые недостатки, но на самом деле это очень близко к тому, что я хочу. Это позволит мне продемонстрировать скрипты Python, которые включают соединения user / passwd, не показывая пароль на экране или не вводя его в командной строке. После импорта peekaboo import peekaboпароль доступен как peekaboo.password(если peekaboo.py содержится password='secret')
Dannid
8
Если вы хотите развить эту идею еще на один шаг, вы можете использовать Cython для компиляции любого файла .py в C и создания двоичного файла для конкретной платформы (например, .pyd для Windows, .so для macOS и т. Д.) ... С помощью cythonizingвашего скрипта и, поделившись сгенерированным двоичным файлом, вы получите преимущество этого ответа + добавьте еще один уровень обфускации, потому что теперь у вас есть декомпилированный код C, чтобы получить пароль. Это не на 100% безопасно, но потребуется много усилий, чтобы добраться до конфиденциальных данных, которые вы хотите скрыть.
Fnord
cythonizing, perfect
Arno
29

Если вы работаете в системе Unix, воспользуйтесь преимуществами модуля netrc в стандартной библиотеке Python. Он считывает пароли из отдельного текстового файла (.netrc), формат которого описан здесь .

Вот небольшой пример использования:

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
источник
19

Лучшее решение, предполагающее, что имя пользователя и пароль не могут быть указаны пользователем во время выполнения, вероятно, представляет собой отдельный исходный файл, содержащий только инициализацию переменных для имени пользователя и пароля, которые импортируются в ваш основной код. Этот файл потребуется редактировать только при изменении учетных данных. В противном случае, если вас беспокоят только плечевые серферы со средней памятью, кодировка base 64, вероятно, будет самым простым решением. ROT13 слишком легко декодировать вручную, он не чувствителен к регистру и сохраняет слишком много смысла в зашифрованном состоянии. Закодируйте свой пароль и идентификатор пользователя вне сценария Python. Он должен декодировать сценарий во время выполнения для использования.

Предоставление учетных данных сценариям для автоматизированных задач - всегда рискованное предложение. У вашего сценария должны быть собственные учетные данные, а используемая им учетная запись не должна иметь доступа, кроме необходимого. По крайней мере, пароль должен быть длинным и случайным.

tduehr
источник
Очень хороший ответ - спасибо. Для небольших сценариев, которые я пишу (которые в любом случае являются сценариями обслуживания - достаточно кодировки BASE64)
bernhardrusch
2
Звучит неплохо, но вы можете привести пример реализации? Сейчас это просто описание общей практики, и она не так полезна для тех, кто не делал этого раньше.
Dannid
18

Как насчет импорта имени пользователя и пароля из файла, внешнего по отношению к сценарию? Таким образом, даже если кто-то завладеет скриптом, он не получит пароль автоматически.

Дуглас Ф. Ширер
источник
15

base64 - это способ удовлетворить ваши простые потребности. Импортировать ничего не нужно:

>>> 'your string'.encode('base64')
'eW91ciBzdHJpbmc=\n'
>>> _.decode('base64')
'your string'
tzot
источник
2
Что именно глупо ?! Полный ответ или неимпортирующая часть?
tzot 01
18
Base64 только добавляет иллюзию безопасности.
FlySwat 01
13
Джонатан, похоже, ты не читал вопрос. Речь идет о безвестности (и очень временной), а не о безопасности , поэтому я не понимаю, почему вы считаете мой ответ бесполезным.
tzot 01
1
Я не знал, что вы можете сделать это вместо использования модуля base64. И кодировок тоже много, как и zlib ... весело :)
Kiv
1
@Dennis Использование модуля base64 в настоящее время является предпочтительным способом. Последний больше не работает в новых версиях Python.
Jeyekomon
5

для обфускации python3 использование base64выполняется иначе:

import base64
base64.b64encode(b'PasswordStringAsStreamOfBytes')

что приводит к

b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM='

обратите внимание на неформальное строковое представление, фактическая строка заключена в кавычки

и декодирование обратно в исходную строку

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

чтобы использовать этот результат там, где требуются строковые объекты, можно перевести объект байтов

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

для получения дополнительной информации о том, как python3 обрабатывает байты (и соответственно строки), см. официальную документацию .

дрожание
источник
4

Это довольно распространенная проблема. Обычно лучшее, что вы можете сделать, - это либо

A) создать какую-то функцию шифрования ceasar для кодирования / декодирования (только не rot13) или

Б) предпочтительным методом является использование ключа шифрования, доступного для вашей программы, для кодирования / декодирования пароля. В котором вы можете использовать защиту файлов для защиты доступа к ключу.

В соответствии с этими строками, если ваше приложение работает как служба / демон (например, веб-сервер), вы можете поместить свой ключ в защищенное паролем хранилище ключей с вводом пароля как часть запуска службы. Для перезапуска вашего приложения потребуется администратор, но у вас будет действительно хорошая защита для ваших паролей конфигурации.

Mikhail_Sam
источник
2

Ваша операционная система, вероятно, предоставляет средства для безопасного шифрования данных. Например, в Windows есть DPAPI (API защиты данных). Почему бы не спросить у пользователя их учетные данные при первом запуске, а затем зашифровать их для последующих запусков?

Джейми Эйзенхарт
источник
2

Больше отечественной оценки, а не преобразования аутентификации / паролей / имени пользователя в зашифрованные данные. FTPLIB - это всего лишь пример. " pass.csv " - это имя файла csv.

Сохраните пароль в CSV, как показано ниже:

user_name

пользовательский пароль

(Без заголовка столбца)

Чтение CSV и сохранение его в списке.

Использование элементов списка в качестве деталей аутентификации.

Полный код.

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

Вот мой фрагмент на такую ​​штуку. Вы в основном импортируете или копируете функцию в свой код. getCredentials создаст зашифрованный файл, если он не существует, и вернет словарь, а updateCredential обновится.

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

Поместите информацию о конфигурации в зашифрованный файл конфигурации. Запросите эту информацию в своем коде с помощью ключа. Поместите этот ключ в отдельный файл для каждой среды и не храните его вместе с кодом.

FlySwat
источник
1

Вы знаете яму?

https://pypi.python.org/pypi/pit (только для py2 (версия 0.3))

https://github.com/yoshiori/pit (будет работать на py3 (текущая версия 0.4))

test.py

from pit import Pit

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

Бегать:

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

~ / .Pit / default.yml:

section-name:
  password: my-password
  username: my-name
TakesxiSximada
источник
3
Pit не имеет документации
successhawk
Как отметил @successhawk - я не вижу НИКАКОЙ документации в этих ссылках на github / pypi для «ямы» - но приведенное выше описание ясно - и в целом мне нравится это решение для «сокрытия» учетных данных с легкого просмотра ...
netdesignate
Я не хочу использовать модуль, который не поддерживается, и я получаю ошибки, когда пытаюсь использовать его в соответствии с инструкциями:/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

Если вы работаете в Windows, вы можете рассмотреть возможность использования библиотеки win32crypt. Он позволяет хранить и извлекать защищенные данные (ключи, пароли) пользователем, который запускает скрипт, поэтому пароли никогда не хранятся в открытом тексте или в запутанном формате в вашем коде. Я не уверен, есть ли эквивалентная реализация для других платформ, поэтому при строгом использовании win32crypt ваш код не переносится.

Я считаю, что модуль можно получить здесь: http://timgolden.me.uk/pywin32-docs/win32crypt.html

VilleLipponen
источник
1

Я сделал это следующим образом:

В оболочке 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=='

Затем создайте модуль со следующим кодом:

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

Как только вы это сделаете, вы можете либо напрямую импортировать mypassword, либо импортировать токен и шифр для дешифрования по мере необходимости.

Очевидно, у этого подхода есть некоторые недостатки. Если у кого-то есть и токен, и ключ (как если бы у них был сценарий), он мог легко расшифровать. Однако он запутывается, и если вы скомпилируете код (с чем-то вроде Nuitka), по крайней мере, ваш пароль не будет отображаться в виде обычного текста в шестнадцатеричном редакторе.

Dr_Z2A
источник
0

Это не совсем ответ на ваш вопрос, но это связано. Я собирался добавить в качестве комментария, но мне не разрешили. Я столкнулся с этой же проблемой, и мы решили предоставить скрипт пользователям, использующим Jenkins. Это позволяет нам хранить учетные данные db в отдельном файле, который зашифрован и защищен на сервере и недоступен для пользователей, не являющихся администраторами. Это также позволяет нам немного ускорить создание пользовательского интерфейса и регулирование выполнения.

Джо Хейс
источник
0

Вы также можете рассмотреть возможность хранения пароля вне сценария и предоставления его во время выполнения.

например, fred.py

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

который можно запустить как

$ PASSWORD=password123 python fred.py
fred password123

Дополнительные уровни «безопасности через неясность» могут быть достигнуты путем использования base64(как предложено выше), использования менее очевидных имен в коде и дальнейшего отделения фактического пароля от кода.

Если код находится в репозитории, часто бывает полезно хранить секреты вне него , чтобы можно было добавить его ~/.bashrc(или в хранилище, или сценарий запуска, ...)

export SURNAME=cGFzc3dvcmQxMjM=

и изменить fred.pyна

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

затем повторно авторизуйтесь и

$ python fred.py
fred password123
jalanb
источник
0

Почему бы не использовать простой xor?

Преимущества:

  • выглядит как двоичные данные
  • никто не может прочитать его, не зная ключа (даже если это один символ)

Я дохожу до того, что распознаю простые строки b64 для общих слов, а также rot13. Xor сделает это намного сложнее.

SL
источник
0
import base64
print(base64.b64encode("password".encode("utf-8")))
print(base64.b64decode(b'cGFzc3dvcmQ='.decode("utf-8")))
pradyot
источник
Это решение из принятого ответа .
Сергей Шубин
-1

В сети есть несколько утилит ROT13, написанных на Python - просто погуглите. ROT13 кодирует строку в автономном режиме, копирует ее в источник, декодирует в точке передачи.

Но это действительно слабая защита ...

Кевин Литтл
источник
пожалуйста,
Мика Стаббс