Несмотря на свою довольно простую оболочку, мобильные службы Azure можно использовать для стандартной проверки подлинности в приложении. В этой статье я рассмотрю базовую реализацию с проверкой учетных данных на основе хеша.
На этом этапе убедитесь, что вы загрузили и добавили ссылку на Azure Mobile Services SDK для Windows Phone 8 (или вы можете сделать предварительный выпуск SDK для Windows Phone 7.5 ). NuGet — самый простой способ сделать это.
Начните с создания новой таблицы на портале управления Azure — Пользователи .
В классе App включите ссылку на экземпляр MobileServiceClient, который вы будете использовать для подключения к базовой базе данных:
public static MobileServiceClient MobileService = new MobileServiceClient(
"https://YOUR_AZURE_MS.azure-mobile.net/",
"KEY"
);
Теперь давайте подумаем о логистике механизмов безопасности. Каждый пароль хешируется и засоляется. Позже он сохраняется в таблице Users . В этот момент вы можете подумать — хеш хранится с солью в одной таблице, разве это не угроза безопасности? Не совсем, если пароль хранится в необратимой форме — вернуть его невозможно, даже если каким-то образом получить и хеш, и соль. Кроме того, соль предполагается уникальной для каждого пароля. Это помогает защитить систему от словарных атак .
Вот как генерируется хеш пароля:
public static byte[] GenerateHash(string password, byte[] salt)
{
byte[] passwordData = Encoding.UTF8.GetBytes(password);
byte[] composite = new byte[passwordData.Length + 32];
Array.Copy(passwordData, composite, passwordData.Length);
Array.Copy(salt, 0, composite, passwordData.Length, salt.Length);
SHA256 hashFunction = new SHA256Managed();
byte[] hash = hashFunction.ComputeHash(composite);
return hash;
}
Соль передается функции в виде байтового массива, который позже добавляется к паролю. SHA256 используется для вычисления хеша ядра — избегайте использования алгоритмов хеширования, таких как MD5 и SHA1 .
Хеш генерируется с помощью криптографически безопасного генератора случайных чисел — RNGCryptoServiceProvider .
public static byte[] GetSalt()
{
byte[] rngContainer = new byte[32];
RNGCryptoServiceProvider rngProvider = new RNGCryptoServiceProvider();
rngProvider.GetBytes(rngContainer);
return rngContainer;
}
Внутренне мне также потребуется извлечь представление байта из строки соли для целей проверки. Вот как выглядит функция:
public static byte[] GetSaltFromString(string source)
{
string[] raw = source.Split('-');
byte[] result = new byte[raw.Length];
for (int i = 0; i < raw.Length; i++)
{
result[i] = Convert.ToByte(raw[i], 16);
}
return result;
}
Для отправки данных в Azure нам нужна сериализуемая пользовательская модель, а также возможность для разработчика выбирать, использовать новый случайный хэш или нет:
public static User GetSecureUserModel(string username, string password, string email = "",
byte[] customSalt = null)
{
User user = new User();
user.Username = username;
user.Email = email;
byte[] hash;
if (customSalt == null)
{
byte[] salt = HashHelper.GetSalt();
user.Salt = BitConverter.ToString(salt);
hash = HashHelper.GenerateHash(password, salt);
}
else
{
hash = HashHelper.GenerateHash(password, customSalt);
}
user.Hash = BitConverter.ToString(hash);
return user;
}
You can infer the model structure from the table fields:
[DataTable(Name="Users")]
public class User
{
public int? Id { get; set; }
public string Username { get; set; }
public string Salt { get; set; }
public string Hash { get; set; }
public string Email { get; set; }
}
On registration, a table insertion is executed:
public async static Task<bool> RegisterUser(User user)
{
IMobileServiceTable<User> userTable = App.MobileService.GetTable<User>();
List<User> userList = await userTable.Take(1).Where(x => x.Username == user.Username).ToListAsync();
if (userList.Count == 0)
{
await App.MobileService.GetTable<User>().InsertAsync(user);
return true;
}
else
{
return false;
}
}
Here is what we do when we need to validate credentials that are given the user — a new user model is obtained based on the input and the hash for the given username is retrieved from the database:
public static async Task<User> GetUserFromDatabase(string username)
{
IMobileServiceTable<User> userTable = App.MobileService.GetTable<User>();
List<User> userList = await userTable.Take(1).Where(x => x.Username == username).ToListAsync();
if (userList.Count == 0)
return null;
else
return userList.First();
}
The login is verified against the existing model, with the existing salt being used to create a new password hash, based on the new input.
public async static Task<User> VerifyLogin(string username, string password)
{
User dbUser = await GetUserFromDatabase(username);
if (dbUser != null)
{
User localUser = GetSecureUserModel(username, password, "",
HashHelper.GetSaltFromString(dbUser.Salt));
if (dbUser.Hash == localUser.Hash)
return dbUser;
}
return null;
}
This model can be applied to simple mobile applications that need a core authentication system.
There are several additions that can be added to the implementation — for example, additional hashing can be added to on the server side. A more complex hashing algorithm can be also used with more variation added to the salt.
