Add support page, admin features, and auth hardening.
Rename public affiliate route to /support with localized nav labels, and include affiliate/social/wallpaper admin, 2FA login, and related site updates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
202dc16a52
commit
fc45c35dbe
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace atakanozbancom.Models.classes
|
||||
{
|
||||
public static class PasswordHasher
|
||||
{
|
||||
private const string Prefix = "pbkdf2";
|
||||
private const int DefaultIterations = 100000;
|
||||
private const int SaltSize = 16;
|
||||
private const int HashSize = 32;
|
||||
|
||||
public static string Hash(string password)
|
||||
{
|
||||
if (password == null)
|
||||
throw new ArgumentNullException("password");
|
||||
|
||||
var salt = new byte[SaltSize];
|
||||
using (var rng = RandomNumberGenerator.Create())
|
||||
{
|
||||
rng.GetBytes(salt);
|
||||
}
|
||||
|
||||
var hash = Pbkdf2(password, salt, DefaultIterations, HashSize);
|
||||
return string.Format(
|
||||
"{0}${1}${2}${3}",
|
||||
Prefix,
|
||||
DefaultIterations,
|
||||
Convert.ToBase64String(salt),
|
||||
Convert.ToBase64String(hash));
|
||||
}
|
||||
|
||||
public static bool IsHashed(string stored)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(stored)
|
||||
&& stored.StartsWith(Prefix + "$", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static bool Verify(string password, string stored)
|
||||
{
|
||||
if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(stored))
|
||||
return false;
|
||||
|
||||
if (!IsHashed(stored))
|
||||
return FixedTimeEquals(password, stored);
|
||||
|
||||
var parts = stored.Split('$');
|
||||
if (parts.Length != 4 || !string.Equals(parts[0], Prefix, StringComparison.Ordinal))
|
||||
return false;
|
||||
|
||||
int iterations;
|
||||
if (!int.TryParse(parts[1], out iterations) || iterations < 1000)
|
||||
return false;
|
||||
|
||||
byte[] salt;
|
||||
byte[] expected;
|
||||
try
|
||||
{
|
||||
salt = Convert.FromBase64String(parts[2]);
|
||||
expected = Convert.FromBase64String(parts[3]);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var actual = Pbkdf2(password, salt, iterations, expected.Length);
|
||||
return FixedTimeEquals(actual, expected);
|
||||
}
|
||||
|
||||
private static byte[] Pbkdf2(string password, byte[] salt, int iterations, int length)
|
||||
{
|
||||
using (var derive = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
|
||||
{
|
||||
return derive.GetBytes(length);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool FixedTimeEquals(byte[] a, byte[] b)
|
||||
{
|
||||
if (a == null || b == null || a.Length != b.Length)
|
||||
return false;
|
||||
|
||||
var diff = 0;
|
||||
for (var i = 0; i < a.Length; i++)
|
||||
diff |= a[i] ^ b[i];
|
||||
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
private static bool FixedTimeEquals(string a, string b)
|
||||
{
|
||||
if (a == null || b == null || a.Length != b.Length)
|
||||
return false;
|
||||
|
||||
var diff = 0;
|
||||
for (var i = 0; i < a.Length; i++)
|
||||
diff |= a[i] ^ b[i];
|
||||
|
||||
return diff == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user