web-dev-qa-db-ja.com

24時間後に期限切れになる一意のトークンを生成する方法は?

ユーザーが有効かどうかを確認するWCF Webサービスがあります。

ユーザーが有効な場合、24時間後に有効期限が切れるトークンを生成します。

public bool authenticateUserManual(string userName, string password,string language,string token)
{
    if (Membership.ValidateUser(userName,password))
    {
        //////////
        string token = ???? 
        //////////

        return true;
    }
    else 
    {
        return false;
    }
}   
43
Eray Geveci

2つの可能なアプローチがあります。一意の値を作成して、データベースなどの作成時間とともにどこかに保存するか、トークン内に作成時間を入れて、後でデコードして作成日を確認できるようにします。

一意のトークンを作成するには:

string token = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

タイムスタンプを含む一意のトークンを作成する基本的な例:

byte[] time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
byte[] key = Guid.NewGuid().ToByteArray();
string token = Convert.ToBase64String(time.Concat(key).ToArray());

トークンをデコードして作成時間を取得するには:

byte[] data = Convert.FromBase64String(token);
DateTime when = DateTime.FromBinary(BitConverter.ToInt64(data, 0));
if (when < DateTime.UtcNow.AddHours(-24)) {
  // too old
}

注:安全にするためにタイムスタンプ付きのトークンが必要な場合は、暗号化する必要があります。そうしないと、ユーザーは何が含まれているかを把握し、偽のトークンを作成できます。

137
Guffa

Guffaの回答が好きです。コメントできないので、ここでUdilの質問に対する回答を提供します。

同様の何かが必要でしたが、トークンに証明書のロジックが必要でした。

  1. トークンの有効期限を見る
  2. 検証をマスクするためにGUIDを使用します(グローバルアプリケーションGUIDまたはユーザーGUID)
  3. トークンが私が作成した目的のために提供されたかどうかを確認します(再利用不可..)
  4. トークンを送信したユーザーが、検証対象のユーザーであるかどうかを確認します

ポイント1〜3は固定長なので、簡単でした。ここに私のコードを示します。

トークンを生成するコードは次のとおりです。

public string GenerateToken(string reason, MyUser user)
{
    byte[] _time     = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
    byte[] _key      = Guid.Parse(user.SecurityStamp).ToByteArray();
    byte[] _Id       = GetBytes(user.Id.ToString());
    byte[] _reason   = GetBytes(reason);
    byte[] data       = new byte[_time.Length + _key.Length + _reason.Length+_Id.Length];

    System.Buffer.BlockCopy(_time, 0, data, 0, _time.Length);
    System.Buffer.BlockCopy(_key , 0, data, _time.Length, _key.Length);
    System.Buffer.BlockCopy(_reason, 0, data, _time.Length + _key.Length, _reason.Length);
    System.Buffer.BlockCopy(_Id, 0, data, _time.Length + _key.Length+ _reason.Length, _Id.Length);

    return Convert.ToBase64String(data.ToArray());
}

生成されたトークン文字列を取得して検証するコードは次のとおりです。

public TokenValidation ValidateToken(string reason, MyUser user, string token)
{
    var result = new TokenValidation();
    byte[] data     = Convert.FromBase64String(token);
    byte[] _time     = data.Take(8).ToArray();
    byte[] _key      = data.Skip(8).Take(16).ToArray();
    byte[] _reason   = data.Skip(24).Take(4).ToArray();
    byte[] _Id       = data.Skip(28).ToArray();

    DateTime when = DateTime.FromBinary(BitConverter.ToInt64(_time, 0));
    if (when < DateTime.UtcNow.AddHours(-24))
    {
        result.Errors.Add( TokenValidationStatus.Expired);
    }

    Guid gKey = new Guid(_key);
    if (gKey.ToString() != user.SecurityStamp)
    {
        result.Errors.Add(TokenValidationStatus.WrongGuid);
    }

    if (reason != GetString(_reason))
    {
        result.Errors.Add(TokenValidationStatus.WrongPurpose);
    }

    if (user.Id.ToString() != GetString(_Id))
    {
        result.Errors.Add(TokenValidationStatus.WrongUser);
    }

    return result;
}

TokenValidationクラスは次のようになります。

public class TokenValidation
{
    public bool Validated { get { return Errors.Count == 0; } }
    public readonly List<TokenValidationStatus> Errors = new List<TokenValidationStatus>();
}

public enum TokenValidationStatus
{
    Expired,
    WrongUser,
    WrongPurpose,
    WrongGuid
}

これで、トークンを簡単に検証できるようになりました。24時間ほどリストに保持する必要はありません。これが私のGood-Case Unitテストです:

private const string ResetPasswordTokenPurpose = "RP";
private const string ConfirmEmailTokenPurpose  = "EC";//change here change bit length for reason  section (2 per char)

[TestMethod]
public void GenerateTokenTest()
{
    MyUser user         = CreateTestUser("name");
    user.Id             = 123;
    user.SecurityStamp  = Guid.NewGuid().ToString();
    var token   = sit.GenerateToken(ConfirmEmailTokenPurpose, user);
    var validation    = sit.ValidateToken(ConfirmEmailTokenPurpose, user, token);
    Assert.IsTrue(validation.Validated,"Token validated for user 123");
}

他のビジネスケースに簡単にコードを適合させることができます。

ハッピーコーディング

ウォルター

28

つかいます Dictionary<string, DateTime>タイムスタンプ付きのトークンを保存します:

static Dictionary<string, DateTime> dic = new Dictionary<string, DateTime>();

新しいトークンを作成するたびに、タイムスタンプ付きのトークンを追加します。

dic.Add("yourToken", DateTime.Now);

期限切れのトークンをdicから削除するために実行されているタイマーがあります。

 timer = new Timer(1000*60); //assume run in 1 minute
 timer.Elapsed += timer_Elapsed;

 static void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        var expiredTokens = dic.Where(p => p.Value.AddDays(1) <= DateTime.Now)
                              .Select(p => p.Key);

        foreach (var key in expiredTokens)
            dic.Remove(key);
    }

したがって、トークンを認証するときは、トークンがdicに存在するかどうかを確認するだけです。

3
cuongle

最初の登録の作成中にトークンを保存する必要があります。ログインテーブルからデータを取得する場合、入力された日付を現在の日付と区別する必要があります。1日(24時間)を超える場合は、トークンの有効期限が切れたようなメッセージを表示する必要があります。

キーを生成するには、 here を参照してください

0