安全密码存储,该怎么做,不该怎么做?

2024-05-01 16:38

本文主要是介绍安全密码存储,该怎么做,不该怎么做?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作为软件开发者,其中最重要的一个责任就是保护用户的个人信息,如果用户没有相关的技术知识,他们在使用我们的服务的时候别无选择只能信任我们。可惜的是,当我们调查关于密码的处理的时候,我们发现有各种不同的处理方式,而这些方式有很多都不安全。虽然构建一个完全安全的系统是不可能的,但是我们可以通过一些简单的步骤让我们的密码存储足够安全。

不应该:

首先让我们看看当我们构建一个需要用户认证的系统的时候不应该怎么做。
  • 在不得已的时候不要自己存储用户的认证信息。我们可以考虑使用OAuth的提供者例如Google、Facebook。如果构建企业内部的应用,可以考虑使用已有的内部认证服务,例如企业LDAP或者Kerberos服务。无论是面向公众的还是面向内部的应用程序,用户会喜欢这个应用,因为他不需要多记住一个ID和密码,同时也少了受黑客攻击的危险。
  • 如果你必须存储认证信息,不要存储明文密码。这句话就不解释了。
  • 不要使用可逆的加密方式,除非你在某种状况下真的需要查出来明文密码。因为在进行用户身份验证的时候并不需要明文密码去比对。
  • 不要使用过时的哈希算法,例如md5,在现在这个社会,有人可以通过构建一个超大的md5库来反向的查询出明文。换句话说md5哈希基本上没什么用,你要是不相信可以拿这个密文(569a70c2ccd0ac41c9d1637afe8cd932)去 http://www.md5hacker.com/ 上看看,几秒内就可以查出明文了。

应该:

说完了不应该做的,就说说应该做的:
  • 选择一个单向(不可逆)的加密算法。就像我上面说的一样,仅仅存储加密后的用户密码,用户每次认证就使用相同的算法加密后比对就可以了。
  • 选择一个你的应用可以承受的最慢的加密算法。任何现代的加密算法都支持在加密的时候接受参数从而使加密时间延长,而解密也自然就更难。(例如PBKDF2,可以通过制定迭代的次数来实现)。为什么慢了好呢?因为用户几乎不会关心他为了认证自己的账户额外的花销了100ms。但是黑客就不同了,当他进行上10亿次的尝试计算的时候,就有他喝一壶的了。
  • 选择一个流行的算法。美国国家标准与技术研究院推荐使用PBKDF2加密密码。

PBKDF2

在我给出示例代码前,让我们先来看看PBKDF2算法。
  • 美国国家标准与技术研究院推荐。
  • 可以通过调整key来扩展,从而避免暴力破解。通过key扩展的基本思路是,在将密码哈希后,再使用key加上哈希值再使用相同的算法进行多次的哈希。如果黑客尝试去破解的话,他会因此多花费几十亿次计算的时间。前面提到过,越慢越好,PBKDF2可以通过指定迭代次数,你想让他多慢,他就有多慢。
  • 通过加盐的方式预防彩虹表的破解方式。盐是一个添加到用户的密码哈希过程中的一段随机序列。这个机制能够防止通过预先计算结果的彩虹表破解。每个用户都有自己的盐,这样的结果就是即使用户的密码相同,通过加盐后哈希值也将不同。然而,在将盐与密文存储的位置上有很多矛盾的地方,有的时候将两者存在一起比较方便,有的时候为了安全考虑又不得不将两者分开存储。由于PBKDF2算法通过key的机制避免了暴力破解,我觉得没必要将盐隐藏起来,就跟密文存储在同一个位置。
  • 不需要额外的库或者工具,这是一个开源的实现,在工作环境中能很方便的使用。

最后让我们来看一个例子

这里使用PBKDF2加密的Java代码,仅仅依赖Java SE 6.
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;public class PasswordEncryptionService {public boolean authenticate(String attemptedPassword, byte[] encryptedPassword, byte[] salt)throws NoSuchAlgorithmException, InvalidKeySpecException {// Encrypt the clear-text password using the same salt that was used to// encrypt the original passwordbyte[] encryptedAttemptedPassword = getEncryptedPassword(attemptedPassword, salt);// Authentication succeeds if encrypted password that the user entered// is equal to the stored hashreturn Arrays.equals(encryptedPassword, encryptedAttemptedPassword);}public byte[] getEncryptedPassword(String password, byte[] salt)throws NoSuchAlgorithmException, InvalidKeySpecException {// PBKDF2 with SHA-1 as the hashing algorithm. Note that the NIST// specifically names SHA-1 as an acceptable hashing algorithm for PBKDF2String algorithm = "PBKDF2WithHmacSHA1";// SHA-1 generates 160 bit hashes, so that's what makes sense hereint derivedKeyLength = 160;// Pick an iteration count that works for you. The NIST recommends at// least 1,000 iterations:// http://csrc.nist.gov/publications/nistpubs/800-132/nist-sp800-132.pdf// iOS 4.x reportedly uses 10,000:// http://blog.crackpassword.com/2010/09/smartphone-forensics-cracking-blackberry-backup-passwords/int iterations = 20000;KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, derivedKeyLength);SecretKeyFactory f = SecretKeyFactory.getInstance(algorithm);return f.generateSecret(spec).getEncoded();}public byte[] generateSalt() throws NoSuchAlgorithmException {// VERY important to use SecureRandom instead of just RandomSecureRandom random = SecureRandom.getInstance("SHA1PRNG");// Generate a 8 byte (64 bit) salt as recommended by RSA PKCS5byte[] salt = new byte[8];random.nextBytes(salt);return salt;}
}

流程是这样:

  1. 当增加一个用户的时候,调用generateSalt()生成盐,然后调用getEncryptedPassword(),同时存储盐和密文。再次强调,不要存储明文密码,不要存储明文密码,因为没必要!不要担心将盐和密文存储在同一张表中,上面已经说过了,这个无关紧要。
  2. 当认证用户的时候,从数据库中取出盐和密文,将他们和明文密码同时传给authenticate(),根据返回结果判断是否认证成功。
  3. 当用户修改密码的时候,仍然可以使用原来的盐,只需要调用getEncryptedPassword()方法重新生成密文就可以了。
参考文档:
  • NIST:Special Publication 800-132
  • 维基百科:PBKDF2
  • 维基百科: Salt (cryptography)
  • 维基百科: Rainbow Table Attacks

这篇关于安全密码存储,该怎么做,不该怎么做?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/952164

相关文章

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

MySQL 存储引擎 MyISAM详解(最新推荐)

《MySQL存储引擎MyISAM详解(最新推荐)》使用MyISAM存储引擎的表占用空间很小,但是由于使用表级锁定,所以限制了读/写操作的性能,通常用于中小型的Web应用和数据仓库配置中的只读或主要... 目录mysql 5.5 之前默认的存储引擎️‍一、MyISAM 存储引擎的特性️‍二、MyISAM 的主

Linux lvm实例之如何创建一个专用于MySQL数据存储的LVM卷组

《Linuxlvm实例之如何创建一个专用于MySQL数据存储的LVM卷组》:本文主要介绍使用Linux创建一个专用于MySQL数据存储的LVM卷组的实例,具有很好的参考价值,希望对大家有所帮助,... 目录在Centos 7上创建卷China编程组并配置mysql数据目录1. 检查现有磁盘2. 创建物理卷3. 创

Mac备忘录怎么导出/备份和云同步? Mac备忘录使用技巧

《Mac备忘录怎么导出/备份和云同步?Mac备忘录使用技巧》备忘录作为iOS里简单而又不可或缺的一个系统应用,上手容易,可以满足我们日常生活中各种记录的需求,今天我们就来看看Mac备忘录的导出、... 「备忘录」是 MAC 上的一款常用应用,它可以帮助我们捕捉灵感、记录待办事项或保存重要信息。为了便于在不同

SpringBoot如何对密码等敏感信息进行脱敏处理

《SpringBoot如何对密码等敏感信息进行脱敏处理》这篇文章主要为大家详细介绍了SpringBoot对密码等敏感信息进行脱敏处理的几个常用方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录​1. 配置文件敏感信息脱敏​​2. 日志脱敏​​3. API响应脱敏​​4. 其他注意事项​​总结

JavaScript实战:智能密码生成器开发指南

本文通过JavaScript实战开发智能密码生成器,详解如何运用crypto.getRandomValues实现加密级随机密码生成,包含多字符组合、安全强度可视化、易混淆字符排除等企业级功能。学习密码强度检测算法与信息熵计算原理,获取可直接嵌入项目的完整代码,提升Web应用的安全开发能力 目录

使用Python实现调用API获取图片存储到本地的方法

《使用Python实现调用API获取图片存储到本地的方法》开发一个自动化工具,用于从JSON数据源中提取图像ID,通过调用指定API获取未经压缩的原始图像文件,并确保下载结果与Postman等工具直接... 目录使用python实现调用API获取图片存储到本地1、项目概述2、核心功能3、环境准备4、代码实现

SpringBoot项目中Redis存储Session对象序列化处理

《SpringBoot项目中Redis存储Session对象序列化处理》在SpringBoot项目中使用Redis存储Session时,对象的序列化和反序列化是关键步骤,下面我们就来讲讲如何在Spri... 目录一、为什么需要序列化处理二、Spring Boot 集成 Redis 存储 Session2.1

使用Java实现Navicat密码的加密与解密的代码解析

《使用Java实现Navicat密码的加密与解密的代码解析》:本文主要介绍使用Java实现Navicat密码的加密与解密,通过本文,我们了解了如何利用Java语言实现对Navicat保存的数据库密... 目录一、背景介绍二、环境准备三、代码解析四、核心代码展示五、总结在日常开发过程中,我们有时需要处理各种软

基于MongoDB实现文件的分布式存储

《基于MongoDB实现文件的分布式存储》分布式文件存储的方案有很多,今天分享一个基于mongodb数据库来实现文件的存储,mongodb支持分布式部署,以此来实现文件的分布式存储,需要的朋友可以参考... 目录一、引言二、GridFS 原理剖析三、Spring Boot 集成 GridFS3.1 添加依赖