waniwanisan
@waniwanisan

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

データベースに保存されているハッシュ化されたパスワードからログイン機能を動かしたい

解決したいこと

現在、ログイン機能(メールアドレスとパスワード記述)を実装しようとしています。
ログイン機能の前に新規登録機能を実装しましたがその際postgres接続でテーブル名は"user"ですがカラムにパスワードがありますが登録時、ハッシュ化しています。
ログイン機能実装時にテキストボックスに記述されたパスワードを取得し、それを新規登録と同じ要領でハッシュ化すればデータベースに登録されているハッシュ化パスワードと一致するので比較してtrueを走らせればよいと考えていましたがハッシュ化された値は違うものでした。
調べると同じパスワードでも違う値を返すこと、ハッシュ化された文字列はもとには戻せないことを知りました。ログイン機能を実装するにはどのようにすればよいですか?

例)
ログインが成功すればページ遷移できる仕様

該当するソースコード

登録機能のコントローラー(アクション名はCreate)

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using TestWebApplication.Models;
using User.Models;
using MyConnection.Commons;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using System.Security.Cryptography;

namespace TestWebApplication.Controllers
{
    public class RegistController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }


        [HttpPost] //regist.cshtmlのformタグ内から変数を持ってきている
        public IActionResult Create(UserList model)
        {
            Console.WriteLine(model.Password);
            Console.WriteLine(model.ConfirmPassword);
            Console.WriteLine(model.MailAddress);
            Console.WriteLine(model.Name);

            if (ModelState.IsValid)
            {
                Console.WriteLine("成功!!");
            }
            else
            {
                return View("Index");
            }
            //参考文献https://www.atmarkit.co.jp/fdotnet/dotnettips/977aspmvctempdata/aspmvctempdata.html
            byte[] salt = new byte[128 / 8];
            using var rng = RandomNumberGenerator.Create();
            rng.GetBytes(salt);

            byte[] hash = KeyDerivation.Pbkdf2(
              model.Password,
              salt,
              prf: KeyDerivationPrf.HMACSHA256,
              iterationCount: 10000,  // 反復回数
              numBytesRequested: 256 / 8);  // ハッシュの長さ
            string hashed = string.Concat(hash.Select(b => $"{b:x2}"));  //パスワードがハッシュ化された変数 中身3ee3d8d2aaf4b0b2ab82a2b9641442463f8e86eb13794ceed448a804656ccb7d

            //データベース接続処理
            using var db = new Db();
            var newMenber = new UserList { Password = hashed, MailAddress = model.MailAddress, Name = model.Name };
            db.Menber.Add(newMenber);
            db.SaveChanges();
            return RedirectToAction("Index", "Login");
        }
    }
}

ログイン機能のコントローラー(アクション名はLogin)

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using TestWebApplication.Models;

using User.Models;
using MyConnection.Commons;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using System.Security.Cryptography;

namespace TestWebApplication.Controllers
{
    public class LoginController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Login(string mail, string password)
        {
            Console.WriteLine(mail);
            Console.WriteLine(password);

            using (var context = new Db()) //DbはMyConnectionのクラス名
            {

                var result = context.Menber.Count(x => x.MailAddress == mail); //MenberはMyConnection.csを参照

                if(result == 0)
                {
                    return View("Index");
                }

                var product = context.Menber.Single(x => x.MailAddress == mail);
                Console.WriteLine(product.Name); //同じメールアドレスが存在:それに対応する名前
                Console.WriteLine(product.MailAddress); //同じメールアドレスが存在:それに対応するメールアドレス
                Console.WriteLine(product.Password);    //同じメールアドレスが存在:それに対応するハッシュ化されたパスワード 中身3ee3d8d2aaf4b0b2ab82a2b9641442463f8e86eb13794ceed448a804656ccb7d

                byte[] salt = new byte[128 / 8];
                using var rng = RandomNumberGenerator.Create();
                rng.GetBytes(salt);

                byte[] hash = KeyDerivation.Pbkdf2(
                  password,
                  salt,
                  prf: KeyDerivationPrf.HMACSHA256,
                  iterationCount: 10000,  // 反復回数
                  numBytesRequested: 256 / 8);  // ハッシュの長さ
                string hashed = string.Concat(hash.Select(b => $"{b:x2}"));  //パスワードがハッシュ化された変数 中身16809ac99606ff81e80ab960ceefec321008fa392a4d555ddbe5cddcf7223678
                Console.WriteLine(hashed);

                return RedirectToAction("Index", "Main");


            }


        }
    }
}

product.Passwordとhashedが一致していれば良かったが上手くいかなかった。(同じパスワード文字列でハッシュ化している)

0

1Answer

調べると同じパスワードでも違う値を返すこと、ハッシュ化された文字列はもとには戻せないことを知りました。

ソルト値と反復回数が同じなら同じパスワードには同じハッシュが返ります。ご質問のコードではハッシュを求めるたびに毎回ソルトを変えているので同じパスワードでもハッシュが違ってしまいます。

ユーザー1人ごとに1つのソルト値をランダム生成してデータベースに保存しておき、同じユーザーのためのハッシュ計算では同じソルトを使うようにしてください。

ソルトの誤用を防ぐために Wikipedia の解説を読むこともおすすめします: ソルト (暗号) - Wikipedia

1Like

Your answer might help someone💌