博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计安全的账号系统
阅读量:6284 次
发布时间:2019-06-22

本文共 3832 字,大约阅读时间需要 12 分钟。

设计一个安全的账号系统,很重要的一个方面就是如何保护用户的密码。保护用户密码最简单的方式就是使用带盐的密码hash(salted password hashing)。

具体的操作就是给密码加一个随机的前缀或者后缀,然后再进行hash。这个随机的后缀或者前缀成为“盐”。通过加盐,相同的密码每次hash都是完全不一样的字符串了。检查用户输入的密码是否正确的时候,我们也还需要这个盐,所以盐一般都是跟hash一起保存在数据库里,或者作为hash字符串的一部分。

如果需要达到更高的安全等级,可以考虑将salt值和最终hash结果存在不同的数据库。

比较加盐hash结果时,注意使用时间恒定的比较函数。

普通的情况下,在比较两个字符串时,函数是一个字符一个字符进行比较,如果某个字符不匹配就会立即返回。攻击者可以根据验证的时间长短来判断前几位字符是否正确,然后逐步修正最终得到正确的结果。

因此,在比较 hash 时,使用时间恒定的比较函数,可以让攻击者摸不着头脑。

具体实现如下:

using System;using System.Text;using System.Security.Cryptography; namespace PasswordHash{    ///     /// Salted password hashing with PBKDF2-SHA1.     ///     public class PasswordHash    {        // The following constants may be changed without breaking existing hashes.        public const int SALT_BYTE_SIZE = 24;        public const int HASH_BYTE_SIZE = 24;        public const int PBKDF2_ITERATIONS = 1000;         public const int ITERATION_INDEX = 0;        public const int SALT_INDEX = 1;        public const int PBKDF2_INDEX = 2;         ///         /// Creates a salted PBKDF2 hash of the password.        ///         /// The password to hash.        /// 
The hash of the password.
public static string CreateHash(string password) { // Generate a random salt RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider(); byte[] salt = new byte[SALT_BYTE_SIZE]; csprng.GetBytes(salt); // Hash the password and encode the parameters byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE); return PBKDF2_ITERATIONS + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash); } /// /// Validates a password given a hash of the correct one. /// /// The password to check. /// A hash of the correct password. ///
True if the password is correct. False otherwise.
public static bool ValidatePassword(string password, string correctHash) { // Extract the parameters from the hash char[] delimiter = { ':' }; string[] split = correctHash.Split(delimiter); int iterations = Int32.Parse(split[ITERATION_INDEX]); byte[] salt = Convert.FromBase64String(split[SALT_INDEX]); byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]); byte[] testHash = PBKDF2(password, salt, iterations, hash.Length); return SlowEquals(hash, testHash); } /// /// Compares two byte arrays in length-constant time. This comparison /// method is used so that password hashes cannot be extracted from /// on-line systems using a timing attack and then attacked off-line. /// /// The first byte array. /// The second byte array. ///
True if both byte arrays are equal. False otherwise.
private static bool SlowEquals(byte[] a, byte[] b) { uint diff = (uint)a.Length ^ (uint)b.Length; for (int i = 0; i < a.Length && i < b.Length; i++) diff |= (uint)(a[i] ^ b[i]); return diff == 0; } /// /// Computes the PBKDF2-SHA1 hash of a password. /// /// The password to hash. /// The salt. /// The PBKDF2 iteration count. /// The length of the hash to generate, in bytes. ///
A hash of the password.
private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes) { Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt); pbkdf2.IterationCount = iterations; return pbkdf2.GetBytes(outputBytes); } }}

 

转载于:https://www.cnblogs.com/wangyunjie/p/6064113.html

你可能感兴趣的文章
PHP字符编码转换类3
查看>>
rsync同步服务配置手记
查看>>
http缓存知识
查看>>
Go 时间交并集小工具
查看>>
iOS 多线程总结
查看>>
webpack是如何实现前端模块化的
查看>>
TCP的三次握手四次挥手
查看>>
关于redis的几件小事(六)redis的持久化
查看>>
package.json
查看>>
webpack4+babel7+eslint+editorconfig+react-hot-loader 搭建react开发环境
查看>>
Maven 插件
查看>>
初探Angular6.x---进入用户编辑模块
查看>>
计算机基础知识复习
查看>>
【前端词典】实现 Canvas 下雪背景引发的性能思考
查看>>
大佬是怎么思考设计MySQL优化方案的?
查看>>
<三体> 给岁月以文明, 给时光以生命
查看>>
Android开发 - 掌握ConstraintLayout(九)分组(Group)
查看>>
springboot+logback日志异步数据库
查看>>
Typescript教程之函数
查看>>
Android 高效安全加载图片
查看>>