在 python 脚本中隐藏密码(仅用于不安全的模糊处理)

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

有没有一种简单的方法可以在文件中隐藏这个密码(只是当我编辑文件时没有人可以读取密码) ?

288722 次浏览

在‘ Net’上有几个用 Python 编写的 ROT13实用程序——只要用 google 搜索就可以了。ROT13离线编码字符串,复制到源码,在传输点解码。但这是 真的弱保护..。

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

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

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

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

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

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

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

为自动化任务提供脚本凭证始终是一个有风险的建议。您的脚本应该有自己的凭据,并且它所使用的帐户除了必要的访问权限之外不应该有任何访问权限。至少密码应该很长,而且是随机的。

这是一个非常普遍的问题,通常你能做的最好的事情就是

A)创建某种类型的停止密码函数来编码/解码(只是不是 rot13) 或者

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

如果您的应用程序作为服务/守护进程(如 webserver)运行,那么您可以将密钥放入密码保护的密钥存储库,并将密码输入作为服务启动的一部分。需要一个管理员来重启你的应用程序,但是你可以很好的保护你的配置密码。

Base64是满足您简单需求的方法,不需要导入任何东西:

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

将配置信息放在加密的配置文件中。使用键在代码中查询此信息。将此密钥放在每个环境的单独文件中,不要将其与代码一起存储。

如果您正在使用 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

比起将身份验证/密码/用户名转换为加密的详细信息,更多的是自主开发的方法。 FTPLIB就是一个例子。 “ Pass.csv”是 csv 文件名

在 CSV 中保存密码如下:

User _ name

User _ password

(没有栏目标题)

读取 CSV 并将其保存到列表中。

使用 List 元素作为身份验证详细信息。

全密码。

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

这里有一个简单的方法:

  1. 创建一个 python 模块——我们称之为 peekaboo.py。
  2. 在 peekaboo.py 中,包含密码和需要该密码的任何代码
  3. 通过导入这个模块(通过 python 命令行等)创建一个编译后的版本-peekaboo.pyc。
  4. 现在,删除 Peekaboo.py。
  5. 现在,您可以只依靠 peekaboo.pyc 导入 peekaboo。由于 peekaboo.pyc 是字节编译的,普通用户无法阅读。

这应该比 base64解码稍微安全一些——尽管它容易受到 py _ to _ pyc 反编译器的攻击。

你知道 Pit 吗?

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

如果在 Windows 上运行,可以考虑使用 win32crypt 库。它允许运行脚本的用户存储和检索受保护的数据(密钥,密码) ,因此密码永远不会以明文或模糊的格式存储在代码中。我不确定是否有其他平台的同等实现,所以严格使用 win32crypt,您的代码是不可移植的。

我相信模块可以在这里获得: http://timgolden.me.uk/pywin32-docs/win32crypt.html

这并没有准确地回答你的问题,但它是相关的。我本来想加个评论,但是被拒绝了。 我一直在处理同样的问题,我们决定将脚本公开给使用 Jenkins 的用户。这允许我们将 db 凭据存储在一个单独的文件中,该文件在服务器上加密和保护,非管理员无法访问。 它还为我们创建 UI 和限制执行提供了一些快捷方式。

对于使用 base64蟒蛇3模糊处理方法不同:

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如何处理字节(以及相应的字符串)的更多信息,请参见 正式文件

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

例如 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

下面是我对这种事情的一些片段。基本上就是将函数导入或复制到代码中。如果加密文件不存在,getCredential 将创建该文件并返回一个字典,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()

为什么不用一个简单的 xor 呢?

优点:

  • 看起来像二进制数据
  • 没有人可以在不知道密钥的情况下读取它(即使它只是一个字符)

到了这里,我认出了普通单词和 rot13的简单 b64字符串。Xor 会让事情变得更难。

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

我做到这一点的方法如下:

在蟒蛇的外壳上:

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