我需要在Python中安全地存储用户名和密码,我有哪些选择?

问题:我需要在Python中安全地存储用户名和密码,我有哪些选择?

我正在编写一个小的Python脚本,该脚本将使用用户名和密码组合定期从第三方服务中获取信息。我不需要创建100%防弹的东西(甚至存在100%的东西吗?),但是我想采用一种很好的安全措施,因此至少有人要花很长时间才能破解它。

该脚本没有GUI,将由定期运行cron,因此每次运行解密内容时都不会真正输入密码,因此我必须将用户名和密码存储在加密文件或加密文件中在SQLite数据库中,这将是更好的选择,因为无论如何我都将使用SQLite,并且可能需要在某个时候编辑密码。另外,我可能会将整个程序包装在EXE中,因为这时它仅适用于Windows。

如何安全地存储通过cron作业定期使用的用户名和密码组合?

I’m writing a small Python script which will periodically pull information from a 3rd party service using a username and password combo. I don’t need to create something that is 100% bulletproof (does 100% even exist?), but I would like to involve a good measure of security so at the very least it would take a long time for someone to break it.

This script won’t have a GUI and will be run periodically by cron, so entering a password each time it’s run to decrypt things won’t really work, and I’ll have to store the username and password in either an encrypted file or encrypted in a SQLite database, which would be preferable as I’ll be using SQLite anyway, and I might need to edit the password at some point. In addition, I’ll probably be wrapping the whole program in an EXE, as it’s exclusively for Windows at this point.

How can I securely store the username and password combo to be used periodically via a cron job?


回答 0

我推荐一种类似于ssh-agent的策略。如果您不能直接使用ssh-agent,则可以实现类似的方法,以便密码仅保存在RAM中。cron作业可能已经配置了凭据,以便在每次运行时从代理获取实际密码,一次使用,然后立即使用该del语句取消引用。

管理员仍必须在启动时或在启动时输入密码以启动ssh-agent,但这是一个合理的折衷方案,可以避免将纯文本密码存储在磁盘上的任何位置。

I recommend a strategy similar to ssh-agent. If you can’t use ssh-agent directly you could implement something like it, so that your password is only kept in RAM. The cron job could have configured credentials to get the actual password from the agent each time it runs, use it once, and de-reference it immediately using the del statement.

The administrator still has to enter the password to start ssh-agent, at boot-time or whatever, but this is a reasonable compromise that avoids having a plain-text password stored anywhere on disk.


回答 1

Python钥匙圈库集成了CryptProtectData在Windows API(以及Mac和Linux相关的API)进行加密与用户的登录凭据数据。

简单用法:

import keyring

# the service is just a namespace for your app
service_id = 'IM_YOUR_APP!'

keyring.set_password(service_id, 'dustin', 'my secret password')
password = keyring.get_password(service_id, 'dustin') # retrieve password

要将用户名存储在密钥环上的用法:

import keyring

MAGIC_USERNAME_KEY = 'im_the_magic_username_key'

# the service is just a namespace for your app
service_id = 'IM_YOUR_APP!'  

username = 'dustin'

# save password
keyring.set_password(service_id, username, "password")

# optionally, abuse `set_password` to save username onto keyring
# we're just using some known magic string in the username field
keyring.set_password(service_id, MAGIC_USERNAME_KEY, username)

稍后从钥匙圈获取您的信息

# again, abusing `get_password` to get the username.
# after all, the keyring is just a key-value store
username = keyring.get_password(service_id, MAGIC_USERNAME_KEY)
password = keyring.get_password(service_id, username)  

使用用户的操作系统凭据对项目进行加密,因此,以您的用户帐户运行的其他应用程序将能够访问密码。

为了稍微掩盖该漏洞,您可以在将密码存储在密钥环上之前以某种方式对密码进行加密/混淆。当然,任何以您的脚本为目标的人都只能查看源代码,并弄清楚如何对密码进行解密/取消混淆,但是您至少要防止某些应用程序清除库中的所有密码并获取您的密码。 。

The python keyring library integrates with the CryptProtectData API on Windows (along with relevant API’s on Mac and Linux) which encrypts data with the user’s logon credentials.

Simple usage:

import keyring

# the service is just a namespace for your app
service_id = 'IM_YOUR_APP!'

keyring.set_password(service_id, 'dustin', 'my secret password')
password = keyring.get_password(service_id, 'dustin') # retrieve password

Usage if you want to store the username on the keyring:

import keyring

MAGIC_USERNAME_KEY = 'im_the_magic_username_key'

# the service is just a namespace for your app
service_id = 'IM_YOUR_APP!'  

username = 'dustin'

# save password
keyring.set_password(service_id, username, "password")

# optionally, abuse `set_password` to save username onto keyring
# we're just using some known magic string in the username field
keyring.set_password(service_id, MAGIC_USERNAME_KEY, username)

Later to get your info from the keyring

# again, abusing `get_password` to get the username.
# after all, the keyring is just a key-value store
username = keyring.get_password(service_id, MAGIC_USERNAME_KEY)
password = keyring.get_password(service_id, username)  

Items are encrypted with the user’s operating system credentials, thus other applications running in your user account would be able to access the password.

To obscure that vulnerability a bit you could encrypt/obfuscate the password in some manner before storing it on the keyring. Of course, anyone who was targeting your script would just be able to look at the source and figure out how to unencrypt/unobfuscate the password, but you’d at least prevent some application vacuuming up all passwords in the vault and getting yours as well.


回答 2

在查看了有关此问题和相关问题的答案之后,我使用一些建议的加密和隐藏秘密数据的方法整理了一些代码。此代码专门用于脚本必须在没有用户干预的情况下运行的情况(如果用户手动启动该脚本,则最好将其放入密码中,并仅将其保存在内存中,以解决此问题)。这种方法不是超级安全的。从根本上讲,脚本可以访问机密信息,因此具有完全系统访问权限的任何人都可以使用脚本及其关联文件,并且可以访问它们。id的作用是使偶然检查的数据变得模糊不清,并且如果对数据文件进行单独检查或一起检查而不使用脚本,则数据文件本身将保持安全。

我这样做的动机是通过一个项目对我的一些银行帐户进行轮询以监视交易-我需要它在后台运行,而不必每隔一两分钟重新输入一次密码。

只需将此代码粘贴到脚本的顶部,更改saltSeed,然后根据需要在代码中使用store(),retrieve()和require():

from getpass import getpass
from pbkdf2 import PBKDF2
from Crypto.Cipher import AES
import os
import base64
import pickle


### Settings ###

saltSeed = 'mkhgts465wef4fwtdd' # MAKE THIS YOUR OWN RANDOM STRING

PASSPHRASE_FILE = './secret.p'
SECRETSDB_FILE = './secrets'
PASSPHRASE_SIZE = 64 # 512-bit passphrase
KEY_SIZE = 32 # 256-bit key
BLOCK_SIZE = 16  # 16-bit blocks
IV_SIZE = 16 # 128-bits to initialise
SALT_SIZE = 8 # 64-bits of salt


### System Functions ###

def getSaltForKey(key):
    return PBKDF2(key, saltSeed).read(SALT_SIZE) # Salt is generated as the hash of the key with it's own salt acting like a seed value

def encrypt(plaintext, salt):
    ''' Pad plaintext, then encrypt it with a new, randomly initialised cipher. Will not preserve trailing whitespace in plaintext!'''

    # Initialise Cipher Randomly
    initVector = os.urandom(IV_SIZE)

    # Prepare cipher key:
    key = PBKDF2(passphrase, salt).read(KEY_SIZE)

    cipher = AES.new(key, AES.MODE_CBC, initVector) # Create cipher

    return initVector + cipher.encrypt(plaintext + ' '*(BLOCK_SIZE - (len(plaintext) % BLOCK_SIZE))) # Pad and encrypt

def decrypt(ciphertext, salt):
    ''' Reconstruct the cipher object and decrypt. Will not preserve trailing whitespace in the retrieved value!'''

    # Prepare cipher key:
    key = PBKDF2(passphrase, salt).read(KEY_SIZE)

    # Extract IV:
    initVector = ciphertext[:IV_SIZE]
    ciphertext = ciphertext[IV_SIZE:]

    cipher = AES.new(key, AES.MODE_CBC, initVector) # Reconstruct cipher (IV isn't needed for edecryption so is set to zeros)

    return cipher.decrypt(ciphertext).rstrip(' ') # Decrypt and depad


### User Functions ###

def store(key, value):
    ''' Sore key-value pair safely and save to disk.'''
    global db

    db[key] = encrypt(value, getSaltForKey(key))
    with open(SECRETSDB_FILE, 'w') as f:
        pickle.dump(db, f)

def retrieve(key):
    ''' Fetch key-value pair.'''
    return decrypt(db[key], getSaltForKey(key))

def require(key):
    ''' Test if key is stored, if not, prompt the user for it while hiding their input from shoulder-surfers.'''
    if not key in db: store(key, getpass('Please enter a value for "%s":' % key))


### Setup ###

# Aquire passphrase:
try:
    with open(PASSPHRASE_FILE) as f:
        passphrase = f.read()
    if len(passphrase) == 0: raise IOError
except IOError:
    with open(PASSPHRASE_FILE, 'w') as f:
        passphrase = os.urandom(PASSPHRASE_SIZE) # Random passphrase
        f.write(base64.b64encode(passphrase))

        try: os.remove(SECRETSDB_FILE) # If the passphrase has to be regenerated, then the old secrets file is irretrievable and should be removed
        except: pass
else:
    passphrase = base64.b64decode(passphrase) # Decode if loaded from already extant file

# Load or create secrets database:
try:
    with open(SECRETSDB_FILE) as f:
        db = pickle.load(f)
    if db == {}: raise IOError
except (IOError, EOFError):
    db = {}
    with open(SECRETSDB_FILE, 'w') as f:
        pickle.dump(db, f)

### Test (put your code here) ###
require('id')
require('password1')
require('password2')
print
print 'Stored Data:'
for key in db:
    print key, retrieve(key) # decode values on demand to avoid exposing the whole database in memory
    # DO STUFF

如果在秘密文件上设置os权限以仅允许脚本本身读取它们,并且脚本本身被编译并标记为仅可执行文件(不可读),则该方法的安全性将得到显着提高。其中一些可以自动化,但是我没有打扰。可能需要为该脚本设置一个用户,然后以该用户身份运行该脚本(并将脚本文件的所有权设置给该用户)。

我喜欢任何人都能想到的任何建议,批评或其他弱点。我对编写加密代码非常陌生,因此我所做的工作几乎可以肯定会得到改善。

After looking though the answers to this and related questions, I’ve put together some code using a few of the suggested methods for encrypting and obscuring secret data. This code is specifically for when the script has to run without user intervention (if the user starts it manually, it’s best to have them put in the password and only keep it in memory as the answer to this question suggests). This method isn’t super-secure; fundamentally, the script can access the secret info so anyone who has full system access has the script and its associated files and can access them. What this does do id obscures the data from casual inspection and leaves the data files themselves secure if they are examined individually, or together without the script.

My motivation for this is a project that polls some of my bank accounts to monitor transactions – I need it to run in the background without me re-entering passwords every minute or two.

Just paste this code at the top of your script, change the saltSeed and then use store() retrieve() and require() in your code as needed:

from getpass import getpass
from pbkdf2 import PBKDF2
from Crypto.Cipher import AES
import os
import base64
import pickle


### Settings ###

saltSeed = 'mkhgts465wef4fwtdd' # MAKE THIS YOUR OWN RANDOM STRING

PASSPHRASE_FILE = './secret.p'
SECRETSDB_FILE = './secrets'
PASSPHRASE_SIZE = 64 # 512-bit passphrase
KEY_SIZE = 32 # 256-bit key
BLOCK_SIZE = 16  # 16-bit blocks
IV_SIZE = 16 # 128-bits to initialise
SALT_SIZE = 8 # 64-bits of salt


### System Functions ###

def getSaltForKey(key):
    return PBKDF2(key, saltSeed).read(SALT_SIZE) # Salt is generated as the hash of the key with it's own salt acting like a seed value

def encrypt(plaintext, salt):
    ''' Pad plaintext, then encrypt it with a new, randomly initialised cipher. Will not preserve trailing whitespace in plaintext!'''

    # Initialise Cipher Randomly
    initVector = os.urandom(IV_SIZE)

    # Prepare cipher key:
    key = PBKDF2(passphrase, salt).read(KEY_SIZE)

    cipher = AES.new(key, AES.MODE_CBC, initVector) # Create cipher

    return initVector + cipher.encrypt(plaintext + ' '*(BLOCK_SIZE - (len(plaintext) % BLOCK_SIZE))) # Pad and encrypt

def decrypt(ciphertext, salt):
    ''' Reconstruct the cipher object and decrypt. Will not preserve trailing whitespace in the retrieved value!'''

    # Prepare cipher key:
    key = PBKDF2(passphrase, salt).read(KEY_SIZE)

    # Extract IV:
    initVector = ciphertext[:IV_SIZE]
    ciphertext = ciphertext[IV_SIZE:]

    cipher = AES.new(key, AES.MODE_CBC, initVector) # Reconstruct cipher (IV isn't needed for edecryption so is set to zeros)

    return cipher.decrypt(ciphertext).rstrip(' ') # Decrypt and depad


### User Functions ###

def store(key, value):
    ''' Sore key-value pair safely and save to disk.'''
    global db

    db[key] = encrypt(value, getSaltForKey(key))
    with open(SECRETSDB_FILE, 'w') as f:
        pickle.dump(db, f)

def retrieve(key):
    ''' Fetch key-value pair.'''
    return decrypt(db[key], getSaltForKey(key))

def require(key):
    ''' Test if key is stored, if not, prompt the user for it while hiding their input from shoulder-surfers.'''
    if not key in db: store(key, getpass('Please enter a value for "%s":' % key))


### Setup ###

# Aquire passphrase:
try:
    with open(PASSPHRASE_FILE) as f:
        passphrase = f.read()
    if len(passphrase) == 0: raise IOError
except IOError:
    with open(PASSPHRASE_FILE, 'w') as f:
        passphrase = os.urandom(PASSPHRASE_SIZE) # Random passphrase
        f.write(base64.b64encode(passphrase))

        try: os.remove(SECRETSDB_FILE) # If the passphrase has to be regenerated, then the old secrets file is irretrievable and should be removed
        except: pass
else:
    passphrase = base64.b64decode(passphrase) # Decode if loaded from already extant file

# Load or create secrets database:
try:
    with open(SECRETSDB_FILE) as f:
        db = pickle.load(f)
    if db == {}: raise IOError
except (IOError, EOFError):
    db = {}
    with open(SECRETSDB_FILE, 'w') as f:
        pickle.dump(db, f)

### Test (put your code here) ###
require('id')
require('password1')
require('password2')
print
print 'Stored Data:'
for key in db:
    print key, retrieve(key) # decode values on demand to avoid exposing the whole database in memory
    # DO STUFF

The security of this method would be significantly improved if os permissions were set on the secret files to only allow the script itself to read them, and if the script itself was compiled and marked as executable only (not readable). Some of that could be automated, but I haven’t bothered. It would probably require setting up a user for the script and running the script as that user (and setting ownership of the script’s files to that user).

I’d love any suggestions, criticisms or other points of vulnerability that anyone can think of. I’m pretty new to writing crypto code so what I’ve done could almost certainly be improved.


回答 3

有一些选项可以存储Python程序需要使用的密码和其他机密信息,特别是需要在后台运行的程序,它不能仅仅要求用户输入密码。

应避免的问题:

  1. 将密码签入源代码管理,其他开发人员甚至公众都可以在其中看到它。
  2. 同一服务器上的其他用户从配置文件或源代码中读取密码。
  3. 将密码保存在源文件中,其他人在编辑时可以在您的肩膀上看到它。

选项1:SSH

这并不总是一个选择,但可能是最好的选择。您的私钥永远不会通过网络传输,SSH只是运行数学计算以证明您拥有正确的密钥。

为了使其工作,您需要以下内容:

  • 需要通过SSH访问数据库或您正在访问的任何内容。尝试搜索“ SSH”以及您正在访问的任何服务。例如,“ ssh postgresql”。如果这不是您数据库的功能,请转到下一个选项。
  • 创建一个帐户来运行将调用数据库的服务,并生成一个SSH密钥
  • 将公钥添加到要调用的服务中,或者在该服务器上创建本地帐户,然后在此处安装公钥。

选项2:环境变量

这是最简单的一个,因此它可能是一个不错的起点。十二因子应用程序对此进行了很好的描述。基本思想是,您的源代码只是从环境变量中提取密码或其他机密,然后在运行程序的每个系统上配置这些环境变量。如果您使用适用于大多数开发人员的默认值,则可能也很不错。您必须权衡这与使软件“默认情况下安全”有关。

这是一个从环境变量中提取服务器,用户名和密码的示例。

import os

server = os.getenv('MY_APP_DB_SERVER', 'localhost')
user = os.getenv('MY_APP_DB_USER', 'myapp')
password = os.getenv('MY_APP_DB_PASSWORD', '')

db_connect(server, user, password)

查找如何在操作系统中设置环境变量,并考虑以其自己的帐户运行服务。这样,当您使用自己的帐户运行程序时,环境变量中不会包含敏感数据。设置这些环境变量时,请格外小心,以免其他用户无法读取它们。例如,检查文件权限。当然,具有root权限的任何用户都可以阅读它们,但这无济于事。

选项3:配置文件

这与环境变量非常相似,但是您从文本文件中读取机密。我仍然发现环境变量在部署工具和持续集成服务器等方面更灵活。如果决定使用配置文件,Python会在标准库中支持几种格式,例如JSONINInetrcXML。您还可以找到外部软件包,例如PyYAMLTOML。就个人而言,我发现JSON和YAML最容易使用,并且YAML允许注释。

配置文件要考虑的三件事:

  1. 文件在哪里?也许是默认位置(如~/.my_app)和命令行选项以使用其他位置。
  2. 确保其他用户无法读取该文件。
  3. 显然,不要将配置文件提交给源代码。您可能想要提交一个模板,用户可以将其复制到其主目录。

选项4:Python模块

一些项目只是将其秘密直接放入Python模块中。

# settings.py
db_server = 'dbhost1'
db_user = 'my_app'
db_password = 'correcthorsebatterystaple'

然后导入该模块以获取值。

# my_app.py
from settings import db_server, db_user, db_password

db_connect(db_server, db_user, db_password)

使用此技术的一个项目是Django。显然,settings.py尽管您可能想要提交一个名为settings_template.py用户可以复制和修改的文件,但您不应提交到源代码管理。

我发现此技术存在一些问题:

  1. 开发人员可能会不小心将文件提交给源代码管理。添加它可以.gitignore降低这种风险。
  2. 您的某些代码不受源代码控制。如果您训练有素,只在这里输入字符串和数字,那将不是问题。如果您在此处开始编写日志记录过滤器类,请停止!

如果您的项目已使用此技术,则很容易过渡到环境变量。只需将所有设置值移至环境变量,然后更改Python模块以从这些环境变量中读取。

There are a few options for storing passwords and other secrets that a Python program needs to use, particularly a program that needs to run in the background where it can’t just ask the user to type in the password.

Problems to avoid:

  1. Checking the password in to source control where other developers or even the public can see it.
  2. Other users on the same server reading the password from a configuration file or source code.
  3. Having the password in a source file where others can see it over your shoulder while you are editing it.

Option 1: SSH

This isn’t always an option, but it’s probably the best. Your private key is never transmitted over the network, SSH just runs mathematical calculations to prove that you have the right key.

In order to make it work, you need the following:

  • The database or whatever you are accessing needs to be accessible by SSH. Try searching for “SSH” plus whatever service you are accessing. For example, “ssh postgresql”. If this isn’t a feature on your database, move on to the next option.
  • Create an account to run the service that will make calls to the database, and generate an SSH key.
  • Either add the public key to the service you’re going to call, or create a local account on that server, and install the public key there.

Option 2: Environment Variables

This one is the simplest, so it might be a good place to start. It’s described well in the Twelve Factor App. The basic idea is that your source code just pulls the password or other secrets from environment variables, and then you configure those environment variables on each system where you run the program. It might also be a nice touch if you use default values that will work for most developers. You have to balance that against making your software “secure by default”.

Here’s an example that pulls the server, user name, and password from environment variables.

import os

server = os.getenv('MY_APP_DB_SERVER', 'localhost')
user = os.getenv('MY_APP_DB_USER', 'myapp')
password = os.getenv('MY_APP_DB_PASSWORD', '')

db_connect(server, user, password)

Look up how to set environment variables in your operating system, and consider running the service under its own account. That way you don’t have sensitive data in environment variables when you run programs in your own account. When you do set up those environment variables, take extra care that other users can’t read them. Check file permissions, for example. Of course any users with root permission will be able to read them, but that can’t be helped. If you’re using systemd, look at the service unit, and be careful to use EnvironmentFile instead of Environment for any secrets. Environment values can be viewed by any user with systemctl show.

Option 3: Configuration Files

This is very similar to the environment variables, but you read the secrets from a text file. I still find the environment variables more flexible for things like deployment tools and continuous integration servers. If you decide to use a configuration file, Python supports several formats in the standard library, like JSON, INI, netrc, and XML. You can also find external packages like PyYAML and TOML. Personally, I find JSON and YAML the simplest to use, and YAML allows comments.

Three things to consider with configuration files:

  1. Where is the file? Maybe a default location like ~/.my_app, and a command-line option to use a different location.
  2. Make sure other users can’t read the file.
  3. Obviously, don’t commit the configuration file to source code. You might want to commit a template that users can copy to their home directory.

Option 4: Python Module

Some projects just put their secrets right into a Python module.

# settings.py
db_server = 'dbhost1'
db_user = 'my_app'
db_password = 'correcthorsebatterystaple'

Then import that module to get the values.

# my_app.py
from settings import db_server, db_user, db_password

db_connect(db_server, db_user, db_password)

One project that uses this technique is Django. Obviously, you shouldn’t commit settings.py to source control, although you might want to commit a file called settings_template.py that users can copy and modify.

I see a few problems with this technique:

  1. Developers might accidentally commit the file to source control. Adding it to .gitignore reduces that risk.
  2. Some of your code is not under source control. If you’re disciplined and only put strings and numbers in here, that won’t be a problem. If you start writing logging filter classes in here, stop!

If your project already uses this technique, it’s easy to transition to environment variables. Just move all the setting values to environment variables, and change the Python module to read from those environment variables.


回答 4

尝试加密密码没有多大意义:您要隐藏密码的人拥有Python脚本,该脚本将具有解密密码。最快的获取密码的方法是在将Python脚本与第三方服务一起使用密码之前,将打印语句添加到Python脚本中。

因此,将密码作为字符串存储在脚本中,并对base64进行编码,这样仅读取文件就不够,然后每天调用它。

There’s not much point trying to encrypt the password: the person you’re trying to hide it from has the Python script, which will have the code to decrypt it. The fastest way to get the password will be to add a print statement to the Python script just before it uses the password with the third-party service.

So store the password as a string in the script, and base64 encode it so that just reading the file isn’t enough, then call it a day.


回答 5

我认为您所能做的最好就是保护脚本文件及其正在运行的系统。

基本上执行以下操作:

  • 使用文件系统权限(chmod 400)
  • 系统上所有者帐户的强密码
  • 降低系统受到破坏的能力(防火墙,禁用不需要的服务等)
  • 删除不需要的管理员/ root / sudo特权

I think the best you can do is protect the script file and system it’s running on.

Basically do the following:

  • Use file system permissions (chmod 400)
  • Strong password for owner’s account on the system
  • Reduce ability for system to be compromised (firewall, disable unneeded services, etc)
  • Remove administrative/root/sudo privileges for those that do not need it

回答 6

操作系统通常支持为用户保护数据。对于Windows,它看起来像是http://msdn.microsoft.com/en-us/library/aa380261.aspx

您可以使用http://vermeulen.ca/python-win32api.html从python调用win32 api

据我了解,这将存储数据,以便只能从用于存储它的帐户访问它。如果要编辑数据,可以通过编写代码来提取,更改和保存值来进行。

operating systems often have support for securing data for the user. in the case of windows it looks like it’s http://msdn.microsoft.com/en-us/library/aa380261.aspx

you can call win32 apis from python using http://vermeulen.ca/python-win32api.html

as far as i understand, this will store the data so that it can be accessed only from the account used to store it. if you want to edit the data you can do so by writing code to extract, change and save the value.


回答 7

我使用密码术是因为我在系统上安装(编译)其他常用库时遇到了麻烦。(Win7 x64,Python 3.5)

from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher_suite = Fernet(key)
cipher_text = cipher_suite.encrypt(b"password = scarybunny")
plain_text = cipher_suite.decrypt(cipher_text)

我的脚本在物理安全的系统/房间中运行。我使用“加密脚本”将凭据加密到配置文件中。然后在需要使用它们时解密。实际系统中没有“加密脚本”,只有加密的配置文件存在。分析代码的人可以通过分析代码轻松地破坏加密,但是如果需要,您仍然可以将其编译为EXE。

I used Cryptography because I had troubles installing (compiling) other commonly mentioned libraries on my system. (Win7 x64, Python 3.5)

from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher_suite = Fernet(key)
cipher_text = cipher_suite.encrypt(b"password = scarybunny")
plain_text = cipher_suite.decrypt(cipher_text)

My script is running in a physically secure system/room. I encrypt credentials with an “encrypter script” to a config file. And then decrypt when I need to use them. “Encrypter script” is not on the real system, only encrypted config file is. Someone who analyses the code can easily break the encryption by analysing the code, but you can still compile it into an EXE if necessary.