Приховування пароля в сценарії python (лише небезпечна обфускація)


127

У мене є сценарій python, який створює ODBC-з'єднання. З'єднання ODBC генерується за допомогою рядка з'єднання. У цьому рядку з'єднання я повинен включати ім’я користувача та пароль для цього з'єднання.

Чи є простий спосіб затьмарити цей пароль у файлі (тільки що ніхто не може прочитати пароль, коли я редагую файл)?


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 - Я мав на увазі, що ви будете кодувати таку функцію та додавати можливість читати пароль із файлу
Martin Beckett

@MartinBeckett, але, як сказав Youarefunny, вам доведеться підняти setuid на python для того, щоб надати root-скрипту доступ до файлу пароля?
пірамідальна поверхня

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.


2
Це все ще має деякі недоліки, але насправді це дуже близько до того, що я хочу. Це дозволить мені демонструвати сценарії python, які включають з'єднання користувача / passwd, не розкриваючи екран на екрані або вводячи його в командний рядок. Після імпорту peekaboo import peekaboпароль доступний як peekaboo.password(якщо peekaboo.py міститься password='secret')
Dannid

8
Якщо ви хочете зробити цю ідею ще на крок, ви можете використовувати Cython для компіляції будь-якого .py-файлу в C та створення бінарного файлу, визначеного для платформи (наприклад: .pyd для Windows, .so для macOS тощо) ... За cythonizingвашим сценарієм і поділившись створеним двійковим файлом, ви отримаєте перевагу від цієї відповіді + додасте ще один шар обфускування, тому що тепер у вас є декомпілювати код C, щоб дістатися паролю. Це не на 100% безпека, але знадобиться багато праці, щоб дійти до конфіденційних даних, які ви хочете приховати.
Фнорд

цитонізуючий, досконалий
Арно

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

19

Найкраще рішення, якщо припустити, що ім’я користувача та пароль не можуть бути надані користувачем під час виконання, це, мабуть, окремий вихідний файл, що містить лише змінну ініціалізацію імені користувача та пароля, імпортованих у ваш основний код. Цей файл потребуватиме редагування лише тоді, коли зміниться облікові дані. В іншому випадку, якщо ви переживаєте лише за плечових серферів із середньою пам’яттю, кодування на базі 64 - це, мабуть, найпростіше рішення. ROT13 просто занадто просто розшифрувати вручну, не відрізняється від регістру і зберігає надто багато значення в зашифрованому стані. Кодуйте свій пароль та ідентифікатор користувача поза сценарієм python. Нехай він сценарій декодує під час виконання для використання.

Надання облікових даних сценаріїв для автоматизованих завдань - це завжди ризикована пропозиція. У вашого сценарію повинні бути власні облікові дані, а обліковий запис, який він використовує, не повинен мати іншого доступу, крім того, що саме є необхідним. Принаймні пароль повинен бути довгим і досить випадковим.


Дуже приємна відповідь - дякую. Щодо невеликих сценаріїв, які я пишу (а це сценарії технічного обслуговування в будь-якому випадку - вистачить кодування BASE64)
bernhardrusch

2
Це добре звучить, але ви можете навести приклад реалізації? Зараз це лише опис загальної практики, і не такий корисний для когось ще цього не робив.
Даннід

18

Як щодо імпортування імені користувача та пароля з файлу, зовнішнього до сценарію? Таким чином, навіть якщо хтось отримав сценарій, він автоматично не отримає пароль.


15

base64 - це шлях до ваших простих потреб. Не потрібно нічого імпортувати:

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

2
Що саме дурне ?! Ціла відповідь чи частина, яка не імпортує?
tzot

18
Base64 лише додає ілюзії безпеки.
FlySwat

13
Джонатане, здається, ти не читав питання. Мова йде про незрозумілість (і дуже тимчасову), а не про безпеку , тому я не розумію, чому ви вважаєте, що моя відповідь не є корисною.
tzot

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

Це досить поширена проблема. Зазвичай найкраще, що ви можете зробити, це будь-яке

А) створити якусь функцію шифру ceasar для кодування / декодування (тільки не гниль13) або

Б) кращим методом є використання ключа шифрування, в межах досяжності вашої програми, кодування / декодування пароля. У якому ви можете використовувати захист файлів для захисту доступу до ключа.

У відповідності до цих рядків, якщо ваша програма працює як служба / демон (як веб-сервер), ви можете помістити свій ключ у захищену паролем сховище ключів із введенням пароля як частина запуску служби. Адміністратору потрібно буде перезапустити додаток, але у вас буде справді хороший привід для паролів конфігурації.


2

Ваша операційна система, ймовірно, забезпечує засоби для безпечного шифрування даних. Наприклад, у Windows є DPAPI (API захисту даних). Чому б не попросити у користувача їхніх облікових даних при першому запуску, а потім білка їх зашифрована для наступних запусків?


2

Більше домашній додаток, а не перетворення автентифікації / паролів / імені користувача для зашифрованих даних. FTPLIB - лише приклад. " pass.csv " - це ім'я файлу CSV

Збережіть пароль у CSV, як показано нижче:

ім'я користувача

user_password

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

Читання 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

Помістіть інформацію про конфігурацію у зашифрований файл конфігурації. Запитайте цю інформацію у своєму коді за допомогою ключа. Розміщуйте цей ключ в окремому файлі за середовищем, а не зберігайте його разом із кодом.


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

3
У Pit немає жодної документації
successhawk

Як зазначав @successhawk - я не бачу жодної документації у цих посиланнях github / pypi на "pit" - але вищеописаний опис зрозумілий - і в цілому мені подобається це рішення для "приховування" облікових даних від простого перегляду ...
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


1

Я зробив це таким чином:

На оболонці пітона:

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

Після цього ви можете або імпортувати мій пароль безпосередньо, або ви можете імпортувати маркер і шифр, щоб розшифрувати за потребою.

Очевидно, у цього підходу є деякі недоліки. Якщо хтось має і маркер, і ключ (як це було б, якщо у них є сценарій), він може легко розшифрувати. Однак це примушує, і якщо ви компілюєте код (з чимось на зразок Nuitka), принаймні ваш пароль не з’явиться як звичайний текст у шестигранному редакторі.


0

Це не відповідає точно на ваше запитання, але це пов'язано. Я збирався додати коментар, але мені не було дозволено. Я займався цим самим питанням, і ми вирішили викрити сценарій користувачам, що користуються Дженкінсом. Це дозволяє нам зберігати облікові дані 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

0

Чому б не простий xor?

Переваги:

  • виглядає як двійкові дані
  • ніхто не може прочитати його, не знаючи ключа (навіть якщо це одна картка)

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



-1

Існує кілька утиліт ROT13, написаних на Python в мережі "Мережа - для них просто google". ROT13 кодує рядок в автономному режимі, скопіюйте її в джерело, декодуйте в точці передачі.

Але це справді слабкий захист ...


будь-ласка, включіть посилання або зразок коду, щоб зробити цю відповідь кориснішою
Micah Stubbs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.