前后端登录时密码的加密

前后端登录时密码的加密

概述

在一次 http 请求中,前后端如何安全地传输密码呢?服务器端如何安全存储密码呢?

一、明文传输、存储

1、大致步骤

用户:提供用户名和密码。

前端:密码明文传输。

后端:用户注册时,明文存储密码;

用户登录时,①读取数据库中密码,②与接收的密码进行比较以验证登录。

2、优缺点

没任何安全性可言,裸奔密码。请求被抓包,密码即暴露;数据库泄露,密码即暴露。

二、单向哈希后传输、存储

1、大致步骤

用户:提供用户名和密码。

前端:密码经单向哈希后再发送给后端服务器。

后端:用户注册时,存储密码的哈希值;

用户登录时,①读取数据库中哈希值,②与接收的密码哈希值进行比较以验证登录。

2、优缺点

相比明文传输、存储,这个方式有了最基础安全防范。但密码的单向哈希,且存储时未加盐。密码容易被暴力穷举检索出来,如彩虹表攻击。现在主流的彩虹表都是100G以上了。

三、加盐存储

1、大致步骤

用户:提供用户名和密码。

前端:密码经单向哈希后再发送给后端服务器。

后端:用户注册时,①生成随机盐,加入到接收的哈希值中,经单向哈希后生成哈希值2;②存储盐值、哈希值2;

用户登录时,①读取数据库中盐值,加入到接收的密码哈希值中,经单向哈希后生成哈希值2;②与库中哈希值2进行比较以验证登录。

2、优缺点

密码加盐可以有效地防止彩虹表攻击。即使攻击者知道了盐的内容和加盐的位置,加盐仍然大大增加了利用彩虹表攻击的难度。

这种方式的优点是,即使数据库泄露,密码仍然具有一定的安全性。

缺点:多次请求,传输的哈希值总是固定的,并没有“加盐”保护。由于http请求的特性,不能直接对密码“加盐”,需要引入非对称加密。

3.代码实现

前端:

import CryptoJS from 'crypto-js';

export default {

encryptPassword(username, password){

//password 以utf-8编码,经SHA 256散列计算,返回散列值的十六进制表示

const hash = CryptoJS.SHA256(password).toString();

return {username:username,password:hash};

}

}

View Code

后端:.net core 7

验证登录:

using Microsoft.AspNetCore.Mvc;

using System.Security.Cryptography;

using System.Text;

[Route("api/[controller]")]

[ApiController]

public class AuthenController : ControllerBase

{

//...

[HttpPost]

[Route("login")]

public ResponseResult Login([FromBody] LoginModel loginModel)

{

//...

if (string.IsNullOrEmpty(user.Password) || string.IsNullOrEmpty(user.Salt))

{

return false;

}

//读取前端传来的密码

byte[] password = Convert.FromHexString(loginModel.Password);

//加盐

byte[] salt = Convert.FromBase64String(user.Salt);

byte[] combined = new byte[password.Length + salt.Length];

Buffer.BlockCopy(password, 0, combined, 0, password.Length);

Buffer.BlockCopy(salt, 0, combined, password.Length, salt.Length);

var targetPassword = SHA256.HashData(combined);

string passwordFromInput = Convert.ToBase64String(targetPassword);

//验证登录

bool valid = string.Compare(passwordFromInput, user.Password, true) == 0;

//...

}

}

public class LoginModel

{

public string Username { get; set; } = null!;

public string Password { get; set; } = null!;

}

View Code

注册账号:(使用默认密码123)

using Microsoft.AspNetCore.Mvc;

using Microsoft.EntityFrameworkCore;

using System.Security.Cryptography;

using System.Text;

[ApiController]

[Route("api/[controller]")]

public class UserController : ControllerBase

{

[HttpPost("[action]")]

public ResponseResult AddUser([FromBody] UserInfo userinfo)

{

//...

string password = PasswordHelper.Encrypt("123", out string saltValue);

User user = new()

{

Id = Guid.NewGuid().ToString(),

UserName = userinfo.UserName,

RealName = userinfo.RealName,

//...

Password = password,

Salt = saltValue

};

_appContext.Users.Add(user);

_appContext.SaveChanges();

return new ResponseResult();

}

}

public class PasswordHelper

{

public static string Encrypt(string plaintext, out string saltValue)

{

//plaintext="123"

var bPlaintext = Encoding.UTF8.GetBytes(plaintext);

var password = SHA256.HashData(bPlaintext);

//生成随机盐

var salt = GetRandomSalt();

saltValue = Convert.ToBase64String(salt);//编码为base64

//加盐

byte[] combined = new byte[password.Length + salt.Length];

Buffer.BlockCopy(password, 0, combined, 0, password.Length);

Buffer.BlockCopy(salt, 0, combined, password.Length, salt.Length);

var targetPassword = SHA256.HashData(combined);

return Convert.ToBase64String(targetPassword);

}

///

/// 获取随机盐值

///

public static byte[] GetRandomSalt()

{

return RandomNumberGenerator.GetBytes(16);

}

}

View Code

前端对密码明文,使用utf-8编码,哈希加密后,以十六进制字符串表示;

后端验证登录时,从以十六进制表示的字符串开始,生成 byte[],从而得到前端传来的哈希值。

后端注册账号时,从生成随机盐开始,进行密码哈希值加盐,再哈希计算生成哈希值2,将哈希值2转为base64字符串存入数据库中(盐值亦是)。

共同点:(1)密码明文的编码方式:在注册、登录时,或设置默认密码时,前后端对密码明文都使用utf-8编码;

(2)出入数据库时的编码方式:盐值、加盐后密码都是以 base64 编码字符串【写入或读出】数据库。

(3)加盐的方式。

四、非对称加密后传输后,加盐存储

基础版

1、大致步骤

用户:提供用户名和密码。

前端:注册账号或登录时,①生成随机值;②密码经单向哈希后,与生成的随机值一起,经公钥加密后,生成密文,③将密文发送给后端服务器。

后端:用户注册时,①使用私钥解密密文,丢掉随机值,得到哈希值;②生成随机盐,加入到哈希值中,经单向哈希后生成哈希值2;③存储盐值、哈希值2;

用户登录时,①使用私钥解密密文,丢掉随机值,得到哈希值;②读取数据库中盐值,加入到哈希值中,经单向哈希后生成哈希值2;③与库中哈希值2进行比较以验证登录。

2、优缺点

引入随机值,每次传输的密文不同。但RSA加密对于其加密的明文的长度有限制。同时,非对称加解密需要一些开销。另外,还需要维护公钥。

改进版

1、大致步骤

用户:提供用户名和密码。

前端:注册账号或登录时,①随机生成对称加密密钥;②密码经单向哈希后,进行对称加密,生成密文;③随机生成的对称加密密钥经公钥加密后,生成签名;④将密文、签名一起发送给后端服务器。

后端:用户注册时,①使用私钥解密签名,得到对称加密密钥;②使用对称密钥解密密文,得到哈希值;③生成随机盐,加入到哈希值中,经单向哈希后生成哈希值2;④存储盐值、哈希值2;

用户登录时,①使用私钥解密签名,得到对称加密密钥;②使用对称密钥解密密文,得到哈希值;③读取数据库中盐值,加入到哈希值中,经单向哈希后生成哈希值2;④与库中哈希值2进行比较以验证登录。

2、优缺点

此方式虽然仍需要维护公钥,但传输的数据将不再有限制(即使是https,也需要付出维护公钥的成本)。

缺点是:开销进一步加大。

3.题外话

可将要传输的“密码的哈希值”换成任何你想要的数据,以达到安全传输数据的目的。

此方式为https的雏形,与https相比,区别在于:①此方式并不验证公钥,②签名和加密后密文一起被发送到后端服务器,③用于加密数据的“对称加密的密钥”此后不再使用。

而 https 通过2次请求完成对公钥的验证以及对“对称加密的密钥”的约定,之后才发送数据,且继续使用密钥:

(1)第一次请求:服务器返回含公钥的证书;客户端验证证书(与本地的证书进行比较),决定是否发起第二次请求;

(2)第二次请求:客户端使用公钥加密“对称加密的密钥”,再发送给服务器,以约定之后使用的“对称加密的密钥”。

——后续的请求与返回,使用约定好的对称加密的密钥来加密要传输的数据【将继续使用对称加密】

六、总结

对用户名和密码的验证,是其他登录方式的基础。

七、其他

如有错漏,欢迎指正。

相关推荐

OPPOr9大概多少钱
365best官网

OPPOr9大概多少钱

📅 08-21 👁️ 2098
国力震撼世界,古代最强大的10个帝国,中国4个朝代上榜
身份证号码归属地查询(批量免费查询)
365bet网络娱乐

身份证号码归属地查询(批量免费查询)

📅 07-14 👁️ 7385
毫米与像素换算
365best官网

毫米与像素换算

📅 07-14 👁️ 1009
如何在Word文档中将两个或多个表格合并为一个
365bet网络娱乐

如何在Word文档中将两个或多个表格合并为一个

📅 08-13 👁️ 1661
将 Github Pages 个人博客录入搜索引擎(以 Bing 为例)