Как организовать прохождение двухфакторной аутентификации через ZP

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 788
Благодарностей
2 454
Баллы
113
Есть сайты, которые требуют вход с использованием двухфакторной аутентификации.
Каким образом её проходить через Зеннопостер - может есть на форуме где-то ссылочка на готовое решение?
 
  • Спасибо
Реакции: kriptochel1996

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 788
Благодарностей
2 454
Баллы
113
Неужели действительно никто еще не решил данную проблему или никому не нужно было вместо СМС использовать двухфакторку?
 

Ribas

Client
Регистрация
31.05.2014
Сообщения
1 440
Благодарностей
503
Баллы
113

Nord

Client
Регистрация
22.03.2012
Сообщения
2 406
Благодарностей
1 473
Баллы
113
В основном где попадалось, это шло как фича, которую можна отключить. И небыло проблем
 

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 788
Благодарностей
2 454
Баллы
113
А в чём конкретно проблема?
Знаний нехватает как это реализовать.

В основном где попадалось, это шло как фича, которую можна отключить. И небыло проблем
Дело в том, что я наоборот хочу включить двухфакторку, так как устал вводить СМСки.
При работе с одного аккаунта проблем нет - ввел вручную и нет проблем.
Но, если уже задуматься о работе с 1000 аккаунтов - то вручную вводить столько кодов подтверждения - это уже проблема.
 

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 788
Благодарностей
2 454
Баллы
113
И так, нашел решение в виде исходного кода программы в статье: http://huh-muh.blogspot.com/2016/10/c.html

Спасибо всем, кто помогал переписать этот код под Зеннопостер.


В общий код забрасываем новый класс:
Подключаем DLL MessagingToolkit.QRCode.dll (скачать можно здесь: http://osdn.net/projects/sfnet_qrreader/downloads/MessagingToolkit.QRCode.dll/ )
Код:
using System.Globalization;
using System.Security.Cryptography;
using System.Drawing;
Код:
public class QR
        {
            public static byte[] ToBytes(string input)
            {
                if (string.IsNullOrEmpty(input))
                {
                    throw new ArgumentNullException("input");
                }

                input = input.TrimEnd('=');
                int byteCount = input.Length * 5 / 8;
                byte[] returnArray = new byte[byteCount];
                byte curByte = 0, bitsRemaining = 8;
                int mask = 0, arrayIndex = 0;

                foreach (char c in input)
                {
                    int cValue = CharToValue(c);

                    if (bitsRemaining > 5)
                    {
                        mask = cValue << (bitsRemaining - 5);
                        curByte = (byte)(curByte | mask);
                        bitsRemaining -= 5;
                    }
                    else
                    {
                        mask = cValue >> (5 - bitsRemaining);
                        curByte = (byte)(curByte | mask);
                        returnArray[arrayIndex++] = curByte;
                        curByte = (byte)(cValue << (3 + bitsRemaining));
                        bitsRemaining += 3;
                    }
                }

                if (arrayIndex != byteCount) {
                    returnArray[arrayIndex] = curByte;
                }

                return returnArray;
            }

            public static string ToString(byte[] input)
            {
                if (input == null || input.Length == 0) {
                    throw new ArgumentNullException("input");
                }

                int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
                char[] returnArray = new char[charCount];

                byte nextChar = 0, bitsRemaining = 5;
                int arrayIndex = 0;

                foreach (byte b in input)
                {
                    nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
                    returnArray[arrayIndex++] = ValueToChar(nextChar);

                    if (bitsRemaining < 4)
                    {
                        nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
                        returnArray[arrayIndex++] = ValueToChar(nextChar);
                        bitsRemaining += 5;
                    }

                    bitsRemaining -= 3;
                    nextChar = (byte)((b << bitsRemaining) & 31);
                }

                if (arrayIndex != charCount)
                {
                    returnArray[arrayIndex++] = ValueToChar(nextChar);
                    while (arrayIndex != charCount) returnArray[arrayIndex++] = '=';
                }

                return new string(returnArray);
            }

            private static int CharToValue(char c)
            {
                int value = (int)c;
                if (value < 91 && value > 64) {
                    return value - 65;
                }
                if (value < 56 && value > 49) { 
                    return value - 24;
                }
                if (value < 123 && value > 96) {
                    return value - 97;
                }

                throw new ArgumentException("Character is not a Base32 character.", "c");
            }

            private static char ValueToChar(byte b)
            {
                if (b < 26) {
                    return (char)(b + 65);
                    }

                if (b < 32) {
                    return (char)(b + 24);
                    }

                throw new ArgumentException("Byte is not a value Base32 value.", "b");
            }

            const int IntervalLength = 30;
            const int PinLength = 6;
            static readonly int PinModulo = (int)Math.Pow(10, PinLength);
            static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            public string provisionUrl;
            private byte[] key;
            public string GoogleAuthControl(string url)
            {
                try
                { key = ToBytes(url.Split('=')[1]);
                    GeneratePin();
                    }
                catch {
                }
                return GeneratePin();
            }
           
            static long CurrentInterval
            {
                get {
                    var ElapsedSeconds = (long)Math.Floor((DateTime.UtcNow - UnixEpoch).TotalSeconds);
                    return ElapsedSeconds / IntervalLength;
                    }
                }

            public string GeneratePin() {
                return GeneratePin(this.key, CurrentInterval);
            }
            static string GeneratePin(byte[] key, long counter)
            {
                const int SizeOfInt32 = 4;
                var CounterBytes = BitConverter.GetBytes(counter);
                if (BitConverter.IsLittleEndian) { 
                    Array.Reverse(CounterBytes);
                }
                var Hash = new HMACSHA1(key).ComputeHash(CounterBytes);
                var Offset = Hash[Hash.Length - 1] & 0xF;
                var SelectedBytes = new byte[SizeOfInt32];
                Buffer.BlockCopy(Hash, Offset, SelectedBytes, 0, SizeOfInt32);

                if (BitConverter.IsLittleEndian) {
                    Array.Reverse(SelectedBytes);
                }
                var SelectedInteger = BitConverter.ToInt32(SelectedBytes, 0);
                var TruncatedHash = SelectedInteger & 0x7FFFFFFF;
                var Pin = TruncatedHash % PinModulo;
                return Pin.ToString(CultureInfo.InvariantCulture).PadLeft(PinLength, '0');
  }
 }

На вход (в переменную project.Variables["IMG_2FA_base64"].Value ) подаем QR код в base64, на выходе - получаем результат, который можем использовать.

Сниппет:
Код:
byte[] bytes = Convert.FromBase64String(project.Variables["IMG_2FA_base64"].Value);
            var ms = new MemoryStream(bytes, 0, bytes.Length);
            Image image = Image.FromStream(ms, true);
            Bitmap bmpScreenCapture = new Bitmap(image);
            MessagingToolkit.QRCode.Codec.Data.QRCodeBitmapImage im = new MessagingToolkit.QRCode.Codec.Data.QRCodeBitmapImage(bmpScreenCapture);
            MessagingToolkit.QRCode.Codec.QRCodeDecoder d = new MessagingToolkit.QRCode.Codec.QRCodeDecoder();
            QR qr = new QR();
return qr.GoogleAuthControl(d.decode(im));
 

Вложения

Последнее редактирование:

Protey

Client
Регистрация
09.01.2016
Сообщения
208
Благодарностей
29
Баллы
28
Народ, поделитесь плиз шаблоном, чтоб из QR картинки кодировалось в base64, а потом через сниппет получать код
А то сколько не пробую:
Кладу путь картинки в переменную qr2fa
Код:
byte[] imageArray = System.IO.File.ReadAllBytes(project.Variables["qr2fa"].Value);
string base64ImageRepresentation = Convert.ToBase64String(imageArray);
return base64ImageRepresentation;
На выходе получаю base64 QR картинки в переменную in_base64
Код:
byte[] bytes = Convert.FromBase64String(project.Variables["in_base64"].Value);
            var ms = new MemoryStream(bytes, 0, bytes.Length);
            Image image = Image.FromStream(ms, true);
            Bitmap bmpScreenCapture = new Bitmap(image);
            MessagingToolkit.QRCode.Codec.Data.QRCodeBitmapImage im = new MessagingToolkit.QRCode.Codec.Data.QRCodeBitmapImage(bmpScreenCapture);
            MessagingToolkit.QRCode.Codec.QRCodeDecoder d = new MessagingToolkit.QRCode.Codec.QRCodeDecoder();
            QR qr = new QR();
return qr.GoogleAuthControl(d.decode(im));
bmpScreenCapture.Dispose();
в итоге в логе:
Выполнение действия CSharp OwnCode Ссылка на объект не указывает на экземпляр объекта.
Сбойное действие: 2FA
или
Компиляция кода проекта Ошибка при компиляции общего кода "CS0246" "Не удалось найти имя типа или пространства имен "HMACSHA1" (пропущена директива using или ссылка на сборку?)". [Строка: 118; Cтолбец: 32]
Компиляция кода проекта Ошибка при компиляции общего кода "CS0103" "Имя "CultureInfo" отсутствует в текущем контексте". [Строка: 129; Cтолбец: 37]

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

and2517

Client
Регистрация
10.03.2016
Сообщения
28
Благодарностей
8
Баллы
3
И так, нашел решение в виде исходного кода программы в статье: http://huh-muh.blogspot.com/2016/10/c.html

Спасибо всем, кто помогал переписать этот код под Зеннопостер.


В общий код забрасываем новый класс:
Подключаем DLL MessagingToolkit.QRCode.dll (скачать можно здесь: http://osdn.net/projects/sfnet_qrreader/downloads/MessagingToolkit.QRCode.dll/ )
Код:
using System.Globalization;
using System.Security.Cryptography;
using System.Drawing;
Код:
public class QR
        {
            public static byte[] ToBytes(string input)
            {
                if (string.IsNullOrEmpty(input))
                {
                    throw new ArgumentNullException("input");
                }

                input = input.TrimEnd('=');
                int byteCount = input.Length * 5 / 8;
                byte[] returnArray = new byte[byteCount];
                byte curByte = 0, bitsRemaining = 8;
                int mask = 0, arrayIndex = 0;

                foreach (char c in input)
                {
                    int cValue = CharToValue(c);

                    if (bitsRemaining > 5)
                    {
                        mask = cValue << (bitsRemaining - 5);
                        curByte = (byte)(curByte | mask);
                        bitsRemaining -= 5;
                    }
                    else
                    {
                        mask = cValue >> (5 - bitsRemaining);
                        curByte = (byte)(curByte | mask);
                        returnArray[arrayIndex++] = curByte;
                        curByte = (byte)(cValue << (3 + bitsRemaining));
                        bitsRemaining += 3;
                    }
                }

                if (arrayIndex != byteCount) {
                    returnArray[arrayIndex] = curByte;
                }

                return returnArray;
            }

            public static string ToString(byte[] input)
            {
                if (input == null || input.Length == 0) {
                    throw new ArgumentNullException("input");
                }

                int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
                char[] returnArray = new char[charCount];

                byte nextChar = 0, bitsRemaining = 5;
                int arrayIndex = 0;

                foreach (byte b in input)
                {
                    nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
                    returnArray[arrayIndex++] = ValueToChar(nextChar);

                    if (bitsRemaining < 4)
                    {
                        nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
                        returnArray[arrayIndex++] = ValueToChar(nextChar);
                        bitsRemaining += 5;
                    }

                    bitsRemaining -= 3;
                    nextChar = (byte)((b << bitsRemaining) & 31);
                }

                if (arrayIndex != charCount)
                {
                    returnArray[arrayIndex++] = ValueToChar(nextChar);
                    while (arrayIndex != charCount) returnArray[arrayIndex++] = '=';
                }

                return new string(returnArray);
            }

            private static int CharToValue(char c)
            {
                int value = (int)c;
                if (value < 91 && value > 64) {
                    return value - 65;
                }
                if (value < 56 && value > 49) {
                    return value - 24;
                }
                if (value < 123 && value > 96) {
                    return value - 97;
                }

                throw new ArgumentException("Character is not a Base32 character.", "c");
            }

            private static char ValueToChar(byte b)
            {
                if (b < 26) {
                    return (char)(b + 65);
                    }

                if (b < 32) {
                    return (char)(b + 24);
                    }

                throw new ArgumentException("Byte is not a value Base32 value.", "b");
            }

            const int IntervalLength = 30;
            const int PinLength = 6;
            static readonly int PinModulo = (int)Math.Pow(10, PinLength);
            static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            public string provisionUrl;
            private byte[] key;
            public string GoogleAuthControl(string url)
            {
                try
                { key = ToBytes(url.Split('=')[1]);
                    GeneratePin();
                    }
                catch {
                }
                return GeneratePin();
            }
          
            static long CurrentInterval
            {
                get {
                    var ElapsedSeconds = (long)Math.Floor((DateTime.UtcNow - UnixEpoch).TotalSeconds);
                    return ElapsedSeconds / IntervalLength;
                    }
                }

            public string GeneratePin() {
                return GeneratePin(this.key, CurrentInterval);
            }
            static string GeneratePin(byte[] key, long counter)
            {
                const int SizeOfInt32 = 4;
                var CounterBytes = BitConverter.GetBytes(counter);
                if (BitConverter.IsLittleEndian) {
                    Array.Reverse(CounterBytes);
                }
                var Hash = new HMACSHA1(key).ComputeHash(CounterBytes);
                var Offset = Hash[Hash.Length - 1] & 0xF;
                var SelectedBytes = new byte[SizeOfInt32];
                Buffer.BlockCopy(Hash, Offset, SelectedBytes, 0, SizeOfInt32);

                if (BitConverter.IsLittleEndian) {
                    Array.Reverse(SelectedBytes);
                }
                var SelectedInteger = BitConverter.ToInt32(SelectedBytes, 0);
                var TruncatedHash = SelectedInteger & 0x7FFFFFFF;
                var Pin = TruncatedHash % PinModulo;
                return Pin.ToString(CultureInfo.InvariantCulture).PadLeft(PinLength, '0');
  }
}

На вход (в переменную project.Variables["IMG_2FA_base64"].Value ) подаем QR код в base64, на выходе - получаем результат, который можем использовать.

Сниппет:
Код:
byte[] bytes = Convert.FromBase64String(project.Variables["IMG_2FA_base64"].Value);
            var ms = new MemoryStream(bytes, 0, bytes.Length);
            Image image = Image.FromStream(ms, true);
            Bitmap bmpScreenCapture = new Bitmap(image);
            MessagingToolkit.QRCode.Codec.Data.QRCodeBitmapImage im = new MessagingToolkit.QRCode.Codec.Data.QRCodeBitmapImage(bmpScreenCapture);
            MessagingToolkit.QRCode.Codec.QRCodeDecoder d = new MessagingToolkit.QRCode.Codec.QRCodeDecoder();
            QR qr = new QR();
return qr.GoogleAuthControl(d.decode(im));
Как правильно закодировать QR код в base64?
 

and2517

Client
Регистрация
10.03.2016
Сообщения
28
Благодарностей
8
Баллы
3

BAZAg

Client
Регистрация
08.11.2015
Сообщения
1 788
Благодарностей
2 454
Баллы
113
Актуальное решение (добавлю чтобы не потерялось):
Прикреплю шаблон.
 

Вложения

  • 230,3 КБ Просмотры: 128

Un_cle_v

Новичок
Регистрация
28.04.2020
Сообщения
8
Благодарностей
0
Баллы
1
да просто через вот этот сайт https://rootprojects.org/authenticator/ проганяете свой секрет и парсите зенкой код. никакого программироватьния знать не нужно
 

ssXXXss

Client
Регистрация
23.12.2014
Сообщения
7 379
Благодарностей
2 041
Баллы
113

Un_cle_v

Новичок
Регистрация
28.04.2020
Сообщения
8
Благодарностей
0
Баллы
1
а если синхронизация не подходит а ты болдой спишь ?))
так я ж говорю как раз о способе, когда зенка все сама делает. Если, например, Амазон вдруг запрашивает 2FA, зенка это определяет, идет по указанной мной ссылке, вставляет имя аккаунта, название сервиса и секретный ключ (эти 3 параметра не меняются со временем и вытягиваются из QR кода любой читалкой QR) и считывает из окошка код из 6 цифр, а потом возвращается к форме логина амазона и вводит код. ОТР авторизация пройдена. А я себе как спал так и сплю. =)
 

ssXXXss

Client
Регистрация
23.12.2014
Сообщения
7 379
Благодарностей
2 041
Баллы
113
так я ж говорю как раз о способе, когда зенка все сама делает. Если, например, Амазон вдруг запрашивает 2FA, зенка это определяет, идет по указанной мной ссылке, вставляет имя аккаунта, название сервиса и секретный ключ (эти 3 параметра не меняются со временем и вытягиваются из QR кода любой читалкой QR) и считывает из окошка код из 6 цифр, а потом возвращается к форме логина амазона и вводит код. ОТР авторизация пройдена. А я себе как спал так и сплю. =)
акаунта где аунтификатор ? я просто скажу так, иду вчера на ехмо и не пускает, ОТР не правильный, мне в мобиле надо сделать синхронизацию, или сайт автоматом делает это ?
 

Un_cle_v

Новичок
Регистрация
28.04.2020
Сообщения
8
Благодарностей
0
Баллы
1
акаунта где аунтификатор ? я просто скажу так, иду вчера на ехмо и не пускает, ОТР не правильный, мне в мобиле надо сделать синхронизацию, или сайт автоматом делает это ?
да, QR код для Amazon, например, имеет вид ссылки otpauth://totp/Amazon%3A{LOGIN}?secret={SECRET_KEY}&issuer=Amazon. где логин - это логин или имейл. Когда считаете свой QR увидите что у вас там в качестве логина\аккаунта, кто ишуер и какой сикрет ки. После того, как вобьете все эти параметры на сайте и нажмете Regenerate этот сайт будет генерить OTP один в один что и в мобильном приложении, можете сверить.
 

ssXXXss

Client
Регистрация
23.12.2014
Сообщения
7 379
Благодарностей
2 041
Баллы
113
да, QR код для Amazon, например, имеет вид ссылки otpauth://totp/Amazon%3A{LOGIN}?secret={SECRET_KEY}&issuer=Amazon. где логин - это логин или имейл. Когда считаете свой QR увидите что у вас там в качестве логина\аккаунта, кто ишуер и какой сикрет ки. После того, как вобьете все эти параметры на сайте и нажмете Regenerate этот сайт будет генерить OTP один в один что и в мобильном приложении, можете сверить.
ты меня понять не можешь, иногда сайт говорит что нифига неправильный секрет, я так один раз раз с хэшфлеером поругался из за этого что не мог зайти на сайт, я им писал что типо бабки ввалил а вы меня кидаете, после захожу на эксмо и такая же бяда, они мне и подсказали синхронизацию сдедать, как сайт делает синхронизацию ?
 

Un_cle_v

Новичок
Регистрация
28.04.2020
Сообщения
8
Благодарностей
0
Баллы
1
ты меня понять не можешь, иногда сайт говорит что нифига неправильный секрет, я так один раз раз с хэшфлеером поругался из за этого что не мог зайти на сайт, я им писал что типо бабки ввалил а вы меня кидаете, после захожу на эксмо и такая же бяда, они мне и подсказали синхронизацию сдедать, как сайт делает синхронизацию ?
сайт сам синхронизацию не делает. нужно посмотреть в гугловской апке в какие секунды он обнуляет коды (2 раза в минуту) и в зенопостере яваскриптом настроить delay перед нажатием Regenerate. Например, если гугл аутентификатор обновляет каждую 6ю и 36ю секунду каждой минуты, то яваскрипт в зенке должен делать делей до 6й или 36й секунды перед нажатием Reganerate.

Речь об этом?
 

ssXXXss

Client
Регистрация
23.12.2014
Сообщения
7 379
Благодарностей
2 041
Баллы
113
сайт сам синхронизацию не делает. нужно посмотреть в гугловской апке в какие секунды он обнуляет коды (2 раза в минуту) и в зенопостере яваскриптом настроить delay перед нажатием Regenerate. Например, если гугл аутентификатор обновляет каждую 6ю и 36ю секунду каждой минуты, то яваскрипт в зенке должен делать делей до 6й или 36й секунды перед нажатием Reganerate.

Речь об этом?
в приложухе в приложении нет такого чтобы сама обновлялась
 

Un_cle_v

Новичок
Регистрация
28.04.2020
Сообщения
8
Благодарностей
0
Баллы
1
у меня в гугл аутентификаторе все коды обновляются каждые 30 секунд
 

ssXXXss

Client
Регистрация
23.12.2014
Сообщения
7 379
Благодарностей
2 041
Баллы
113
у меня в гугл аутентификаторе все коды обновляются каждые 30 секунд
я не про обновление секретных кодов, в приложухе они у меня так же обновляются, но бывает что телефон спешит по времени или отстаёт это полюбому у каждого, я про синхронизацию говорю, может и не из за времени он просит это сделать, не вдавался в подробности
 

evgosyan

Client
Регистрация
06.08.2020
Сообщения
26
Благодарностей
22
Баллы
3
Возможно кому-то будет интересно. Дополню способ.

Делал решение для себя и ничего лучше и проще, чем установить этот пакет https://git.coolaj86.com/coolaj86/node-authenticator.js в node.js

Устанавливал для командной строки, чтобы было проще работать (npm install authenticator-cli --global)

Дальше уже все просто, написал небольшой код на C#, который запускает командную строку и обрабатывает получившийся результат.
Параметры для запуска в качестве примера:
64795

Пример кода:
C#:
ProcessStartInfo procStartInfo = new ProcessStartInfo(    "cmd", "/c " + 
                                                        project.Variables["package_name"].Value + " " +
                                                        project.Variables["args"].Value );

procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;

using(Process process = new Process()) {
  process.StartInfo = procStartInfo;
  process.Start();
  process.WaitForExit();
    
      string result = process.StandardOutput.ReadToEnd();
    //Выполняем поиск токена через регулярное выражение
    string pattern = @"(?<=Token:\ ).*"; 
    Regex rg = new Regex(pattern); 
    MatchCollection matchedStrings = rg.Matches(result);
    
    for (int count = 0; count < matchedStrings.Count; count++) 
        {
            Console.WriteLine(matchedStrings[count].Value); 
        }
    
}
На выходе получаете значение OTP кода:
64796
 

alexzver

Client
Регистрация
28.12.2015
Сообщения
59
Благодарностей
10
Баллы
8
Актуальное решение (добавлю чтобы не потерялось):
Прикреплю шаблон.
У меня код не подходит, хотя раньше подходил. Подозреваю, что проблема во времени.
Только времени где?)
На сервере, на зенке, в инстансе?
Помогите пожалуйста решить эту проблему. Что нужно сделать, чтобы коды подходили?
 

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)