You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP与Flutter中SHA-256加盐哈希结果不一致求助

Flutter SHA-256加盐哈希与PHP不一致的解决方案

我在登录验证时遇到Flutter的crypto包计算SHA-256加盐哈希结果与PHP/MySQL不一致的问题:

  • 密码:Admin1234
  • 盐:由PHP random_bytes()生成的二进制数据(显示为乱码????@???R????k]?????$I"?fmP)
  • PHP计算结果:d0a09d26a26196c8609d9f8ffd8a671ae0124752eea7bf338ca54f6f7f1b5e3e
  • Flutter计算结果:df2a65dc4abd86afad3a6b0e30ba217fb881c23e259a3072c09480525a79216d

问题根源

PHP的random_bytes()生成的是原始二进制数据,现有Flutter代码存在两个关键问题:

  1. 从数据库读取salt时,将二进制数据当成字符串处理,不可打印字符被转义/乱码,丢失了原始字节信息
  2. 使用Latin1Encoder()转换拼接后的字符串,但PHP是直接将密码字符串与二进制salt按原始字节拼接计算哈希,两者字节序列完全不匹配

解决方案

方案1:改用Base64编码存储盐(推荐,避免二进制存储的编码问题)

先修改PHP代码,生成盐时转成Base64字符串存储,验证时再解码回二进制:

<?php
class Hash{
    public static function make($string, $salt = ''){
        // 将存储的Base64盐解码回二进制
        $saltBytes = base64_decode($salt);
        return hash('sha256', $string . $saltBytes);
    }
    
    public static function salt($length){
        // 生成二进制盐后转成Base64字符串,方便存储和传输
        return base64_encode(random_bytes($length));
    }
    
    public static function unique(){
        return self::make(uniqid());
    }
}
?>

再修改Flutter代码,读取Base64盐并解码,与密码字节拼接后计算哈希:

import 'dart:convert';
import 'package:crypto/crypto.dart';

void _login(){
    db.getConnection().then((conn){
      String sql = 'SELECT salt FROM user WHERE email = "${username.text}"';
      conn.query(sql).then((results){
        if(results.isNotEmpty){
          // 读取存储的Base64盐字符串
          String saltBase64 = results.first[0];
          // 解码成原始二进制字节
          Uint8List saltBytes = base64Decode(saltBase64);

          String password = _password.text;
          // 将密码转成UTF-8字节(PHP默认用UTF-8处理字符串)
          Uint8List passwordBytes = utf8.encode(password);
          // 拼接密码字节和盐字节
          Uint8List saltedBytes = Uint8List.fromList([...passwordBytes, ...saltBytes]);
          var digest = sha256.convert(saltedBytes);

          print(digest); // 现在结果与PHP一致
        }
        else
          print('login failed');
      });
    });
  }

方案2:直接存储二进制盐(需确保Flutter读取到原始二进制)

如果数据库中已存储二进制盐(比如BLOB类型),Flutter需要直接读取原始Uint8List而非字符串:

import 'dart:convert';
import 'package:crypto/crypto.dart';

void _login(){
    db.getConnection().then((conn){
      String sql = 'SELECT salt FROM user WHERE email = "${username.text}"';
      conn.query(sql).then((results){
        if(results.isNotEmpty){
          // 直接获取二进制盐(需确保数据库驱动返回Uint8List,比如mysql1库对BLOB的处理)
          Uint8List saltBytes = results.first[0];

          String password = _password.text;
          Uint8List passwordBytes = utf8.encode(password);
          Uint8List saltedBytes = Uint8List.fromList([...passwordBytes, ...saltBytes]);
          var digest = sha256.convert(saltedBytes);

          print(digest);
        }
        else
          print('login failed');
      });
    });
  }

关键说明

  • PHP的hash()函数处理字符串时默认用UTF-8编码转字节,所以Flutter中要用utf8.encode()而非Latin1Encoder()
  • 二进制数据存储或传输时,优先用Base64编码转成字符串,避免因编码转换丢失原始字节信息

内容的提问来源于stack exchange,提问作者Muhammad Amiruddin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 23:50:23