69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
"""
|
||
HTML 模板加密/解密模块
|
||
使用 AES (Fernet) 对 HTML 资源进行加密存储,运行时解密后渲染
|
||
"""
|
||
|
||
import base64
|
||
import os
|
||
|
||
from cryptography.fernet import Fernet
|
||
from cryptography.hazmat.primitives import hashes
|
||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||
|
||
|
||
def _get_fernet_key() -> bytes:
|
||
"""从环境变量 HTML_ENCRYPT_KEY 派生 Fernet 密钥,若未设置则使用默认开发密钥"""
|
||
raw = os.environ.get(
|
||
"HTML_ENCRYPT_KEY",
|
||
"maixiang_html_encrypt_default_key_change_in_production",
|
||
)
|
||
# 使用 PBKDF2 派生 32 字节密钥,再转为 Fernet 所需的 base64url
|
||
kdf = PBKDF2HMAC(
|
||
algorithm=hashes.SHA256(),
|
||
length=32,
|
||
salt=b"maixiang_html_salt",
|
||
iterations=100000,
|
||
)
|
||
key_bytes = kdf.derive(raw.encode("utf-8"))
|
||
return base64.urlsafe_b64encode(key_bytes)
|
||
|
||
|
||
def encrypt(plain_data: bytes) -> bytes:
|
||
"""加密原始数据,返回密文(bytes)"""
|
||
key = _get_fernet_key()
|
||
f = Fernet(key)
|
||
return f.encrypt(plain_data)
|
||
|
||
|
||
def decrypt(encrypted_data: bytes) -> bytes:
|
||
"""解密数据,返回明文(bytes)"""
|
||
key = _get_fernet_key()
|
||
f = Fernet(key)
|
||
return f.decrypt(encrypted_data)
|
||
|
||
|
||
def encrypt_file(path: str) -> None:
|
||
"""就地加密文件:读取 UTF-8 内容,加密后写回同一路径"""
|
||
with open(path, "rb") as f:
|
||
plain = f.read()
|
||
encrypted = encrypt(plain)
|
||
with open(path, "wb") as f:
|
||
f.write(encrypted)
|
||
|
||
|
||
def decrypt_file(path: str) -> None:
|
||
"""就地解密文件:读取密文,解密后以 UTF-8 写回同一路径"""
|
||
with open(path, "rb") as f:
|
||
encrypted = f.read()
|
||
plain = decrypt(encrypted)
|
||
with open(path, "wb") as f:
|
||
f.write(plain)
|
||
|
||
|
||
def is_encrypted(data: bytes) -> bool:
|
||
"""简单启发式:Fernet 密文以 b'gAAAAA' 开头(base64 编码后)"""
|
||
try:
|
||
return data[:7] == b"gAAAAAB"
|
||
except Exception:
|
||
return False
|