本文主要是介绍Python和JS开发者保护使用者敏感信息的策略浅谈,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Python和JS开发者保护使用者敏感信息的策略浅谈
概述
Python和JS这类语言,开发的程序,不能编译发布,对如敏感信息,如销售系统,开发登录程序,登录密码是非常重要的,从这类语言的特点和程序设计角度,如何保护登录密码值得考虑。不要在代码中硬编码密码,应该使用加密算法对密码进行加密。
保护敏感信息是一个多方面的任务,需要结合使用多种技术和策略。本文是我的学习和认识总结记录,不当之处请读者朋友不吝指点,我将不断更新。
以下是基本的安全措施。
1. 使用环境变量
将敏感信息存储在环境变量中,而不是直接硬编码在源代码里。这样,即使源代码被泄露,敏感信息也不会直接暴露出来。在程序运行时,可以从环境变量中读取这些信息。
2. 使用外部配置文件
将敏感信息存储在外部配置文件中,并确保这些配置文件不会被包含在版本控制系统(如Git)中。可以通过.gitignore文件排除这些配置文件。在程序中读取这些外部配置文件来获取敏感信息。
3.使用哈希和加密
对于密码验证,不应该在代码中直接对比明文密码。相反,应该使用哈希函数处理密码。在用户注册时,将用户密码的哈希值存储在数据库中。在用户登录时,将输入的密码进行同样的哈希处理,然后对比数据库中存储的哈希值。这样,即使数据库被泄露,攻击者也无法直接获得原始密码。
4. 保护源码
对于使用Python、JavaScript这类解释型语言开发的程序,因为不能编译发布,很容易找到登录密码对比语句,可以采取保护源码。有几种方法可以提高代码的保护水平,减少安全风险:
(1)代码编译/打包
对于JavaScript,可以使用Webpack、Rollup或Parcel等工具将代码打包。这些工具通常结合了代码压缩和混淆的功能,可以将多个文件合并成一个文件,同时使代码更难以直接阅读。
对于Python,虽然不能像编译型语言那样编译成机器码,但可以使用工具如PyInstaller或cx_Freeze将Python程序打包成可执行文件(exe),这样可以隐藏源代码。
(2)代码混淆(Obfuscation)
对于JavaScript,可以使用工具如UglifyJS或Terser进行混淆。
对于Python,如pyarmor、pyminifier,可以混淆Python源码,使其难以被直接阅读。
(3)使用C/C++等编译型语言编写关键逻辑
将程序中对安全性要求较高的部分,如密码验证逻辑,用C或C++等编译型语言编写,并编译成库文件(如.dll、.so或.dylib文件),然后从Python或JavaScript中调用这些库。这样可以保护这些核心逻辑不被轻易查看或修改。
需要明确的是,这只是其中的一部分。通过采取一系列措施,可以有效地保护这些敏感信息。需要强调的是,以上的方法并不能100%保证安全,但可以大大提高攻击者攻击的难度,增强系统的安全性。
以Python用户登录系统为例介绍
一、如何加密密码。
一个具备图形用户界面(GUI)的Python用户登录系统,密码保存在文件中。建一个名为passwords.txt的文件,格式如下,每行包含一个用户名和密码,以逗号和空格分隔,例如:
user1, password1
abc, abc123
在实际应用中,直接以明文形式存储密码是非常不安全的。我们Python 的标准库之一hashlib 【详见https://docs.python.org/zh-cn/3/library/hashlib.html 】加密,加密后的文件为hashed_passwords.txt文件中,为了使生成的hashed_passwords.txt文件对开发者而言是唯一的,我们可以引入一个额外的参数作为“盐”(salt),并将其与密码一起哈希处理。这种“盐”值可以是任何数据,但为了保证唯一性,通常使用随机生成的数据或者某些与用户账户直接相关的唯一数据(如用户ID、用户名的哈希值等)。
【"盐值"(Salt)在密码学中是一个添加到哈希算法的一部分,用以确保相同的输入在哈希后会产生不同的结果。这样即使两个用户拥有相同的密码,他们的哈希值也会是不同的。】
下面是将passwords.txt的文件加密hashed_passwords.txt文件的python代码:
import hashlib
import osdef hash_password(password_salt):"""生成密码的SHA-256哈希值,结合盐值"""sha_signature = hashlib.sha256(password_salt.encode()).hexdigest()return sha_signature# 假设有两个用户的密码需要存储,这里使用用户名作为盐值的一种简单示例
passwords_to_hash = {"user1": "password1","abc": "abc123",
}# 生成哈希值并存储
with open("hashed_passwords.txt", "w") as file:for username, password in passwords_to_hash.items():# 在这个例子中,盐(salt)值,是一种简化的做法salt = "abc123" # 这里仅作为示例,不推荐在生产环境中这样做password_salt = password + salthashed_password = hash_password(password_salt)file.write(f"{username}, {hashed_password}\n")print("完成!")
这个示例仅用来展示了如何增加哈希的唯一性。【对于生产环境下强烈建议使用专门的密码哈希库,如bcrypt,它会自动处理盐值和其他安全措施。】
打开加密后的文件如下:
用户需要记住和使用的是加密前的密码,也就是明文密码,而非加密后的值或哈希值。
下面是使用tkinter库来创建GUI登录文件,来使用加密密码。源码如下:
import tkinter as tk
import hashlib
from tkinter import messagebox# 创建主窗口
root = tk.Tk()
root.title("用户登录")
root.geometry("300x150")# 添加用户名和密码标签和输入框
tk.Label(root, text="用户名:").place(x=20, y=20)
tk.Label(root, text="密码:").place(x=20, y=50)username_entry = tk.Entry(root)
username_entry.place(x=100, y=20)password_entry = tk.Entry(root, show="*")
password_entry.place(x=100, y=50)# 用于显示登录失败信息的标签,初始时不显示任何文本
login_fail_label = tk.Label(root, text="", fg="red")
login_fail_label.place(x=20, y=110)# 处理登录逻辑
def login():username = username_entry.get()password = password_entry.get() hashed_password = hash_password(password) # 对输入的密码生成哈希值try:with open("hashed_passwords.txt", "r") as file:for line in file:stored_username, stored_hashed_password = line.strip().split(", ")if username == stored_username and hashed_password == stored_hashed_password:login_success_window()root.withdraw() # 隐藏登录窗口returnlogin_fail_label.config(text="登录失败!用户名或密码错误。")except FileNotFoundError:login_fail_label.config(text="密码文件不存在!")def hash_password(password):"""生成密码的SHA-256哈希值,结合盐值"""password_salt = password + "abc123"sha_signature = hashlib.sha256(password_salt.encode()).hexdigest()return sha_signature# 新窗口函数
def login_success_window():new_window = tk.Toplevel(root)new_window.title("主窗口")new_window.geometry("400x300")tk.Label(new_window, text="欢迎你的到来!!").pack()# 处理取消按钮的点击事件
def cancel():root.destroy()# 添加登录和取消按钮
login_button = tk.Button(root, text="登录", command=login)
login_button.place(x=100, y=80)cancel_button = tk.Button(root, text="取消", command=cancel)
cancel_button.place(x=170, y=80)# 运行主循环
root.mainloop()
将hashed_passwords.txt文件存在这个源码文件相同的路径下,因为这个源码依赖于读写hashed_passwords.txt文件。
运行源码文件,显示如下图:
用户需要记住和使用的是加密前的密码,也就是明文密码,你可以登录试试啦。
修改A
添加完善功能,在新窗口new_window中实现修改用户密码功能,修改密码时需要两次输入新密码来确认,源码如下:
import tkinter as tk
import hashlib
from tkinter import messagebox# 创建主窗口
root = tk.Tk()
root.title("用户登录")
root.geometry("300x150")# 添加用户名和密码标签和输入框
tk.Label(root, text="用户名:").place(x=20, y=20)
tk.Label(root, text="密码:").place(x=20, y=50)username_entry = tk.Entry(root)
username_entry.place(x=100, y=20)password_entry = tk.Entry(root, show="*")
password_entry.place(x=100, y=50)# 用于显示登录失败信息的标签,初始时不显示任何文本
login_fail_label = tk.Label(root, text="", fg="red")
login_fail_label.place(x=20, y=110)# 处理登录逻辑
def login():username = username_entry.get()password = hash_password(password_entry.get()) # 加密用户输入的密码# print(username,password)try:with open("hashed_passwords.txt", "r") as file: # 确保文件名正确for line in file:stored_username, stored_hashed_password = line.strip().split(", ")if username == stored_username and password == stored_hashed_password:login_success_window()root.withdraw() # 隐藏登录窗口return# 如果循环结束还没有返回,说明登录失败login_fail_label.config(text="登录失败!用户名或密码错误。")except FileNotFoundError:login_fail_label.config(text="密码文件不存在!")# 新窗口函数
def login_success_window():new_window = tk.Toplevel(root)new_window.title("修改密码")new_window.geometry("400x300")tk.Label(new_window, text="欢迎你的到来!!").pack()# 添加修改密码的输入框和按钮tk.Label(new_window, text="新密码:").pack()new_password_entry = tk.Entry(new_window, show="*")new_password_entry.pack()# 添加第二个新密码输入框和标签tk.Label(new_window, text="确认新密码:").pack() # 新增代码new_password_confirm_entry = tk.Entry(new_window, show="*") # 新增代码new_password_confirm_entry.pack() # 新增代码change_password_button = tk.Button(new_window, text="修改密码", command=lambda: change_password(new_password_entry.get(), new_password_confirm_entry.get())) # 修改这行change_password_button.pack()def change_password(new_password, confirm_password):if new_password != confirm_password:messagebox.showerror("错误", "两次输入的密码不一致!")returnusername = username_entry.get()new_hashed_password = hash_password(new_password)# 读取原始密码文件,并替换相应用户名的密码try:with open("hashed_passwords.txt", "r") as file:lines = file.readlines()with open("hashed_passwords.txt", "w") as file:for line in lines:stored_username, stored_hashed_password = line.strip().split(", ")if stored_username == username:file.write(f"{stored_username}, {new_hashed_password}\n")else:file.write(line)messagebox.showinfo("成功", "密码修改成功!")except FileNotFoundError:messagebox.showerror("错误", "密码文件不存在!")# 哈希密码函数
def hash_password(password):"""生成密码的SHA-256哈希值,结合盐值"""password_salt = password + "abc123"sha_signature = hashlib.sha256(password_salt.encode()).hexdigest()return sha_signature# 处理取消按钮的点击事件
def cancel():root.destroy()# 添加登录和取消按钮
login_button = tk.Button(root, text="登录", command=login)
login_button.place(x=100, y=80)cancel_button = tk.Button(root, text="取消", command=cancel)
cancel_button.place(x=170, y=80)# 运行主循环
root.mainloop()
运行这个源码文件,显示如下图:
用户需要记住和使用的是加密前的密码,也就是明文密码,你可以登录并且修改密码试试啦。
修改B
将所有代码写入单个Python文件对于小型项目而言是常见且可接受的。然而,随着项目的增长,这种做法可能会导致代码难以维护。
现在,将上面的代码分割,将处理认证逻辑移动到另一个文件auth.py中,负责界面显示和用户交互的代码放到主文件login_system_main.py中。
这样的结构使得你的项目更加模块化,逻辑与界面分离,便于后期的维护和扩展。
1、login_system_main.py文件源码如下:
import tkinter as tk
from tkinter import messagebox
import auth # 导入我们之前分割出去的认证模块# 创建主窗口
root = tk.Tk()
root.title("用户登录")
root.geometry("300x150")# 添加用户名和密码标签和输入框
tk.Label(root, text="用户名:").place(x=20, y=20)
tk.Label(root, text="密码:").place(x=20, y=50)username_entry = tk.Entry(root)
username_entry.place(x=100, y=20)password_entry = tk.Entry(root, show="*")
password_entry.place(x=100, y=50)# 用于显示登录失败信息的标签,初始时不显示任何文本
login_fail_label = tk.Label(root, text="", fg="red")
login_fail_label.place(x=20, y=110)# 新窗口函数
def login_success_window():root.withdraw() # 隐藏登录窗口new_window = tk.Toplevel(root)new_window.title("修改密码")new_window.geometry("400x300")tk.Label(new_window, text="欢迎你的到来!!").pack()# 添加修改密码的输入框和按钮tk.Label(new_window, text="新密码:").pack()new_password_entry = tk.Entry(new_window, show="*")new_password_entry.pack()tk.Label(new_window, text="确认新密码:").pack()new_password_confirm_entry = tk.Entry(new_window, show="*")new_password_confirm_entry.pack()change_password_button = tk.Button(new_window, text="修改密码", command=lambda: auth.change_password(username_entry.get(),new_password_entry.get(),new_password_confirm_entry.get(),lambda msg: messagebox.showinfo("成功", msg),lambda err: messagebox.showerror("错误", err)))change_password_button.pack()def login():auth.login(username_entry.get(),password_entry.get(),login_success_window,lambda err: login_fail_label.config(text=err))# 添加登录和取消按钮
login_button = tk.Button(root, text="登录", command=login)
login_button.place(x=100, y=80)cancel_button = tk.Button(root, text="取消", command=root.destroy)
cancel_button.place(x=170, y=80)# 运行主循环
root.mainloop()
2、auth.py源码文件内容如下:
import hashlibdef hash_password(password):"""生成密码的SHA-256哈希值,结合盐值"""password_salt = password + "abc123"sha_signature = hashlib.sha256(password_salt.encode()).hexdigest()return sha_signaturedef login(username, password, success_callback, fail_callback):hashed_password = hash_password(password)try:with open("hashed_passwords.txt", "r") as file:for line in file:stored_username, stored_hashed_password = line.strip().split(", ")if username == stored_username and hashed_password == stored_hashed_password:success_callback()returnfail_callback("登录失败!用户名或密码错误。")except FileNotFoundError:fail_callback("密码文件不存在!")def change_password(username, new_password, confirm_password, success_callback, error_callback):if new_password != confirm_password:error_callback("两次输入的密码不一致!")returnnew_hashed_password = hash_password(new_password)try:with open("hashed_passwords.txt", "r") as file:lines = file.readlines()with open("hashed_passwords.txt", "w") as file:for line in lines:stored_username, stored_hashed_password = line.strip().split(", ")if stored_username == username:file.write(f"{stored_username}, {new_hashed_password}\n")else:file.write(line)success_callback("密码修改成功!")except FileNotFoundError:error_callback("密码文件不存在!")
从login_system_main.py文件启动,运行效果和未分开的一样。
二、Python源码保护
一)、自己编程实现源码保护
自己编写脚本对Python代码进行加密,然后在应用运行时再解密执行。
要实现Python代码的加密与运行时解密执行,你可以采用以下步骤。这个例子使用了Python的cryptography库来进行加密和解密,确保了加密过程的安全性。请注意,这种方法并不是对抗专业逆向工程的最佳方式,但它可以增加代码被轻易读取和修改的难度。
在此,使用Python自带的标准库进行简单的加密和解密,我们可以利用base64进行编码以及exec来执行解码后的代码。base64官方介绍见https://docs.python.org/zh-cn/3/library/base64.html
对于,我前面建立的登录系统含有3个文件:
将这三个文件放到同一个目录中。
auth.py
login_system_main.py
hashed_passwords.txt
其中,hashed_passwords.txt是登录密码,
auth.py认是证逻辑代码文件
login_system_main.py是用户交互的代码主文件
如何用base64进行编码、解码并发布?
第一步、使用如下加密程序如下,使用它对login_system_main.py和auth.py其进行base64编码:
# encode_script.py
import base64def encode_file_to_base64(source_file, encoded_file):""":param source_file: 源文件路径,即要加密的Python脚本文件路径。:param encoded_file: 编码后的文件路径,即加密后的数据将要保存的文件路径。"""# 读取你想加密的Python脚本with open(source_file, "rb") as file:file_data = file.read()# 编码文件内容encoded_data = base64.b64encode(file_data)# 将编码后的数据写入一个新文件with open(encoded_file, "wb") as file:file.write(encoded_data)# 使用示例
# 调用函数,加密
encode_file_to_base64("login_system_main.py", "login_system_main.enc")
encode_file_to_base64("auth.py", "auth.enc")
第二步:装载解码并执行,源码如下:
# loader.py
import base64
import types
import sys# 定义一个函数用于从base64编码的文件中解码数据
def decode_file_from_base64(encoded_file_path):# 以二进制读取模式打开文件with open(encoded_file_path, 'rb') as file:encoded_data = file.read()# 解码数据decoded_data = base64.b64decode(encoded_data)# 将解码后的二进制数据转换为字符串,并返回return decoded_data.decode()# 定义一个函数用于从代码字符串中加载模块
def load_module_from_code(module_name, code):# 创建一个新的模块对象module = types.ModuleType(module_name)# 执行代码,并将代码定义的全局变量存储到模块的命名空间exec(code, module.__dict__)# 将创建的模块添加到 sys.modules 中,这样它就可以被其他地方通过 import 语句导入sys.modules[module_name] = module# 返回模块对象return module# 主函数
def main():# 解码 auth.enc 文件,并将解码后的内容加载为 auth 模块auth_code = decode_file_from_base64('auth.enc')auth_module = load_module_from_code('auth', auth_code)# 创建一个全局命名空间,其中包含了 auth 模块# 这个字典模拟了全局命名空间,以便于 exec 函数执行代码时能够识别 auth 模块globals_for_exec = {'auth': auth_module}# 解码 login_system_main.enc 文件,并使用自定义的全局命名空间执行解码后的代码login_system_main_code = decode_file_from_base64('login_system_main.enc')exec(login_system_main_code, globals_for_exec)# 当该脚本被直接执行时,调用 main 函数
if __name__ == '__main__':main()
特别提示,由这个代码文件装载解码并执行加密的代码文件,我这里命名为loader.py。
这种方式允许你动态地执行加密存储的Python代码,增加了代码的安全性和灵活性。这种方法要求你明确知道多个.py文件之间的依赖关系,需要按正确的顺序加载它们。对于更复杂的依赖关系,有些麻烦。
这个方法为解码并执行加密的Python模块提供了一个基本的框架,但在实际应用中,根据你的具体需求和安全考虑,可能需要进一步的定制和改进。
将这如下几个复制到同一个文件,发布给用户即可
从loader.py文件启动运行。
二)、使用第三方的工具
Python代码混淆工具pyarmor,它可以混淆代码并提供一定程度的保护。
pyarmor是一个有免费和付费版本的Python代码混淆工具。对于个人用户和非商业用途,pyarmor的免费版本通常已经足够用。它对源代码提供了基本的混淆保护,可以有效防止源代码被轻易阅读和修改。
中文使用文档:https://pyarmor.readthedocs.io/zh/latest
下面是如何使用pyarmor混淆Python代码和发布运行混淆后的代码的步骤:
安装PyArmor
首先,你需要安装pyarmor。可以通过pip安装,在命令行工具(例如cmd或PowerShell)中运行以下命令:
pip install pyarmor
如果安装有多个Python版本,在命令行中运行以下命令:
安装路径\python.exe -m pip install 模块(库、包)名
其中,安装路径
【查看Python安装路径方法,在cmd中使用命令
py -0p
其中0是零。
其中带星号*者,为默认使用版本。
3.10的安装路径D:\Python\Python310】
或
py -X.Y -m pip install 模块(库、包)名
其中
X.Y代表Python版本,多余的部分舍弃如3.9.1取3.8,3.10.13取3.10,即只取第二个点前的部分。
D:\Python\Python310\python.exe -m pip install numpy
【关于安装安装第三方库的更多情况,可参见:https://blog.csdn.net/cnds123/article/details/104393385 】
我这里为Python 3.10(路径D:\Python\Python310)安装pyarmor,因此在cmd中,使用
D:\Python\Python310\python.exe -m pip install pyarmor
或
py -3.10 -m pip install pyarmor
即可。
安装完成之后,cmd中输入命令 pyarmor --version 并回车执行。如果安装成功,会显示出 Pyarmor 的版本信息。
如何PyArmor 8.0+版本混淆源码
对于PyArmor 8.0+版本(我这里是Pyarmor 8.4.7),混淆一个源码foo.py,可以在cmd中使用如下命令
pyarmor gen foo.py
该命令生成一个混淆的源码dist/foo.py,默认输出路径中所有生成的文件:
其中,有一个额外的 Python 包pyarmor_runtime_000000,需要它来运行混淆的源码。
分发源码和运行
仅仅复制dist/foo.py到另一台机器是行不通的,而是复制 dist目录, 请在相同Python版本、相同平台的机器上运行此混淆代码,否则无法运行。因为pyarmor_runtime_000000有扩展模块,所以它是平台相关的并且绑定到Python版本。
通过Python解释器运行它:
[pythond版本的路径/]python[.exe] dist/foo.py
例如
D:\Python\Python310\python dist/login_system_main.py
对于,前面建立的登录系统含有3个文件:
将这三个文件放到在D:/Encryption_testing中,注意这个路径下面用到。
D:\Encryption_testing中
auth.py
login_system_main.py
hashed_passwords.txt
其中,hashed_passwords.txt是登录密码,
auth.py认是证逻辑代码文件
login_system_main.py是用户交互的代码主文件
如何用pyarmor混淆并发布?
步骤1、打开命令行并切换到工作目录
首先,打开命令行工具(例如cmd或PowerShell),然后使用cd命令切换到你的工作目录:
cd /d D:\Encryption_testing
步骤2、使用PyArmor混淆Python源码
pyarmor gen auth.py
pyarmor gen login_system_main.py
这将在dist目录下生成混淆后的源码。默认情况下,混淆后的文件会被放在当前目录下的dist子目录中。你可以通过 -o选项来指定不同的输出目录。
步骤3、分发源码和运行
复制 dist目录, 请在相同Python版本、相同平台的机器上运行此混淆代码,否则无法运行。因为pyarmor_runtime_000000有扩展模块,所以它是平台相关的并且绑定到Python版本。
这样运行它:
D:\Python\Python310\python.exe dist/login_system_main.py
这样使用PyArmor好像有点麻烦。
这篇关于Python和JS开发者保护使用者敏感信息的策略浅谈的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!