浏览器环境下JS前端加密密码,FastAPI后端解密可行方案咨询
浏览器前端加密密码并在FastAPI后端解密的可行方案
你可以直接使用浏览器原生的Web Crypto API(无需安装任何第三方库)结合后端RSA非对称加密来实现需求,以下是具体步骤和代码示例:
1. 后端生成RSA密钥对
首先在FastAPI后端生成RSA私钥和公钥,公钥传给前端用于加密,私钥留在后端解密:
from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.backends import default_backend # 生成2048位RSA私钥 private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) # 导出PEM格式公钥(供前端使用) public_key_pem = private_key.public_key().public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ).decode('utf-8') # 导出PEM格式私钥(后端保存,绝对不能暴露) private_key_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption() ).decode('utf-8')
2. 前端用Web Crypto API加密密码
前端通过Web Crypto API导入公钥,对密码进行加密,转换为Base64字符串后发送给后端:
async function encryptPassword(password, publicKeyPem) { // 清理PEM格式的头尾标识和空格 const cleanPublicKey = publicKeyPem .replace(/-----BEGIN PUBLIC KEY-----/, '') .replace(/-----END PUBLIC KEY-----/, '') .replace(/\s/g, ''); // 将Base64公钥转换为ArrayBuffer const keyBuffer = Uint8Array.from(atob(cleanPublicKey), c => c.charCodeAt(0)); // 导入RSA公钥 const publicKey = await window.crypto.subtle.importKey( 'spki', keyBuffer, { name: 'RSA-OAEP', hash: 'SHA-256' }, false, ['encrypt'] ); // 将密码转换为ArrayBuffer const passwordBuffer = new TextEncoder().encode(password); // 执行加密 const encryptedBuffer = await window.crypto.subtle.encrypt( { name: 'RSA-OAEP' }, publicKey, passwordBuffer ); // 转换为Base64字符串便于传输 return btoa(String.fromCharCode(...new Uint8Array(encryptedBuffer))); } // 使用示例:从后端获取公钥后加密密码 // const encryptedPwd = await encryptPassword('用户输入的密码', 后端返回的publicKeyPem); // 然后通过POST请求将encryptedPwd发送到FastAPI接口
3. FastAPI后端解密密码
后端接收加密后的Base64字符串,用私钥解密得到明文密码:
from fastapi import FastAPI, HTTPException import base64 from cryptography.hazmat.primitives.asymmetric import padding from cryptography.hazmat.primitives import hashes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization app = FastAPI() # 这里替换成你实际保存的私钥内容 PRIVATE_KEY_PEM = """-----BEGIN PRIVATE KEY----- 你的私钥PEM内容 -----END PRIVATE KEY-----""" @app.post("/verify-password") async def verify_password(encrypted_password: str): try: # 加载私钥 private_key = serialization.load_pem_private_key( PRIVATE_KEY_PEM.encode('utf-8'), password=None, backend=default_backend() ) # 将Base64加密字符串解码为字节 encrypted_bytes = base64.b64decode(encrypted_password) # 解密密码 decrypted_bytes = private_key.decrypt( encrypted_bytes, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) # 转换为明文密码 plain_password = decrypted_bytes.decode('utf-8') # 注意:拿到明文后不要存储,立即用bcrypt/Argon2等哈希算法处理后验证 # 示例:比对数据库中存储的密码哈希 # if bcrypt.checkpw(plain_password.encode('utf-8'), db_password_hash): # return {"status": "success"} return {"message": "解密成功", "plain_password": plain_password} except Exception as e: raise HTTPException(status_code=400, detail=f"解密失败: {str(e)}")
关键注意事项
- 安全要求:必须通过HTTPS传输,避免公钥和加密数据被劫持;后端拿到明文密码后,一定要用bcrypt、Argon2等慢哈希算法处理,绝对不能直接存储明文。
- 算法一致性:前后端必须使用相同的加密算法和参数(这里用的是RSA-OAEP + SHA-256),否则会解密失败。
- Web Crypto API限制:该API仅在HTTPS环境或localhost下可用,普通HTTP环境会报错。
内容的提问来源于stack exchange,提问作者KK RR
相关产品推荐
相关产品推荐

