Ẩn mật khẩu trong tập lệnh python (chỉ che giấu không an toàn)


127

Tôi đã có một tập lệnh python đang tạo kết nối ODBC. Kết nối ODBC được tạo bằng chuỗi kết nối. Trong chuỗi kết nối này, tôi phải bao gồm tên người dùng và mật khẩu cho kết nối này.

Có một cách dễ dàng để che khuất mật khẩu này trong tệp (chỉ là không ai có thể đọc mật khẩu khi tôi chỉnh sửa tệp)?


16
Chỉ cần nhớ rằng người dùng đang chạy tệp này sẽ có ít nhất quyền truy cập đọc vào nó và có thể dễ dàng lấy mật khẩu. Nếu bạn chỉ có thể đọc được và bạn lo lắng về việc mọi người nhìn thấy nó trên vai bạn, nhưng hãy cảnh báo trong khi người quan sát trung bình không thể ghi nhớ mọi thứ đủ nhanh để lấy mật khẩu, bất kỳ ai có quyền truy cập vào tập lệnh và một chút bí quyết kỹ thuật và một chút tham vọng sẽ có thể lấy được mật khẩu của bạn. Luôn luôn nghĩ an ninh thông qua rất cẩn thận, nó quan trọng.
Youarefunny

Câu trả lời:


117

Mã hóa Base64 nằm trong thư viện chuẩn và sẽ làm gì để ngăn những người lướt vai:

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

5
Tôi đồng ý. Mật khẩu được mã hóa base64 trông bí ẩn hơn nhiều.
Ed Haber

4
Nhưng điều đó không giúp ích gì cho việc người dùng có thể đọc được tập lệnh và mật khẩu không được đọc.
Martin Beckett

79
Tôi không nghĩ rằng đó base64là tốt hơn so với rot13trong bối cảnh này. Ngược lại, base64có các đặc điểm điển hình của nó (dấu bằng, ...) và do đó dễ phát hiện hơn các phương pháp khác. Bất kỳ obfuscation không có lợi ích thiết thực, mặc dù. Thực sự xấu rằng câu trả lời này được đánh giá cao. Nó chỉ mang lại cảm giác an toàn sai lầm ...
schlamar

12
Nếu bạn đang ghi lại mật khẩu để tập lệnh có thể sử dụng, bất kỳ ai có quyền truy cập vào tập lệnh sẽ có thể lấy mật khẩu, bất kể bạn sử dụng phương thức mã hóa nào. Yêu cầu ở đây chỉ là ẩn mật khẩu khỏi ai đó chỉ nhìn vào tập lệnh trong khi nó đang mở. Trong trường hợp base64này là thích hợp hơn rot13như trong thư viện chuẩn Python.
Dave Webb

17
base64 KHÔNG mã hóa. đó là obfuscation tốt nhất.
cgeek

52

Douglas F Shearer's là giải pháp được phê duyệt chung trong Unix khi bạn cần chỉ định mật khẩu cho đăng nhập từ xa.
Bạn thêm tùy chọn --password-from-file để chỉ định đường dẫn và đọc văn bản gốc từ một tệp.
Các tập tin sau đó có thể nằm trong khu vực riêng của người dùng được bảo vệ bởi hệ điều hành. Nó cũng cho phép những người dùng khác nhau tự động lấy tập tin của riêng họ.

Đối với mật khẩu mà người dùng tập lệnh không được phép biết - bạn có thể chạy tập lệnh với sự cho phép được giải thích và có tệp mật khẩu thuộc sở hữu của người dùng root / admin đó.


1
Làm thế nào chính xác để bạn chạy tập lệnh với quyền nâng cao mà không cần cung cấp mật khẩu gốc hoặc mật khẩu quản trị viên? Có liên quan đến việc thiết lập các bit UID không?
Youarefunny

4
Không sao đâu, tôi đã đoán ra. Đối với bất kỳ ai khác quan tâm: Nếu tập lệnh có bit setuid, hệ điều hành sẽ 'chuyển' bit setuid cho trình thông dịch. Thật không may, có những lỗ hổng bảo mật lớn, vì vậy hầu hết các distro hiện đại đều tắt setuid cho các script.
Youarefunny

Tôi không thể tìm thấy bất kỳ thông tin nào về tùy chọn --password-from-file. bạn có bất kì ví dụ nào không? Cảm ơn!
kim tự tháp

@pyramidface - Ý tôi là bạn sẽ mã hóa một tính năng như thế này và thêm khả năng đọc mật khẩu từ một tệp
Martin Beckett

@MartinBeckett nhưng như Youarefunny đã nói, bạn sẽ phải nâng setuid lên python để cấp quyền truy cập tập lệnh gốc vào tập tin mật khẩu?
kim tự tháp

51

Đây là một phương pháp đơn giản:

  1. Tạo một mô-đun python - hãy gọi nó là peekaboo.py.
  2. Trong peekaboo.py, bao gồm cả mật khẩu và bất kỳ mã nào cần mật khẩu đó
  3. Tạo một phiên bản được biên dịch - peekaboo.pyc - bằng cách nhập mô-đun này (thông qua dòng lệnh python, v.v ...).
  4. Bây giờ, xóa peekaboo.py.
  5. Bây giờ bạn có thể vui vẻ nhập peekaboo chỉ dựa vào peekaboo.pyc. Vì peekaboo.pyc là byte được biên dịch nên người dùng thông thường không thể đọc được.

Điều này sẽ an toàn hơn một chút so với giải mã base64 - mặc dù nó dễ bị bộ dịch ngược py_to_pyc.


2
Điều này vẫn còn một số thiếu sót, nhưng nó thực sự rất gần với những gì tôi muốn. Nó sẽ cho phép tôi giới thiệu các tập lệnh python bao gồm các kết nối người dùng / mật khẩu mà không tiết lộ mật khẩu trên màn hình hoặc phải nhập nó vào dấu nhắc lệnh. Sau khi nhập peekaboo import peekabo, mật khẩu có sẵn dưới dạng peekaboo.password(nếu có peekaboo.py password='secret')
Dannid

8
Nếu bạn muốn đưa ý tưởng này thêm một bước nữa, bạn có thể sử dụng Cython để biên dịch bất kỳ tập tin py vào C và tạo ra một nhị phân nền tảng cụ thể (ví dụ: .pyd cho các cửa sổ, .so cho hệ điều hành MacOS, vv) ... Bởi cythonizingkịch bản của bạn và chia sẻ nhị phân được tạo, bạn sẽ nhận được lợi ích của câu trả lời này + thêm một lớp mã hóa khác, vì bây giờ bạn đã dịch ngược mã C để lấy mật khẩu. Điều này không an toàn 100%, nhưng sẽ mất rất nhiều công sức để có được dữ liệu nhạy cảm mà bạn muốn ẩn.
Fnord

cythonizing, hoàn hảo
Arno

29

Nếu bạn đang làm việc trên một hệ thống Unix, hãy tận dụng mô-đun netrc trong thư viện Python chuẩn. Nó đọc mật khẩu từ một tệp văn bản riêng biệt (.netrc), có định dạng được giải mã ở đây .

Đây là một ví dụ sử dụng nhỏ:

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

Giải pháp tốt nhất, giả sử tên người dùng và mật khẩu không thể được cung cấp trong thời gian chạy bởi người dùng, có lẽ là một tệp nguồn riêng biệt chỉ chứa khởi tạo biến cho tên người dùng và mật khẩu được nhập vào mã chính của bạn. Tập tin này chỉ cần chỉnh sửa khi thông tin đăng nhập thay đổi. Mặt khác, nếu bạn chỉ lo lắng về những người lướt vai có bộ nhớ trung bình, mã hóa cơ sở 64 có lẽ là giải pháp dễ nhất. ROT13 quá dễ để giải mã thủ công, không phân biệt chữ hoa chữ thường và giữ quá nhiều ý nghĩa trong trạng thái được mã hóa. Mã hóa mật khẩu và id người dùng bên ngoài tập lệnh python. Có anh ấy giải mã kịch bản lúc chạy để sử dụng.

Cung cấp thông tin đăng nhập cho các tác vụ tự động luôn là một đề xuất rủi ro. Tập lệnh của bạn phải có thông tin đăng nhập riêng và tài khoản mà nó sử dụng sẽ không có quyền truy cập nào ngoài chính xác những gì cần thiết. Ít nhất là mật khẩu phải dài và khá ngẫu nhiên.


Câu trả lời rất hay - cảm ơn bạn. Đối với các tập lệnh nhỏ tôi đang viết (dù sao cũng là tập lệnh bảo trì - mã hóa BASE64 sẽ đủ)
bernhardrusch

2
Điều này nghe có vẻ tốt, nhưng bạn có thể đưa ra một ví dụ thực hiện? Ngay bây giờ, nó chỉ là một mô tả về một thực tiễn chung, và không hữu ích cho ai đó đã không làm điều này trước đây.
Dannid

18

Làm thế nào về việc nhập tên người dùng và mật khẩu từ một tập tin bên ngoài vào tập lệnh? Theo cách đó, ngay cả khi ai đó nắm giữ tập lệnh, họ sẽ không tự động lấy mật khẩu.


15

Base64 là cách phù hợp với nhu cầu đơn giản của bạn. Không cần nhập bất cứ thứ gì:

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

2
Chính xác thì sao? Toàn bộ trả lời, hoặc phần không nhập khẩu?
tzot

18
Base64 chỉ thêm ảo tưởng về bảo mật.
FlySwat

13
Jonathan, có vẻ như bạn đã không đọc câu hỏi. Đó là về sự tối nghĩa (và rất tạm thời), không phải bảo mật , vì vậy tôi không hiểu tại sao bạn cho rằng câu trả lời của tôi không hữu ích.
tzot

1
Tôi không biết bạn có thể làm điều này thay vì phải sử dụng mô-đun base64. Và có rất nhiều bảng mã giống như zlib quá ... vui :)
Kiv

1
@Dennis Sử dụng mô-đun base64 là cách ưa thích hiện nay. Cái sau không hoạt động nữa trong các phiên bản mới hơn của Python.
Jeyekomon

5

cho python3 obfuscation sử dụng base64được thực hiện khác nhau:

import base64
base64.b64encode(b'PasswordStringAsStreamOfBytes')

kết quả là

b'UGFzc3dvcmRTdHJpbmdBc1N0cmVhbU9mQnl0ZXM='

lưu ý biểu diễn chuỗi không chính thức, chuỗi thực tế nằm trong dấu ngoặc kép

và giải mã trở lại chuỗi ban đầu

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

để sử dụng kết quả này trong đó các đối tượng chuỗi được yêu cầu, đối tượng byte có thể được dịch

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

để biết thêm thông tin về cách python3 xử lý byte (và chuỗi phù hợp), vui lòng xem tài liệu chính thức .


4

Đây là một vấn đề khá phổ biến. Thông thường, điều tốt nhất bạn có thể làm là

A) tạo một số loại chức năng mã hóa ceasar để mã hóa / giải mã (không phải là rot13) hoặc

B) phương pháp ưa thích là sử dụng khóa mã hóa, trong tầm với của chương trình của bạn, mã hóa / giải mã mật khẩu. Trong đó bạn có thể sử dụng bảo vệ tập tin để bảo vệ truy cập khóa.

Dọc theo những dòng đó nếu ứng dụng của bạn chạy dưới dạng dịch vụ / daemon (như máy chủ web), bạn có thể đặt khóa của mình vào kho khóa được bảo vệ bằng mật khẩu với mật khẩu nhập vào như một phần của dịch vụ khởi động. Sẽ cần một quản trị viên để khởi động lại ứng dụng của bạn, nhưng bạn sẽ thực sự giả vờ tốt về mật khẩu cấu hình của mình.


2

Hệ điều hành của bạn có thể cung cấp các phương tiện để mã hóa dữ liệu một cách an toàn. Chẳng hạn, trên Windows có DPAPI (API bảo vệ dữ liệu). Tại sao không hỏi người dùng về thông tin đăng nhập của họ trong lần đầu tiên bạn chạy sau đó thu thập chúng được mã hóa cho các lần chạy tiếp theo?


2

Thẩm định nhiều cây nhà hơn là chuyển đổi xác thực / mật khẩu / tên người dùng thành các chi tiết được mã hóa. FTPLIB chỉ là ví dụ. " pass.csv " là tên tệp csv

Lưu mật khẩu trong CSV như dưới đây:

tên tài khoản

mật khẩu người dùng

(Không có tiêu đề cột)

Đọc CSV và lưu nó vào danh sách.

Sử dụng danh sách elelments làm chi tiết xác thực.

Mã đầy đủ.

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

Đây là đoạn trích của tôi cho điều đó. Về cơ bản, bạn nhập hoặc sao chép hàm vào mã của bạn. getCredentials sẽ tạo tệp được mã hóa nếu nó không tồn tại và trả về một từ điển và updateCredential sẽ cập nhật.

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

Đặt thông tin cấu hình trong một tập tin cấu hình được mã hóa. Truy vấn thông tin này trong mã của bạn bằng cách sử dụng một khóa. Đặt khóa này vào một tệp riêng cho mỗi môi trường và không lưu trữ mã đó với mã của bạn.


1

Bạn có biết hố không?

https://pypi.python.org/pypi/pit (chỉ py2 (phiên bản 0.3))

https://github.com/yoshiori/pit (nó sẽ hoạt động trên py3 (phiên bản hiện tại 0.4))

kiểm tra

from pit import Pit

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

Chạy:

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

~ / .pit / default.yml:

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

3
Pit không có bất kỳ tài liệu nào
thành công

Như @successhawk đã lưu ý - Tôi không thấy BẤT K document tài liệu nào trong các liên kết github / pypi đó cho "pit" - nhưng mô tả ở trên rất rõ ràng - và nói chung tôi thích giải pháp này để "ẩn" thông tin khỏi chế độ xem dễ dàng ...
netdesignate

Tôi miễn cưỡng sử dụng một mô-đun không được bảo trì và tôi gặp lỗi khi tôi cố gắng sử dụng mô-đun theo hướng dẫn:/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

Nếu chạy trên Windows, bạn có thể cân nhắc sử dụng thư viện win32crypt. Nó cho phép lưu trữ và truy xuất dữ liệu được bảo vệ (khóa, mật khẩu) bởi người dùng đang chạy tập lệnh, do đó mật khẩu không bao giờ được lưu trữ ở dạng văn bản rõ ràng hoặc định dạng bị xáo trộn trong mã của bạn. Tôi không chắc chắn nếu có một triển khai tương đương cho các nền tảng khác, vì vậy với việc sử dụng nghiêm ngặt win32crypt, mã của bạn không thể mang theo được.

Tôi tin rằng mô-đun có thể được lấy ở đây: http://timgolden.me.uk/pywin32-docs/win32crypt.html


1

Một cách mà tôi đã làm điều này là như sau:

Tại vỏ trăn:

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

Sau đó, tạo một mô-đun với mã sau:

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

Khi bạn đã hoàn thành việc này, bạn có thể nhập trực tiếp mật khẩu hoặc bạn có thể nhập mã thông báo và mật mã để giải mã khi cần.

Rõ ràng, có một số thiếu sót đối với phương pháp này. Nếu ai đó có cả mã thông báo và khóa (như họ có nếu có tập lệnh), họ có thể giải mã dễ dàng. Tuy nhiên, nó làm xáo trộn và nếu bạn biên dịch mã (với một cái gì đó giống như Nuitka) thì ít nhất mật khẩu của bạn sẽ không xuất hiện dưới dạng văn bản thuần túy trong trình soạn thảo hex.


0

Điều này không trả lời chính xác câu hỏi của bạn, nhưng nó liên quan. Tôi sẽ thêm vào như một bình luận nhưng không được phép. Tôi đã xử lý vấn đề tương tự và chúng tôi đã quyết định tiết lộ tập lệnh cho người dùng sử dụng Jenkins. Điều này cho phép chúng tôi lưu trữ thông tin đăng nhập db trong một tệp riêng được mã hóa và bảo mật trên máy chủ và không thể truy cập đối với người không phải quản trị viên. Nó cũng cho phép chúng ta một chút phím tắt để tạo UI và thực thi điều tiết.


0

Bạn cũng có thể xem xét khả năng lưu trữ mật khẩu bên ngoài tập lệnh và cung cấp nó khi chạy

ví dụ: fred.py

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

có thể chạy như thế nào

$ PASSWORD=password123 python fred.py
fred password123

Có thể đạt được các lớp "bảo mật thông qua che khuất" bằng cách sử dụng base64(như được đề xuất ở trên), sử dụng các tên ít rõ ràng hơn trong mã và phân tách mật khẩu thực tế từ mã.

Nếu mã nằm trong kho lưu trữ, việc lưu trữ bí mật bên ngoài nó thường rất hữu ích , vì vậy người ta có thể thêm mã này vào~/.bashrc (hoặc vào kho tiền hoặc tập lệnh khởi chạy, ...)

export SURNAME=cGFzc3dvcmQxMjM=

và thay đổi fred.py thành

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

sau đó đăng nhập lại và

$ python fred.py
fred password123

0

Tại sao không có một xor đơn giản?

Ưu điểm:

  • trông giống như dữ liệu nhị phân
  • không ai có thể đọc nó mà không biết khóa (ngay cả khi đó là một char duy nhất)

Tôi đi đến điểm mà tôi nhận ra các chuỗi b64 đơn giản cho các từ phổ biến và rot13 là tốt. Xor sẽ làm cho nó khó hơn nhiều.



-1

Có một số tiện ích ROT13 được viết bằng Python trên 'Net - chỉ cần google cho chúng. ROT13 mã hóa chuỗi ngoại tuyến, sao chép nó vào nguồn, giải mã tại điểm truyền.

Nhưng đây thực sựsự bảo vệ yếu ...


vui lòng bao gồm một liên kết hoặc mã mẫu để làm cho câu trả lời này hữu ích hơn
Micah Stubbs
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.