设计安全的账号系统

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

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

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

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

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

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

具体实现如下:

using System;
using System.Text;
using System.Security.Cryptography;

namespace PasswordHash
{
    /// <summary>
    /// Salted password hashing with PBKDF2-SHA1.    /// </summary>
    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;

        /// <summary>
        /// Creates a salted PBKDF2 hash of the password.
        /// </summary>
        /// <param name="password">The password to hash.</param>
        /// <returns>The hash of the password.</returns>
        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);
        }

        /// <summary>
        /// Validates a password given a hash of the correct one.
        /// </summary>
        /// <param name="password">The password to check.</param>
        /// <param name="correctHash">A hash of the correct password.</param>
        /// <returns>True if the password is correct. False otherwise.</returns>
        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);
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="a">The first byte array.</param>
        /// <param name="b">The second byte array.</param>
        /// <returns>True if both byte arrays are equal. False otherwise.</returns>
        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;
        }

        /// <summary>
        /// Computes the PBKDF2-SHA1 hash of a password.
        /// </summary>
        /// <param name="password">The password to hash.</param>
        /// <param name="salt">The salt.</param>
        /// <param name="iterations">The PBKDF2 iteration count.</param>
        /// <param name="outputBytes">The length of the hash to generate, in bytes.</param>
        /// <returns>A hash of the password.</returns>
        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);
        }
    }
}
时间: 2024-08-11 03:38:22

设计安全的账号系统的相关文章

设计安全的账号系统的正确姿势【转】

引子 最近有个虚拟练习项目,涉及到系统安全保障的设计,于是对安全保障这块做了一些更深入的了解.发现了很多有趣的东西,开阔了眼界.中间查了一些资料,于是我打算重新整理,用更加循序渐进,大家都能懂的方式,说一说如何设计一个安全的系统. 著名的安全事件 首先来看看最近几年比较著名的拖库撞库后密码泄露的事件: 2011年12月,国内最大的程序员社区 CSDN 遭拖库,600万个账户信息泄露. 2014年3月,携程旅行网的系统存技术漏洞,漏洞可能导致用户的姓名.身份证号码.银行卡类别.银行卡卡号.银行卡C

设计安全的账号系统的正确姿势

引子 最近有个虚拟练习项目,涉及到系统安全保障的设计,于是对安全保障这块做了一些更深入的了解.发现了很多有趣的东西,开阔了眼界.中间查了一些资料,于是我打算重新整理,用更加循序渐进,大家都能懂的方式,说一说如何设计一个安全的系统. 著名的安全事件 首先来看看最近几年比较著名的拖库撞库后密码泄露的事件: 2011年12月,国内最大的程序员社区 CSDN 遭拖库,600万个账户信息泄露. 2014年3月,携程旅行网的系统存技术漏洞,漏洞可能导致用户的姓名.身份证号码.银行卡类别.银行卡卡号.银行卡C

一步一步教你在 Android 里创建自己的账号系统(二)--同步数据以及设计账号页面

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 在前一篇文章中(一步一步教你在 Android 里创建自己的账号系统(一)),我向大家介绍了如何在 Android 系统中创建自己的账户系统,接下来我会向大家详细介绍一下如何使用账户系统. (一)同步数据 通常而言,我们会在两种情况下使用我们的账号系统: (1)登陆验证 登陆验证其实是一个很实用的功能,试

如何设计一个单点登录系统(3)?

在上一篇文章 如何设计一个单点登录系统(2)? 中主要讲解了可跨域SSO系统服务端,客户端在登录,登出过程中分别应该承担的职责,本文将重点聊一下具体技术实现,源码地址: https://github.com/zhoudapeng/zsso 首先聊服务端的实现,毕竟服务端是整个单点登录系统的大脑 提供登录页,这个是登录的基础,所有的接入方在发现当前用户未登录的情况下都会重定向到sso服务端的登录页,服务端的逻辑如下: sso服务端登录页逻辑 这里服务端需要做个判断: 如果当前登录存在.sso.co

设计和开发ETL系统(一)&mdash;&mdash;ETL过程综述

在这部分将按照设计和实现ETL系统的流程展开,将上一个部分的那些子系统按照提取数据.清洗和一致化.向呈现服务器提交以及管理ETL环境等四个方面进行了分类.(是不是说对ETL主要就是掌握这四个方面的内容) ETL处理步骤 提取数据 清洗和一致化 向呈现层提交 管理ETL环境 计划 创建一个高层的.单页的源到目标示意流程 * 测试.选择和实现一种ETL工具 为维度管理.错误处理和其他有关过程设计默认的策略 * * * 通过目标表向下钻取,给出任何复杂数据重组或转换的图形示意,并且设计初步的作业序列

一步一步教你在 Android 里创建自己的账号系统(一)

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 大家在平时使用 Android 手机的时候,都会发现有些应用(例如 qq,微信,淘宝)为自己创建了账号系统,并且能够在设置页面看到他,可是当自己希望为自己的软件写一个账号系统的时候总是不知从何入手,现在我们就从头开始,一步一步打造属于自己应用的账号系统. 在进行设备账户管理的时候,我们会通过一个 Acco

Meteor:用户账号管理添加密码和微博weibo账号系统支持

Meteor账户系统构建与accounts-base包之上,并为publish和methods提供userId的顶层支持.核心包提供的功能有:数据库中的用户记录支持:额外的包提供密码安全验证:第三方登录服务整合:和预置的用户接口. 1.安装password包 通过下面命令在meteor账号系统中添加password支持: meteor add accounts-password 系统提示已安装包有: accounts-password npm-bcrypt sha srp 2.安装weibo包

使用OpenLDAP构建基础账号系统-01

LDAP - Lightweight Directory Access Protocol,对该协议的具体应用,常见的是微软的Active Directory服务和Linux上的OpenLDAP组件. 实际应用中,我们则是基于OpenLDAP(简称OL)来构建Linux和相关Web应用的账号系统,以满足日常的需求,这里仅对架构和实现配置做简要分享,将会对安全相关的策略和配置做较为详细的说明. 环境: OL服务端:RHEL6.2+openldap-servers-2.4.23 OL客户端:RHEL5

如何设计一个单点登录系统(2)?

如何设计一个单点登录系统(1)? 中,我着重从单点登录系统的由来,单点登录的概念,一个好的单点登录系统应该具备的特点以及本人亲自实现的一个单点登录系统几方面做了些介绍,这篇文章主要介绍下具体实现方案. 1.1 传统SSO实现方案-登录 1.2 传统SSO实现方案-登出 图1.1为传统SSO实现方案中登录流程时序图 图1.2为传统SSO实现方案中登出流程时序图 接下来给大家解释解决跨域问题的方案: 2.1 支持跨域的SSO实现方案-登录 2.2 支持跨域的SSO实现方案-登出 图2.1为支持跨域的