在python脚本中隐藏密码(仅用于不安全的混淆)


127

我有一个python脚本正在创建ODBC连接。ODBC连接是使用连接字符串生成的。在此连接字符串中,我必须包含此连接的用户名和密码。

有没有一种简便的方法来隐藏文件中的此密码(只是在我编辑文件时没人能读取该密码)?


16
请记住,运行此文件的用户将至少具有对该文件的读取访问权限,并且可以轻松获取密码。如果您只能读取Thin,并且您担心别人会看到您的想法,但是要警告您,因为普通观察者无法足够快地记住事情以获取密码,任何有权使用该脚本和密码的人一小部分的技术知识和少量的野心就可以获取您的密码。务必仔细考虑安全性,这一点很重要。
Youarefunny 2011年

Answers:


117

Base64编码在标准库中,并且可以阻止肩膀冲浪者:

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

5
我同意。base64编码的密码看起来更加神秘。
Ed Haber

4
但是,运行脚本的用户必须可以读取脚本,而密码不能读取该脚本,这无济于事。
马丁·贝克特

79
我认为这base64rot13在这种情况下更好。相反,base64它具有其典型特征(等号,...),因此比其他方法更易于检测。但是,任何混淆都没有实际好处。这个答案受到如此高度评价真的很不好。它只是给人一种错误的安全感……
schlamar 2012年

12
如果您正在记录密码以便脚本可以使用它,那么无论您使用哪种加密方法,有权访问脚本的任何人都将能够获取密码。此处的要求只是在打开脚本时向只看脚本的人隐藏密码。在这种情况下base64,最好rot13使用Python标准库中的方法。
戴夫·韦伯

17
base64不是加密的。充其量是迷惑。
csgeek

52

当您需要为远程登录指定密码时,Douglas F Shearer's是Unix中公认的解决方案。
您添加--password-from-file选项来指定路径并从文件中读取纯文本。
然后,该文件可以位于受操作系统保护的用户自己的区域中。它还允许不同的用户自动选择自己的文件。

对于不允许脚本用户知道的密码-您可以使用高级权限运行脚本,并让该root / admin用户拥有密码文件。


1
您如何在没有提供root或admin密码的情况下以提升的权限运行脚本?与设置的UID位有关吗?
Youarefunny 2011年

4
没关系,我想通了。对于其他关心的人:如果脚本中设置了setuid位,则OS会将setuid位“传递”给解释器。不幸的是,存在巨大的安全漏洞,因此大多数现代发行版都关闭了脚本的setuid。
Youarefunny 2011年

我在--password-from-file选项上找不到任何信息。你有什么例子吗?谢谢!
pyramidface

@pyramidface-我的意思是您将编码这样的功能并添加从文件中读取密码的功能
Martin Beckett

@MartinBeckett但就像Youarefunny所说的那样,您必须在python上引发setuid才能使脚本对密码文件具有根访问权限?
pyramidface

51

这是一个简单的方法:

  1. 创建一个python模块-我们称之为peekaboo.py。
  2. 在peekaboo.py中,同时包含密码和需要该密码的任何代码
  3. 通过导入此模块(通过python命令行等)创建一个编译版本-peekaboo.pyc。
  4. 现在,删除peekaboo.py。
  5. 现在,您可以仅依靠peekaboo.pyc来愉快地导入peekaboo。由于peekaboo.pyc是字节编译的,因此临时用户无法读取。

尽管它容易受到py_to_pyc反编译器的攻击,但它应该比base64解码更加安全。


2
这仍然有一些缺点,但实际上与我想要的非常接近。它将允许我演示包含用户/密码连接的python脚本,而无需在屏幕上显示密码,也不必在命令提示符下键入密码。导入peekaboo后import peekabo,密码可用peekaboo.password(如果包含peekaboo.py password='secret'
Dannid 2014年

8
如果您想更进一步,可以使用Cython将任何.py文件编译为C并生成特定于平台的二进制文件(例如:.pyd用于Windows,.so用于macOS等)...通过cythonizing脚本并共享生成的二进制文件,您将获得此答案的好处+添加另一层混淆,因为现在您已经可以反编译C代码以获取密码了。这不是100%安全的,但是要获取要隐藏的敏感数据会花费很多精力。
Fnord

cythonizing,完善
Arno

29

如果您在Unix系统上工作,请利用标准Python库中的netrc模块。它从单独的文本文件(.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

假设用户无法在运行时提供用户名和密码,最好的解决方案可能是单独的源文件,其中仅包含导入到您的主代码中的用户名和密码的变量初始化。仅在凭据更改时才需要编辑此文件。否则,如果您只担心具有平均记忆的冲浪者,那么base 64编码可能是最简单的解决方案。ROT13太容易手动解码,不区分大小写,并且在加密状态下保留了太多含义。在python脚本之外对您的密码和用户ID进行编码。让他在运行时对脚本进行解码以供使用。

为自动化任务提供脚本凭证始终是一个冒险的建议。您的脚本应具有其自己的凭据,并且所使用的帐户应完全不需要访问权限。至少密码应该是长且相当随机的。


很好的答案-谢谢。对于我正在编写的小脚本(无论如何都是维护脚本
-BASE64

2
这听起来不错,但是您可以举一个示例实现吗?现在,这只是对一般做法的描述,对以前从未做过的人没有用。
Dannid 2014年

18

如何从脚本外部的文件中导入用户名和密码?这样,即使有人掌握了该脚本,他们也不会自动获得密码。


15

base64是满足您简单需求的方法。无需导入任何内容:

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

2
到底是什么傻?整个答复还是不重要的部分?
tzot

18
Base64仅增加了安全性的错觉。
FlySwat

13
乔纳森(Jonathan),看来您还没有阅读问题。这是关于晦涩(也是非常暂时的),而不是安全性,因此我不明白您为什么认为我的回答无济于事。
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

这是一个很常见的问题。通常,您能做的最好的就是

A)创建某种ceasar密码函数来进行编码/解码(但不是rot13)或

B)首选方法是在程序可及的范围内使用加密密钥对密码进行编码/解码。您可以在其中使用文件保护来保护访问密钥。

如果您的应用程序作为服务/守护程序(例如Web服务器)运行,则可以将密钥放入密码保护的密钥库中,并在服务启动过程中输入密码。管理员需要重新启动您的应用程序,但是您对配置密码的保护非常好。


2

您的操作系统可能提供了用于安全加密数据的工具。例如,在Windows上有DPAPI(数据保护API)。为什么不在第一次运行时要求用户提供其凭据,然后将其松散加密以进行后续运行?


2

更多本地化的方式,而不是将身份验证/密码/用户名转换为加密的详细信息。FTPLIB只是示例。“ pass.csv ”是csv文件名

将密码保存为CSV格式,如下所示:

用户名

用户密码

(无列标题)

读取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
坑没有任何文件
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


1

我执行此操作的方法如下:

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

然后,使用以下代码创建一个模块:

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之类的代码),则至少您的密码不会在十六进制编辑器中显示为纯文本。


0

这并不能完全回答您的问题,但却是相关的。我本来想添加评论,但不允许。我一直在处理同一问题,因此我们决定使用Jenkins将脚本公开给用户。这使我们可以将数据库凭据存储在单独的文件中,该文件在服务器上已加密并受保护,并且非管理员无法访问。它还为我们提供了一些创建UI和限制执行的捷径。


0

您还可以考虑将密码存储在脚本外部并在运行时提供密码的可能性

例如fred.py

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

可以像

$ PASSWORD=password123 python fred.py
fred password123

可以通过使用base64(如上所述),在代码中使用不太明显的名称以及使实际密码与代码之间的距离进一步达到“通过模糊性实现安全性” 的目的。

如果代码位于存储库中,通常将机密存储在存储库之外很有用,因此可以将其添加到~/.bashrc(或添加到Vault或启动脚本中,...)

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

为什么不拥有简单的异或?

优点:

  • 看起来像二进制数据
  • 任何人都无法在不知道键的情况下读取它(即使它是一个字符)

我到了可以识别普通单词和rot13的简单b64字符串的地步。Xor会让它变得更加困难。



-1

在网上有几种用Python编写的ROT13实用程序-只是谷歌搜索它们。ROT13离线编码字符串,将其复制到源中,然后在传输点解码。

但这确实是薄弱的保护...


请提供链接或示例代码以使该答案更有用
Micah Stubbs
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.