在Flutter+Dart+SQLite应用中向数据库存储数据时如何加密密码?
Flutter + SQLite 场景下密码及敏感信息加密实现方案
一、密码加密实现(必选)
密码绝对不要使用对称加密存储,必须采用不可逆的加盐哈希算法,避免数据库泄露后密码被还原,Dart生态直接使用成熟的crypto库实现即可。
- 第一步:添加依赖到
pubspec.yaml
dependencies: crypto: ^3.0.3 # 如果你已经接入SQLite,sqlite3相关依赖已经存在可以跳过 sqlite3_flutter_libs: ^0.5.18 path_provider: ^2.1.0
- 第二步:实现加盐哈希工具方法
每个用户要生成独立的随机盐,不要全局共用同一个盐,降低撞库风险:
import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; // 生成16位随机盐 String generateSalt() { final random = Random.secure(); final saltBytes = List<int>.generate(16, (_) => random.nextInt(256)); return base64Encode(saltBytes); } // 结合盐生成密码哈希值 String hashPassword(String plainPassword, String salt) { final bytes = utf8.encode('$plainPassword$salt'); final hashResult = sha256.convert(bytes); return hashResult.toString(); }
- 第三步:调整用户表结构,存储哈希值和盐
不要存储明文密码,把盐和哈希后的密码一起存入SQLite:
CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, salt TEXT NOT NULL, -- 其他自定义用户字段 phone TEXT, email TEXT );
- 第四步:注册/登录逻辑适配
- 注册时:先为用户生成唯一盐,用明文密码+盐计算哈希值,最后把盐、哈希密码存入数据库
- 登录校验时:先根据用户名从库中取出对应的盐和存储的哈希值,把用户输入的密码+盐计算哈希,和库中存储的哈希值对比,一致则校验通过
bool verifyLogin(String inputPassword, String storedSalt, String storedHash) { final currentHash = hashPassword(inputPassword, storedSalt); return currentHash == storedHash; }
二、其他个人敏感信息加密(可选)
如果手机号、邮箱等个人信息也需要加密存储,使用AES对称加密即可,因为这类信息后续可能需要解密读取使用。
- 第一步:添加依赖
dependencies: encrypt: ^5.0.1 # 用于存储AES密钥,不要硬编码密钥在代码中 flutter_secure_storage: ^8.0.0
- 第二步:实现加解密工具类
AES密钥要存在系统级安全存储(钥匙串/Keystore)中,不要存在SQLite或普通本地文件:
import 'package:encrypt/encrypt.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class SensitivesEncryptUtil { static const _aesKeyStorageKey = 'app_aes_secret_key'; static const _storage = FlutterSecureStorage(); // APP首次启动时调用一次,生成全局AES密钥存入安全存储 static Future<void> initAesKey() async { final existedKey = await _storage.read(key: _aesKeyStorageKey); if (existedKey == null) { final key = Key.fromSecureRandom(32); // 256位强度AES密钥 await _storage.write(key: _aesKeyStorageKey, value: key.base64); } } // 加密敏感内容 static Future<String> encrypt(String plainText) async { final keyBase64 = await _storage.read(key: _aesKeyStorageKey); final key = Key.fromBase64(keyBase64!); final iv = IV.fromSecureRandom(16); // 每个加密内容生成独立IV final encrypter = Encrypter(AES(key)); final encryptedResult = encrypter.encrypt(plainText, iv: iv); // IV和加密结果拼接后存储,解密时拆分即可 return '${iv.base64}:${encryptedResult.base64}'; } // 解密敏感内容 static Future<String> decrypt(String encryptedStr) async { final keyBase64 = await _storage.read(key: _aesKeyStorageKey); final key = Key.fromBase64(keyBase64!); final parts = encryptedStr.split(':'); final iv = IV.fromBase64(parts[0]); final encryptedData = Encrypted.fromBase64(parts[1]); final encrypter = Encrypter(AES(key)); return encrypter.decrypt(encryptedData, iv: iv); } }
- 第三步:数据库读写逻辑适配
存入SQLite前先对敏感字段调用加密方法,读取后调用解密方法还原即可。
注意事项
- 不要自行实现加密算法,直接使用成熟开源库,避免安全漏洞
- 密码哈希算法优先用sha256及以上强度,不要使用md5、sha1等已经被破解的弱算法
- AES密钥绝对不要硬编码在代码里,必须存在系统安全存储中
内容的提问来源于stack exchange,提问作者Terin Tittu
相关产品推荐
相关产品推荐

