Token Expiration Date. How?

S

Shapper

Hello,

I created a class and a few extensions to create a token:

public String Issue(String data) {
Sash sash = new Sash(new SHA256Managed(), 64);
Byte[] hash = sash.CalculateHash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(SaltProvider.GetSalt());
sash.Dispose();
return Convert.ToBase64String(hash);
} // Issue

public Boolean Check(String data, String token) {
Sash sash = new Sash(new SHA256Managed(), 256);
Boolean valid = sash.CheckHash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(SaltProvider.GetSalt()), Encoding.UTF8.GetBytes(token));
sash.Dispose();
return valid;
} // Check

I am building data variable according to the use of the token. Can be for example:

String data = String.Concat(user.Username, user.Created)

But how can I define a Token Issue and Expiration date?

I am not sure how to include this in data and later user as ...

I am not sure how this is usually done.

Could someone, please, help me out?

Thank You,

Miguel
 
S

Shapper

Hello,



I created a class and a few extensions to create a token:



public String Issue(String data) {

Sash sash = new Sash(new SHA256Managed(), 64);

Byte[] hash = sash.CalculateHash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(SaltProvider.GetSalt());

sash.Dispose();

return Convert.ToBase64String(hash);

} // Issue



public Boolean Check(String data, String token) {

Sash sash = new Sash(new SHA256Managed(), 256);

Boolean valid = sash.CheckHash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(SaltProvider.GetSalt()), Encoding.UTF8.GetBytes(token));

sash.Dispose();

return valid;

} // Check



I am building data variable according to the use of the token. Can be for example:



String data = String.Concat(user.Username, user.Created)



But how can I define a Token Issue and Expiration date?



I am not sure how to include this in data and later user as ...



I am not sure how this is usually done.



Could someone, please, help me out?



Thank You,



Miguel

Hello Pete,

The 64/256 mismatch was a typo problem in my code.

I am sending a verification email when a user signs up.
These emails have a verification URL which I am building.

I have seen 3 types of verification URLs as follows:

A) http://domain.com/verify/HY9UoDigNa-cZzWlvzWNQ3
There is no information on the user (I think).
I suppose the token is saved for later verification.

B) http://domain.com/verify/username-slug/HY9UoDigNa-cZzWlvzWNQ3
It is passed the username.slug and the token.

C) http://domain.com/verify/id/HY9UoDigNa-cZzWlvzWNQ3
I don't think passing the user's id is a good idea.
Maybe I am wrong ... So I go for username-slug.

I am creating the Token by concatenating the following User fields:
Username, Email and CreatedDate

I am not sure if you suggest something else ...

Then I hash the data using the following:

Sash sash = new Sash(new SHA256Managed(), 64);
Byte[] hash = sash.CalculateHash(Encoding.UTF8.GetBytes(data), AppSalt));
sash.Dispose();
return Convert.ToBase64String(hash);

AppSalt is a Guid which I use in my application.

My Sash class is the following:

public sealed class Sash : IDisposable {

private Boolean _disposed = false;
private readonly Int32 _repeats;
private readonly HashAlgorithm _algorithm;

private static Int32 _defaultRepeats = 10000;
private static HashAlgorithm _defaultAlgorithm = new SHA256Managed();
private static RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();

public Sash(HashAlgorithm algorithm, Int32 repeats) {
this._algorithm = algorithm;
this._repeats = repeats;
} // Sash

public Sash(Int32 repeats)
: this(_defaultAlgorithm, repeats) {
} // Sash

public Sash(HashAlgorithm algorithm)
: this(algorithm, _defaultRepeats) {
} // Sash

public Sash()
: this(_defaultAlgorithm, _defaultRepeats) {
} // Sash

public Byte[] GenerateSalt(Int32 size) {
Byte[] salt = new Byte[size];
_rng.GetBytes(salt);
return salt;
} // GenerateSalt

public Byte[] CalculateHash(Byte[] data, Byte[] salt) {

Byte[] hash;
hash = _algorithm.ComputeHash(Concat(data, salt));
for (Int32 i = 0; i < _repeats; ++i) {
hash = _algorithm.ComputeHash(Concat(hash, salt));
}
for (Int32 j = 0; j < data.Length; ++j) {
data[j] = 0;
}
return hash;

} // CalculateHash

public Boolean CheckHash(Byte[] data, Byte[] salt, Byte[] expected) {

Byte[] actual = CalculateHash(data, salt);
for (Int32 i = 0; i < actual.Length; ++i) {
if (actual != expected) { return false; }
}
return true;

} // CheckHash

public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
} // Dispose

public void Dispose(Boolean disposing) {

if (_disposed) return;
if (disposing) {
if (_algorithm != null) _algorithm.Clear();
}
_disposed = true;

} // Dispose

private Byte[] Concat(Byte[] lhs, Byte[] rhs) {
Byte[] result = new Byte[lhs.Length + rhs.Length];
Array.Copy(lhs, result, lhs.Length);
Array.Copy(rhs, 0, result, lhs.Length, rhs.Length);
return result;
} // Concat

} // Sash

Finally, I have a few extensions:

public static class CryptographyHelpers {

public static Byte[] Sash(Byte[] value, HashAlgorithm algorithm, Int32 repeats, out Byte[] salt) {

Sash sash = new Sash(algorithm, repeats);
salt = sash.GenerateSalt(repeats);

Byte[] hash = sash.CalculateHash(value, salt);
sash.Dispose();

return hash;

} // Sash

public static Byte[] Sash(String value, HashAlgorithm algorithm, Int32 repeats, out Byte[] salt) {

return Sash(Encoding.UTF8.GetBytes(value), algorithm, repeats, out salt);

} // Sash

} // CryptographyHelpers

So this is my code. Any suggestion in improve is welcome.

About the expiration ... If I want to set it in this case is simple:
On verification I check the elapsed time since user was created.
But other times this is not so simple ... So I was just wondering.

Finally, I am trying to create a class that makes simple to:
1 - Issue a token from given data;
2 - Verify if a token is valid.

I am not sure if I should use encryption or hashing.

I think Hashing would be safer ... But how is usually done?

I am working on the Token Class but I am not sure the best way to do it ...
 
A

Arne Vajhøj

I am sending a verification email when a user signs up.
These emails have a verification URL which I am building.

That is common.
I have seen 3 types of verification URLs as follows:

A) http://domain.com/verify/HY9UoDigNa-cZzWlvzWNQ3
There is no information on the user (I think).
I suppose the token is saved for later verification.

B) http://domain.com/verify/username-slug/HY9UoDigNa-cZzWlvzWNQ3
It is passed the username.slug and the token.

C) http://domain.com/verify/id/HY9UoDigNa-cZzWlvzWNQ3
I don't think passing the user's id is a good idea.
Maybe I am wrong ... So I go for username-slug.

I don't see a problem with the user in the URL, and looking
up the token by user seems natural to me.
I am creating the Token by concatenating the following User fields:
Username, Email and CreatedDate

I am not sure if you suggest something else ...

Then I hash the data using the following:

Sash sash = new Sash(new SHA256Managed(), 64);
Byte[] hash = sash.CalculateHash(Encoding.UTF8.GetBytes(data), AppSalt));
sash.Dispose();
return Convert.ToBase64String(hash);

AppSalt is a Guid which I use in my application.

My Sash class is the following:

public sealed class Sash : IDisposable {

private Boolean _disposed = false;
private readonly Int32 _repeats;
private readonly HashAlgorithm _algorithm;

private static Int32 _defaultRepeats = 10000;
private static HashAlgorithm _defaultAlgorithm = new SHA256Managed();
private static RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();

public Sash(HashAlgorithm algorithm, Int32 repeats) {
this._algorithm = algorithm;
this._repeats = repeats;
} // Sash

public Sash(Int32 repeats)
: this(_defaultAlgorithm, repeats) {
} // Sash

public Sash(HashAlgorithm algorithm)
: this(algorithm, _defaultRepeats) {
} // Sash

public Sash()
: this(_defaultAlgorithm, _defaultRepeats) {
} // Sash

public Byte[] GenerateSalt(Int32 size) {
Byte[] salt = new Byte[size];
_rng.GetBytes(salt);
return salt;
} // GenerateSalt

public Byte[] CalculateHash(Byte[] data, Byte[] salt) {

Byte[] hash;
hash = _algorithm.ComputeHash(Concat(data, salt));
for (Int32 i = 0; i < _repeats; ++i) {
hash = _algorithm.ComputeHash(Concat(hash, salt));
}
for (Int32 j = 0; j < data.Length; ++j) {
data[j] = 0;
}
return hash;

} // CalculateHash

public Boolean CheckHash(Byte[] data, Byte[] salt, Byte[] expected) {

Byte[] actual = CalculateHash(data, salt);
for (Int32 i = 0; i < actual.Length; ++i) {
if (actual != expected) { return false; }
}
return true;

} // CheckHash

public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
} // Dispose

public void Dispose(Boolean disposing) {

if (_disposed) return;
if (disposing) {
if (_algorithm != null) _algorithm.Clear();
}
_disposed = true;

} // Dispose

private Byte[] Concat(Byte[] lhs, Byte[] rhs) {
Byte[] result = new Byte[lhs.Length + rhs.Length];
Array.Copy(lhs, result, lhs.Length);
Array.Copy(rhs, 0, result, lhs.Length, rhs.Length);
return result;
} // Concat

} // Sash


I do not like that CalculateHash modifies data, but that is not the point.
So this is my code. Any suggestion in improve is welcome.

I think the code is overcomplicated.

You just need something that is hard to guess.

So:
- generate 100 random bytes with RNGCryptoServiceProvider
- hash it with SHA256Managed
and you are done.

Repeating the hashing does not provide additional security
(in this context).

Including username etc. in hashing does not provide additional
security (in this context).
About the expiration ... If I want to set it in this case is simple:
On verification I check the elapsed time since user was created.
But other times this is not so simple ... So I was just wondering.

That seems fine to me.

Arne
 
A

Arne Vajhøj

I think the code is overcomplicated.

You just need something that is hard to guess.

So:
- generate 100 random bytes with RNGCryptoServiceProvider
- hash it with SHA256Managed
and you are done.

If you have real good faith in RNGCryptoServiceProvider
you could drop the hashing.

But I would keep it.

Arne
 
A

Arne Vajhøj

Why don't you just send a GUID. Why send _any_ actual user data, when all
you really need is a way to match the URL back to your own internal data,
where you've stored whatever information is important (such as user name,
email address, date, etc.)?

The GUID can be used directly in the URL using the normal GUID formatting
or if you prefer, convert the bytes to base64 and use that (creates a
slightly shorter string).

If you're worried about someone picking random GUIDs in an attempt at an
attack on your system, just combine a GUID with some sort of checksum and
encrypt using the private key of a public/private pair.

(But I don't really think it's worth doing that...you could just as easily
do things like throttle HTTP requests from a given IP address...the chances
of anyone sending you a GUID that actually matches a valid one is pretty
slim, since the GUID should be valid only between the time you send it out
and the time the legitimate user uses it by clicking on the URL you send
them).

GUID's was not designed to be cryptographic secure.

They are probably relative difficult to guess.

But given that cryptographic secure methods exists, then
GUID's should never be used for this purpose.

Arne
 
A

Arne Vajhøj

This is not a scenario that needs cryptographic security.

It is a scenario that requires cryptographic security.

The purpose is to make sure that the person creating the
account in fact own the email address specified.

That requires that it is practical impossible to
guess the generated link.

That is what cryptographic secure random number
generators do.

Ordinary random number generators does not do that.
Whatever. You're welcome to your opinion, but I haven't heard of a single
successful attack on any system using GUIDs or similarly-"short" random
numbers (it doesn't really have to be a GUID...I just mentioned that
because it's a convenient way to get a random number bigger than an int).

That no successful attacks are known is not a very good criteria
for choosing security mechanisms.

It is very easy to predict future values based on previous values
for simple RNG's (LCG based).

Arne
 
S

Shapper

I am sending a verification email when a user signs up.
These emails have a verification URL which I am building.

I have seen 3 types of verification URLs as follows:

A) http://domain.com/verify/HY9UoDigNa-cZzWlvzWNQ3
There is no information on the user (I think).
I suppose the token is saved for later verification.

B) http://domain.com/verify/username-slug/HY9UoDigNa-cZzWlvzWNQ3
It is passed the username.slug and the token.

C) http://domain.com/verify/id/HY9UoDigNa-cZzWlvzWNQ3
I don't think passing the user's id is a good idea.
Maybe I am wrong ... So I go for username-slug.

I am creating the Token by concatenating the following User fields:
Username, Email and CreatedDate

I am not sure if you suggest something else ...



Why don't you just send a GUID. Why send _any_ actual user data, when all

you really need is a way to match the URL back to your own internal data,

where you've stored whatever information is important (such as user name,

email address, date, etc.)?

Basically to check is User sent and Key (Guid match).

Imagine someone starts to send requests to Verify with random Guids ...

There will be a lot of tries before match ... But with many users ...

I don't know ... What do you think?
The GUID can be used directly in the URL using the normal GUID formatting

or if you prefer, convert the bytes to base64 and use that (creates a

slightly shorter string).

Good idea ...
If you're worried about someone picking random GUIDs in an attempt at an

attack on your system, just combine a GUID with some sort of checksum and

encrypt using the private key of a public/private pair.

Could you give me an example?
(But I don't really think it's worth doing that...you could just as easily

do things like throttle HTTP requests from a given IP address...the chances

of anyone sending you a GUID that actually matches a valid one is pretty

slim, since the GUID should be valid only between the time you send it out

and the time the legitimate user uses it by clicking on the URL you send

them).

But if you have 10 000 users in the database (10 000 Guids) then the probability increases ...

But yes it is slim ...
 
S

Shapper

I am sending a verification email when a user signs up.
These emails have a verification URL which I am building.



That is common.


I have seen 3 types of verification URLs as follows:

A) http://domain.com/verify/HY9UoDigNa-cZzWlvzWNQ3
There is no information on the user (I think).
I suppose the token is saved for later verification.

B) http://domain.com/verify/username-slug/HY9UoDigNa-cZzWlvzWNQ3
It is passed the username.slug and the token.

C) http://domain.com/verify/id/HY9UoDigNa-cZzWlvzWNQ3
I don't think passing the user's id is a good idea.
Maybe I am wrong ... So I go for username-slug.



I don't see a problem with the user in the URL, and looking

up the token by user seems natural to me.


I am creating the Token by concatenating the following User fields:
Username, Email and CreatedDate

I am not sure if you suggest something else ...

Then I hash the data using the following:

Sash sash = new Sash(new SHA256Managed(), 64);
Byte[] hash = sash.CalculateHash(Encoding.UTF8.GetBytes(data), AppSalt));
sash.Dispose();

return Convert.ToBase64String(hash);
AppSalt is a Guid which I use in my application.
My Sash class is the following:
public sealed class Sash : IDisposable {
private Boolean _disposed = false;
private readonly Int32 _repeats;
private readonly HashAlgorithm _algorithm;

private static Int32 _defaultRepeats = 10000;
private static HashAlgorithm _defaultAlgorithm = new SHA256Managed();
private static RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();

public Sash(HashAlgorithm algorithm, Int32 repeats) {
this._algorithm = algorithm;
this._repeats = repeats;
} // Sash

public Sash(Int32 repeats)
: this(_defaultAlgorithm, repeats) {
} // Sash

public Sash(HashAlgorithm algorithm)
: this(algorithm, _defaultRepeats) {
} // Sash

public Sash()
: this(_defaultAlgorithm, _defaultRepeats) {
} // Sash
public Byte[] GenerateSalt(Int32 size) {
Byte[] salt = new Byte[size];
_rng.GetBytes(salt);

return salt;
} // GenerateSalt
public Byte[] CalculateHash(Byte[] data, Byte[] salt) {
Byte[] hash;
hash = _algorithm.ComputeHash(Concat(data, salt));
for (Int32 i = 0; i < _repeats; ++i) {
hash = _algorithm.ComputeHash(Concat(hash, salt));

for (Int32 j = 0; j < data.Length; ++j) {
data[j] = 0;

return hash;
} // CalculateHash
public Boolean CheckHash(Byte[] data, Byte[] salt, Byte[] expected) {
Byte[] actual = CalculateHash(data, salt);
for (Int32 i = 0; i < actual.Length; ++i) {
if (actual != expected) { return false; }

return true;
} // CheckHash
public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);

} // Dispose
public void Dispose(Boolean disposing) {
if (_disposed) return;

if (disposing) {
if (_algorithm != null) _algorithm.Clear();

_disposed = true;
} // Dispose
private Byte[] Concat(Byte[] lhs, Byte[] rhs) {
Byte[] result = new Byte[lhs.Length + rhs.Length];
Array.Copy(lhs, result, lhs.Length);
Array.Copy(rhs, 0, result, lhs.Length, rhs.Length);
return result;
} // Concat

} // Sash



I do not like that CalculateHash modifies data, but that is not the point..


What do you mean?

Any suggestion to improve my code is welcome.
I think the code is overcomplicated.



You just need something that is hard to guess.



So:

- generate 100 random bytes with RNGCryptoServiceProvider

- hash it with SHA256Managed

and you are done.



Repeating the hashing does not provide additional security

(in this context).

What context would provide?
Including username etc. in hashing does not provide additional

security (in this context).

What context would provide?
That seems fine to me.

Yes, this is what I was considering ...

But it is not even important as now and then a command runs to remove unverified users that stay around in the database for a
long time.
 
S

Shapper

So:

- generate 100 random bytes with RNGCryptoServiceProvider

- hash it with SHA256Managed

and you are done.

But I need to save that hash in the database so I can later compare correct?

That is something I need to avoid. So this is way I was using data from thedatabase ...

Or am I missing something?

Thank You,
Miguel
 
S

Shapper

Hello,



I created a class and a few extensions to create a token:



public String Issue(String data) {

Sash sash = new Sash(new SHA256Managed(), 64);

Byte[] hash = sash.CalculateHash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(SaltProvider.GetSalt());

sash.Dispose();

return Convert.ToBase64String(hash);

} // Issue



public Boolean Check(String data, String token) {

Sash sash = new Sash(new SHA256Managed(), 256);

Boolean valid = sash.CheckHash(Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(SaltProvider.GetSalt()), Encoding.UTF8.GetBytes(token));

sash.Dispose();

return valid;

} // Check



I am building data variable according to the use of the token. Can be for example:



String data = String.Concat(user.Username, user.Created)



But how can I define a Token Issue and Expiration date?



I am not sure how to include this in data and later user as ...



I am not sure how this is usually done.



Could someone, please, help me out?



Thank You,



Miguel

Finally, let me explain the 2 operations I am working on.

I have a User table with the following fields:

ID (Primary Key), Username, Slug (Safe URL version of Username), Email (Unique), Key (Guid), Verified (Bool), Salt (Used to Hash the Password) and Password.

The two operations I am implementing are:


1 - Verify user after it has been created.

(Already explained. Leaving a better description.)

I am sending an email with the URL:

http://www.domain.com/verify/slug?key=something

When the url is followed I use the slug to get the user and compare the key (Guid).

If the keys are equal I set Verified to true or display a message (for example if it was already verified);

This is what I have now but I am reading your answers.


2 - Provide a way for the user to recover its password:

I display a form where the user inserts its email. If the user exists I send an email with the URL:

http://www.domain.com/reset/slug?key=something

When the url is followed I use the slug to get the user and compare the key (Guid).

If the keys are equal I display a form so the user can change its password.

I am not sure if the URL process in this case should be the same as in (1) ...

And when resetting the password should the sent URL data has included an expiration date?

I am not sure how to to this ... or how to include this in the system.

Or even if this is necessary ... But I think it is.


Thank You,

Miguel
 
A

Arne Vajhøj

I do not like that CalculateHash modifies data, but that is not the point.

What do you mean?
public Byte[] CalculateHash(Byte[] data, Byte[] salt) {
Byte[] hash;
hash = _algorithm.ComputeHash(Concat(data, salt));
for (Int32 i = 0; i < _repeats; ++i) {
hash = _algorithm.ComputeHash(Concat(hash, salt));
}
for (Int32 j = 0; j < data.Length; ++j) {
data[j] = 0; ^^^^^^^^^^^^ here you change input data
}
return hash;
} // CalculateHash
Repeating the hashing does not provide additional security
(in this context).

What context would provide?

If it was storing of hashed passwords then there is a case
for multiple rounds to make brute force attacks more expensive.
What context would provide?

Good question.

A case where they were not known by an attacker.

Arne
 
A

Arne Vajhøj

But I need to save that hash in the database so I can later compare correct?
Yes.

That is something I need to avoid. So this is way I was using data from the database ...

Or am I missing something?

You code used RNGCryptoServiceProvider to provide salt. That is
not reproducible so you would need to store it anyway.

If you dropped RNGCryptoServiceProvider then there would not
by any security left only obscurity - as all the input for
hashing would be known to the attacker.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top