如何使用Python隐藏敏感凭据
您是否曾经遇到过这样的情况:您正在处理Python项目,需要与某人共享您的代码,或者您将代码托管在公共存储库中,但又不想共享敏感凭据,因此它不会被其他人利用?随机用户?
例如,您正在 Django 中创建一个 Web 应用程序,其中有一个“SECRET_KEY”的概念,它是一个随机生成的唯一密钥,用于数据的加密签名。顾名思义,它不应该被公开共享,因为它破坏了 Django 的许多安全保护。或者,也许您正在使用云存储,比如 AWS S3,您需要将访问令牌存储在代码中,并防止未经授权的用户滥用凭证,我们如何才能做到这两点?对于这种情况,我们需要防止将“密钥”(本质上是保存我们凭据的变量)硬编码到我们的代码中,然后避免将其暴露在我们的公共存储库中。
方法一:从另一个Python文件导入
最简单和基本的方法之一是将凭据保存在另一个Python文件中,例如 secrets.py 并将其导入到所需的文件中。我们需要 。 git 忽略 secrets.py文件。现在我们可以通过两种方式存储凭证,第一种是使用Python变量来存储值,第二种更优选的方式是使用字典。字典是首选,因为如果我们尝试访问一个不存在的变量,它将引发错误,但在字典的情况下,我们可以返回一个默认值。
我们已将以下凭据保存在 secrets.py 的字典中:
Python3
# secrets.py
secrets = {
'SECRET_KEY': "superSecretkey1234",
'DATABASE_USER': "testusr",
'DATABASE_PASSWORD': 'pass1234',
'DATABASE_PORT': 5432
}
Python3
# main.py
from secrets import secrets
secret_key = secrets.get('SECRET_KEY')
# gives default value if the credential is absent
google_maps_key = secrets.get('gmaps_key',
'mapsapikey543')
db_user = secrets.get('DATABASE_USER', 'root')
db_pass = secrets.get('DATABASE_PASSWORD', 'pass')
db_port = secrets.get('DATABASE_PORT', 3306)
print('secret_key :', secret_key)
print('google_maps_key :', google_maps_key)
print('db_user :', db_user)
print('db_pass :', db_pass)
# no need to type cast numbers and booleans
print('db_port :', db_port, type(db_port))
Python3
# main.py
import os
def convert(val):
if type(val) != str:
return val
if val.isnumeric():
return int(val)
elif val == 'True':
return True
elif val == 'False':
return False
else:
return val
secret_key = convert(os.environ.get('SECRET_KEY'))
# gives default value if the credential is absent
google_maps_key = convert(os.environ.get('gmaps_key',
'mapsapikey543'))
db_user = convert(os.environ.get('DATABASE_USER', 'root'))
db_pass = convert(os.environ.get('DATABASE_PASSWORD', 'pass'))
db_port = convert(os.environ.get('DATABASE_PORT', '3306'))
print('secret_key :', secret_key)
print('google_maps_key :', google_maps_key)
print('db_user :', db_user)
print('db_pass :', db_pass)
print('db_port :', db_port, type(db_port))
Python3
# main.py
# 1. Import the config object from decouple.
from decouple import config
# 2. Retrieve the credentials in your code.
# default data-type of returned value is str.
SECRET_KEY = config('SECRET_KEY')
# you can cast the values on the fly.
DEBUG = config('DEBUG', cast=bool)
# provide defaulft value if key is not found.
EMAIL_HOST = config('EMAIL_HOST',
default='localhost')
EMAIL_USER = config('EMAIL_USER',
default='gfg')
# if key not found and no default is given,
# it will raise UndefinedValueError.
EMAIL_PASSWORD = config('EMAIL_PASSWORD')
EMAIL_PORT = config('EMAIL_PORT',
default=25, cast=int)
print('secret_key :', SECRET_KEY)
print('email_host :', EMAIL_HOST)
print('email_user :', EMAIL_USER)
print('email_pass :', EMAIL_PASSWORD)
print('email_port :', EMAIL_PORT, type(EMAIL_PORT))
现在在所需文件 main.py 中导入凭据。
蟒蛇3
# main.py
from secrets import secrets
secret_key = secrets.get('SECRET_KEY')
# gives default value if the credential is absent
google_maps_key = secrets.get('gmaps_key',
'mapsapikey543')
db_user = secrets.get('DATABASE_USER', 'root')
db_pass = secrets.get('DATABASE_PASSWORD', 'pass')
db_port = secrets.get('DATABASE_PORT', 3306)
print('secret_key :', secret_key)
print('google_maps_key :', google_maps_key)
print('db_user :', db_user)
print('db_pass :', db_pass)
# no need to type cast numbers and booleans
print('db_port :', db_port, type(db_port))
输出 :
这是有效的,我们不需要担心布尔值和整数值的数据类型转换(您将了解为什么这在后面的方法中很重要)但不是推荐的方法,因为文件名和字典名可能因不同而不同项目,因此它不会形成标准解决方案。更重要的是,这种方法仅限于Python,因为在更现实的场景中,我们可以使用多种语言,这些语言也需要访问相同的凭据,并且以一种只能访问一种语言的方式存储它们并不理想。更好的方法是使用环境变量。
方法二:使用环境变量
我们可以将凭据存储为环境变量。环境变量本质上是使用操作系统功能设置的键值对,并且可以被任何编程语言使用,因为它们与环境或操作系统相关联。由于我们将凭据设置为环境变量,因此我们不会在代码中公开它们,因此如果其他人有权访问我们的代码,则不会在他们的环境中设置凭据。此外,我们可以为生产环境和本地环境设置不同的值,例如在开发过程中使用不同的邮件服务,我们无需担心更改代码。
许多托管服务提供商(如 Heroku、netlify 等)提供了一种设置环境变量的简单方法。在Python,我们可以使用 os.environ 访问环境变量,它的工作原理与普通的Python字典非常相似。 os.environ 返回字符串值,我们需要手动对每个值进行类型转换。假设我们已经将上面提到的相同凭据设置为环境变量。
蟒蛇3
# main.py
import os
def convert(val):
if type(val) != str:
return val
if val.isnumeric():
return int(val)
elif val == 'True':
return True
elif val == 'False':
return False
else:
return val
secret_key = convert(os.environ.get('SECRET_KEY'))
# gives default value if the credential is absent
google_maps_key = convert(os.environ.get('gmaps_key',
'mapsapikey543'))
db_user = convert(os.environ.get('DATABASE_USER', 'root'))
db_pass = convert(os.environ.get('DATABASE_PASSWORD', 'pass'))
db_port = convert(os.environ.get('DATABASE_PORT', '3306'))
print('secret_key :', secret_key)
print('google_maps_key :', google_maps_key)
print('db_user :', db_user)
print('db_pass :', db_pass)
print('db_port :', db_port, type(db_port))
输出 :
此外,在开发过程中本地设置的环境变量仅在会话中保持不变,因此我们需要在每次运行项目之前手动设置它们。为了使过程更简单,我们有一个很棒的包python-decouple 。该包有助于从环境或外部 .env 或 .ini 文件加载凭据。
我们将凭据存储在 .env 或 settings.ini 文件中并gitignore它们。 python-decouple 按以下顺序搜索选项:
- 环境变量。
- ini 或 .env 文件。
- 调用期间传递的默认值。
如果已经设置了环境变量,则返回相同的值,否则会尝试从文件中读取,如果未找到,则可以返回默认值。所以它可以在生产时从环境中读取,在开发时从文件中读取。
请按照以下简单的 3 步流程进行操作:
第 1 步:使用 pip 安装 python-decouple。
pip install python-decouple
第 2 步:单独存储您的凭据。
首先,我们需要将我们的凭据从我们的代码存储库中“解耦”到一个单独的文件中。如果您使用的是版本控制系统,请说 git 确保将此文件添加到.gitignore 。该文件应采用以下形式之一,并应保存在存储库的根目录中:
- 设置文件
- .env -注意文件没有名字
这些是用于保存项目配置的流行文件格式。这些文件遵循以下语法:
KEY=YOUR_KEY -> without any quotes.
作为惯例,我们将键名存储在所有大写字母中。
这里我们使用 .env 格式并保存了以下凭据:
现在,我们将。 git 忽略这个文件。要了解更多关于 . gitignore读这个:
第 3 步:安全地加载您的凭据。
我们可以通过将“cast”参数指定为相应的类型来转换值。此外,如果我们没有指定默认值并且配置对象找不到给定的键,它将引发“UndefinedValueError”。
蟒蛇3
# main.py
# 1. Import the config object from decouple.
from decouple import config
# 2. Retrieve the credentials in your code.
# default data-type of returned value is str.
SECRET_KEY = config('SECRET_KEY')
# you can cast the values on the fly.
DEBUG = config('DEBUG', cast=bool)
# provide defaulft value if key is not found.
EMAIL_HOST = config('EMAIL_HOST',
default='localhost')
EMAIL_USER = config('EMAIL_USER',
default='gfg')
# if key not found and no default is given,
# it will raise UndefinedValueError.
EMAIL_PASSWORD = config('EMAIL_PASSWORD')
EMAIL_PORT = config('EMAIL_PORT',
default=25, cast=int)
print('secret_key :', SECRET_KEY)
print('email_host :', EMAIL_HOST)
print('email_user :', EMAIL_USER)
print('email_pass :', EMAIL_PASSWORD)
print('email_port :', EMAIL_PORT, type(EMAIL_PORT))
输出: