使用md5盐值加密存储密码

密码的使用涉及到两个方面,一是传输,二是存储,这两个场景,都需要使用加密后的密码进行传输。

使用方式

密码传输的方式有两种,明文和加密:

  1. 明文传输

    优势是客户端不需要加密,直接传递到服务端

    劣势是如果使用http传输而不是https,被抓包时容易捕捉到。

  2. 密文传输

    优势是安全性高

    劣势是需要两端都保存密钥,使用和管理都有一定的复杂度

存储方式有很多种,大概可以分为:

  1. 明文存储

    优势是简单,而且支持密码找回

    劣势是不安全,数据库内的信息被盗,可能导致大量用户信息泄露

  2. 密文传输

    优势是安全性高,使用相同的加密方式即可实现验证

    劣势是管理密钥有成本,无法找回密码,只能重置

  3. 通过md5进行加密

    优势是使用简单

    劣势是如果密码本身很常见,则加密后比较容易破解,无法找回密码,只能重置

  4. 通过md5盐值加密

    优势是使用简单

    劣势是需要将盐值一并存储,无法找回密码,只能重置

实现

传输过程由于涉及到第三方登录,如果在有证书的前提下,使用https证书进行传输数据的加密。

并且通过获取服务端的公钥,进行密码加密,将加密后的密码和公钥都传给服务端,让服务端进行解密。单单说传输方式,这种方式是最为安全的。即使被抓包也无法获取密码。

如果需要对接第三方,客户端不一定能维护密钥,无论是对称加密的密钥还是非对称加密的密钥,不一定能以这种方式传输密码。可能传输明文,也可能传输加密后的密码。在创建用户、更新密码和登录时,可能使用不同的方式加密方式。同时,在服务端进行解密,服务端存储密码时,再以服务端的加密方式进行存储。

如何加密传输和存储用户密码

传输和存储的加密,可以有效确保密码的安全性。

对于传输过程,比较好的方案是客户端通过某一种特定的方式传输,最好的方案是盐值加密,在存储密码时,同时存储一串随机字符串。

md5

md5(Message-Digest Algorithm)特点:

  • 输入任意长度的信息,经过处理,输出为128位的信息(数字指纹)
  • 不同的输入得到的不同的结果(唯一性)
  • 相同的输入,得到的结果相同
1
2
3
4
5
func genMD5(code string) string {
h := md5.New()
_, _ = io.WriteString(h, code)
return hex.EncodeToString(h.Sum(nil))
}

由于相同的输入,可以得到相同的结果,如果结果存储的非常多,可以出现暴力破解的情况。

盐值加密

在md5的基础上,增加一串随机字符串,一起加密,让结果无法预测。

这个随机字符串的生成方式有很多种,可以直接通过uuid生成,或者通过时间戳获取纳秒的位数等,这里使用一个封装好的包

1
go get github.com/anaskhan96/go-password-encoder

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
// Using the default options
salt, encodedPwd := password.Encode("generic password", nil)
check := password.Verify("generic password", salt, encodedPwd, nil)
fmt.Println(check) // true

// Using custom options 盐值长度 迭代次数 密码长度 hash函数
options := &password.Options{10, 10000, 16, sha256.New}
salt, encodedPwd = password.Encode("generic password", options)
fmt.Println(salt)
// niasTEh6l3
fmt.Println(encodedPwd)
// 0c4f9de6c789d3ab07d17567937a3a2a
check = password.Verify("generic password", salt, encodedPwd, options)
fmt.Println(check) // true
}

优化使用,将加密方式、盐值、加密后密码一并存储

1
2
3
4
5
6
7
8
9
options := &password.Options{10, 10000, 16, sha256.New}
salt, encodedPwd = password.Encode("generic password", options)

pwd := fmt.Sprintf("pbkdf2-sha256$%s$%s", salt, encodedPwd)
//savePWDtoDB(pwd)

// check passowrd
p := strings.Split(pwd, "$")
return password.Verify("generic password", p[1], p[2], options)