Naf*_*Kay 72 python security encryption
我正在编写一个小的Python脚本,它将使用用户名和密码组合定期从第三方服务中提取信息.我不需要创建100%防弹的东西(100%甚至存在吗?),但我想要涉及一个很好的安全措施,所以至少需要很长时间才能打破它.
这个脚本没有GUI,并且会定期运行cron,因此每次运行密码时输入密码都不会真正起作用,我必须将用户名和密码存储在加密文件中或加密在SQLite数据库中,这将是更好的,因为我将使用SQLite,我可能需要在某些时候编辑密码.另外,我可能会将整个程序包装在EXE中,因为此时它专门用于Windows.
如何安全地存储用户名和密码组合,以便通过cron作业定期使用?
Dus*_*att 34
该蟒蛇钥匙圈库与整合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
Run Code Online (Sandbox Code Playgroud)
用法如果要将用户名存储在密钥环上:
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)
Run Code Online (Sandbox Code Playgroud)
稍后从钥匙圈获取您的信息
# 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)
Run Code Online (Sandbox Code Playgroud)
项目使用用户的操作系统凭据进行加密,因此在您的用户帐户中运行的其他应用程序将能够访问密码.
为了模糊该漏洞,您可以在将密码存储到密钥环之前以某种方式加密/混淆密码.当然,任何针对您的脚本的人都可以查看源代码并找出如何解密/取消混淆密码,但是您至少会阻止某些应用程序清空Vault中的所有密码并获取您的密码. .
dro*_*ers 24
在查看了这个问题和相关问题的答案后,我使用一些建议的加密和模糊秘密数据的方法汇总了一些代码.此代码专门用于脚本必须在没有用户干预的情况下运行的情况(如果用户手动启动它,最好将它们放入密码并将其保留在内存中,因为这个问题的答案建议).这种方法不是超级安全的; 从根本上说,脚本可以访问秘密信息,因此任何拥有完整系统访问权限的人都可以访问脚本及其相关文件.这样做会使得来自临时检查的数据变得模糊,如果单独检查数据文件或者没有脚本一起检查数据文件本身就是安全的.
我的动机是一个项目,它调查我的一些银行账户来监控交易 - 我需要它在后台运行,而不是每隔一两分钟重新输入密码.
只需将此代码粘贴到脚本顶部,更改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
Run Code Online (Sandbox Code Playgroud)
如果对机密文件设置了os权限,只允许脚本本身读取它们,并且脚本本身被编译并标记为仅可执行(不可读),则此方法的安全性将得到显着提高.其中一些可以自动化,但我没有打扰.可能需要为脚本设置用户并以该用户身份运行脚本(并将脚本文件的所有权设置为该用户).
我喜欢任何人都能想到的任何建议,批评或其他漏洞.我是编写加密代码的新手,所以我所做的几乎肯定会得到改进.
有一些选项可用于存储Python程序需要使用的密码和其他机密信息,特别是需要在后台运行的程序,该程序不能仅仅要求用户输入密码。
应避免的问题:
这并不总是一个选择,但可能是最好的选择。您的私钥永远不会通过网络传输,SSH只是运行数学计算以证明您拥有正确的密钥。
为了使其工作,您需要以下内容:
这是最简单的一个,因此可能是一个不错的起点。在“ 十二因子”应用程序中对此进行了很好的描述。基本思想是,您的源代码只是从环境变量中提取密码或其他机密,然后在运行程序的每个系统上配置这些环境变量。如果您使用适用于大多数开发人员的默认值,则可能也很不错。您必须权衡使软件“默认安全”。
这是一个从环境变量中提取服务器,用户名和密码的示例。
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)
Run Code Online (Sandbox Code Playgroud)
查找如何在操作系统中设置环境变量,并考虑以其自己的帐户运行服务。这样,当您以自己的帐户运行程序时,环境变量中不会包含敏感数据。设置这些环境变量时,请格外小心,以免其他用户无法读取它们。例如,检查文件权限。当然,具有root权限的任何用户都可以阅读它们,但这无济于事。
这与环境变量非常相似,但是您从文本文件中读取机密。我仍然发现环境变量在部署工具和持续集成服务器等方面更灵活。如果决定使用配置文件,Python会在标准库中支持几种格式,例如JSON,INI,netrc和XML。您还可以找到外部软件包,例如PyYAML和TOML。就个人而言,我发现JSON和YAML最易于使用,并且YAML允许注释。
配置文件要考虑的三件事:
~/.my_app)和命令行选项以使用其他位置。有些项目只是将他们的秘密放到Python模块中。
# settings.py
db_server = 'dbhost1'
db_user = 'my_app'
db_password = 'correcthorsebatterystaple'
Run Code Online (Sandbox Code Playgroud)
然后导入该模块以获取值。
# my_app.py
from settings import db_server, db_user, db_password
db_connect(db_server, db_user, db_password)
Run Code Online (Sandbox Code Playgroud)
使用此技术的一个项目是Django。显然,settings.py尽管您可能想要提交一个名为settings_template.py用户可以复制和修改的文件,但是您不应提交源代码管理。
我发现此技术存在一些问题:
.gitignore降低这种风险。如果您的项目已使用此技术,则很容易过渡到环境变量。只需将所有设置值移至环境变量,然后更改Python模块以从那些环境变量中读取。
尝试加密密码没有多大意义:您要隐藏密码的人拥有Python脚本,该脚本将具有解密密码。获取密码最快的方法是在将Python脚本与第三方服务一起使用密码之前,将打印语句添加到Python脚本中。
因此,将密码作为字符串存储在脚本中,并对其进行base64编码,这样仅读取文件就不够,然后每天调用它。
我认为你能做的最好的事情就是保护它运行的脚本文件和系统.
基本上做以下事情:
| 归档时间: |
|
| 查看次数: |
50800 次 |
| 最近记录: |