Arhn - архитектура программирования

Извлечение MachineKey из развернутой службы приложений Azure

У меня есть служба веб-API ASP.NET 4.6, работающая как служба приложений Azure в одном плане службы приложений в одном регионе. Мы модифицируем эту службу, чтобы ее можно было развернуть в нескольких регионах с балансировщиком нагрузки впереди, и у каждого региона будет свой собственный план службы приложений. Поэтому нам необходимо убедиться, что мы используем один и тот же ключ компьютера в каждом плане службы приложений, чтобы предотвратить выход пользователей из системы, когда балансировщик нагрузки направляет их на разные серверы.

Наше приложение некоторое время работало с использованием машинного ключа, автоматически предоставляемого Azure в одном плане службы приложений. Чтобы избежать выхода всех наших клиентов из системы во время перехода, я планировал извлечь этот существующий машинный ключ, а затем развернуть его в новых планах службы приложений в других регионах. Звучит достаточно просто, верно?

Однако извлечение этого ключа оказывается проблемой.

Я пробовал решения, перечисленные здесь: Получение текущего ключа компьютера ASP.NET

Хотя каждый метод возвращает какой-либо ключ, этот ключ не соответствует ключу, фактически используемому для создания токенов-носителей или для защиты билетов токена обновления. Когда я развертываю эти ключи на других серверах, токены-носители по-прежнему считаются недействительными, и попытка использовать существующий токен обновления приводит к ответу invalid_grant.

Кроме того, даже когда я вручную устанавливаю ключ компьютера в файле web.config (или во время выполнения, используя такой код, как this), ни один из извлеченных машинных ключей не соответствует машинному ключу, который я установил вручную, что является дополнительным доказательством того, что все, что они возвращают, на самом деле не является машинным ключом. использовался. Это верно как локально на моем компьютере для разработки, так и в Azure.

Для справки, это код (с изъятым секретом безопасности), который я использовал для извлечения ключей расшифровки и проверки тремя разными способами:

[DllImport(@"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\webengine4.dll")]
internal static extern int EcbCallISAPI(IntPtr pECB, int iFunction, byte[] bufferIn, int sizeIn, byte[] bufferOut, int sizeOut);

[Route("machine-key-test")]
public async Task<JObject> GetMachineKeys()
{
    return new JObject(
        new JProperty("A", GetAdminData()),
        new JProperty("B", GetAdminDataNoIsolateApps()),
        new JProperty("C", GetAdminDataPre45()));

    JObject GetAdminData()
    {
        string appPath = "/";
        byte[] genKeys = new byte[1024];
        byte[] autogenKeys = new byte[1024];

        int res = EcbCallISAPI(IntPtr.Zero, 4, genKeys, genKeys.Length, autogenKeys, autogenKeys.Length);

        if (res == 1)
        {
            // Same as above
            int validationKeySize = 64;
            int decryptionKeySize = 24;

            byte[] validationKey = new byte[validationKeySize];
            byte[] decryptionKey = new byte[decryptionKeySize];

            Buffer.BlockCopy(autogenKeys, 0, validationKey, 0, validationKeySize);
            Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKey, 0, decryptionKeySize);

            int pathHash = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appPath);
            validationKey[0] = (byte)(pathHash & 0xff);
            validationKey[1] = (byte)((pathHash & 0xff00) >> 8);
            validationKey[2] = (byte)((pathHash & 0xff0000) >> 16);
            validationKey[3] = (byte)((pathHash & 0xff000000) >> 24);

            decryptionKey[0] = (byte)(pathHash & 0xff);
            decryptionKey[1] = (byte)((pathHash & 0xff00) >> 8);
            decryptionKey[2] = (byte)((pathHash & 0xff0000) >> 16);
            decryptionKey[3] = (byte)((pathHash & 0xff000000) >> 24);

            var decrptionKeyString = decryptionKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());
            var validationKeyString = validationKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());

            return new JObject(
                new JProperty("D", decrptionKeyString),
                new JProperty("V", validationKeyString));
        }


        return null;
    }

    JObject GetAdminDataNoIsolateApps()
    {
        string appPath = "/";
        byte[] genKeys = new byte[1024];
        byte[] autogenKeys = new byte[1024];

        int res = EcbCallISAPI(IntPtr.Zero, 4, genKeys, genKeys.Length, autogenKeys, autogenKeys.Length);

        if (res == 1)
        {
            // Same as above
            int validationKeySize = 64;
            int decryptionKeySize = 24;

            byte[] validationKey = new byte[validationKeySize];
            byte[] decryptionKey = new byte[decryptionKeySize];

            Buffer.BlockCopy(autogenKeys, 0, validationKey, 0, validationKeySize);
            Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKey, 0, decryptionKeySize);

            var decrptionKeyString = decryptionKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());
            var validationKeyString = validationKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString());

            return new JObject(
                new JProperty("D", decrptionKeyString),
                new JProperty("V", validationKeyString));
        }


        return null;
    }

    JObject GetAdminDataPre45()
    {
    // https://stackoverflow.com/a/35954339/37725
        byte[] autogenKeys = (byte[]) typeof(HttpRuntime)
            .GetField("s_autogenKeys", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);

        Type t = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType(
            "System.Web.Security.Cryptography.MachineKeyMasterKeyProvider");
        ConstructorInfo ctor = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];

        Type ckey = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType(
            "System.Web.Security.Cryptography.CryptographicKey");
        ConstructorInfo ckeyCtor = ckey.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
        Object ckeyobj = ckeyCtor.Invoke(new object[] {autogenKeys});
        object o = ctor.Invoke(new object[] {new MachineKeySection(), null, null, ckeyobj, null});
        var encKey = t.GetMethod("GetEncryptionKey").Invoke(o, null);
        byte[] encBytes = ckey.GetMethod("GetKeyMaterial").Invoke(encKey, null) as byte[];
        var vldKey = t.GetMethod("GetValidationKey").Invoke(o, null);
        byte[] vldBytes = ckey.GetMethod("GetKeyMaterial").Invoke(vldKey, null) as byte[];
        string decryptionKey = BitConverter.ToString(encBytes);
        decryptionKey = decryptionKey.Replace("-", "");
        string validationKey = BitConverter.ToString(vldBytes);
        validationKey = validationKey.Replace("-", "");

        return new JObject(
            new JProperty("D", decryptionKey),
            new JProperty("V", validationKey));
    }
}

И это пример вывода, который я получаю:

{
    "A": {
        "D": "b298ba4ef5e8421e178770f50ee5414dd0aa1698afc3169d",
        "V": "b298ba4e3ed466051c60e4c8646ece2546f27e8b9b2e9a569453eaab6b2a4e93bc08a3171ea61972adfd83c97d21bbcfad2acd3e6d35668f5458f8d7c8f55913"
    },
    "B": {
        "D": "dc509c9af5e8421e178770f50ee5414dd0aa1698afc3169d",
        "V": "84246e973ed466051c60e4c8646ece2546f27e8b9b2e9a569453eaab6b2a4e93bc08a3171ea61972adfd83c97d21bbcfad2acd3e6d35668f5458f8d7c8f55913"
    },
    "C": {
        "D": "A2EDFD4ECE75A91F8E38D62B569248B14CE9193DD42E543E0D4BA5C9E2BED912",
        "V": "DC6144A79985DEF712FABC729871A79FF2CF0DD73CBA617C3764D234DA1B63AD"
    }
}

Я пытался использовать каждый из этих наборов ключей по очереди, как без явной установки алгоритмов расшифровки и проверки, так и с указанием различных комбинаций алгоритмов, которые соответствуют длинам ключей, как определено здесь, но безуспешно.

И, как я уже сказал, ни один из этих ключей не будет соответствовать машинному ключу, который я устанавливаю вручную в моем web.config или который я устанавливаю вручную в коде.

Вывод, к которому я прихожу, заключается в том, что я либо делаю что-то тривиально неправильное, либо мне придется заставить всех наших пользователей принудительно выйти из системы, изменив машинный ключ на новый на всех наших серверах. Я надеюсь, что кто-то сможет указать мне в правильном направлении.


Ответы:


1

Оказывается, я слишком все усложнил.

Если вы используете Kudu (из раздела «Дополнительные инструменты» на портале Azure для рассматриваемой службы приложений), вы можете найти файл по адресу D:\local\Config\rootweb.config, который содержит машинный ключ.

Я нашел его благодаря ответу на несвязанный вопрос здесь, так что, надеюсь, это избавит других от некоторых проблем.

12.12.2019
  • Так как же это на самом деле работает? Вы только что изменили все машинные ключи служб приложений из их файла rootweb.config и убедились, что все они имеют один и тот же ключ? 24.03.2021
  • @Burak В итоге я развернул необходимые значения ValidationKey, DecryptionKey, Decryption и ValidationAlgorithm в качестве переменных среды, а затем использовал эту технику, чтобы установить их при запуске: blog.nilayparikh.com/azure/web/set-machinekey-on-azure-web-app 26.03.2021
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..