
Создание торговой панели администратора на MQL5 (Часть IV): Безопасность входа в систему
Содержание:
- Введение
- Безопасность в MQL5
- Краткое повторение Части III
- Интеграция защиты паролем в панель администратора
- Тестирование и результаты
- Заключение
Введение
Безопасность имеет первостепенное значение в любой сфере деятельности, и мы не можем позволить себе игнорировать ее важность. Учитывая постоянную угрозу несанкционированного доступа, крайне важно защитить нашу панель администратора от потенциальных злоумышленников. Если доступ получат неавторизованные лица, они смогут легко управлять панелью и поставить под угрозу наши усилия. Основная цель системы — обеспечение надежной связи, и хотя мы можем улучшить функциональность на уровне советника, риски вторжения остаются значительными.
Злоумышленник, получивший доступ к панели управления, может отправлять пользователям вводящие в заблуждение сообщения, вызывая путаницу и нанося ущерб репутации системного администратора. Я считаю, что для снижения этих рисков необходимо реализовать уровень безопасности, ограничивающий доступ к ключевым функциям без правильных учетных данных. Такой простой подход к безопасности защищает нашу панель, а также помогает поддерживать целостность наших коммуникаций и доверие сообщества.
Панель входа
Обзор безопасности в MQL5
MQL5 предлагает полный спектр функций безопасности, предназначенных для защиты как исходного кода, так и скомпилированных файлов (EX5), обеспечивая защиту интеллектуальной собственности и предотвращая несанкционированное использование. Ключевые механизмы включают шифрование скомпилированных файлов, лицензирование на основе учетных записей и времени, а также интеграцию с внешними DLL-библиотеками для дополнительной защиты. Платформа поддерживает цифровые подписи для проверки подлинности кода, а MetaQuotes обеспечивает защиту кода посредством компиляции и обфускации для предотвращения обратного проектирования. Для продуктов, распространяемых через MQL5 Маркетдополнительное шифрование гарантирует, что только пользователи с лицензией смогут получить доступ к программному обеспечению и использовать его, создавая надежную структуру безопасности для разработчиков.
В 2012 году пользователь MQL5 Investeo рассмотрел различные методы защиты программ и MQL5-кода, поделившись ценными идеями по внедрению таких методов, как защита паролем, генерация ключей, лицензирование для одной учетной записи, защита с ограничением по времени, удаленные лицензии, безопасное шифрование лицензий и передовые методы защиты от декомпиляции. Его работа служит основополагающим источником информации по повышению безопасности программ.
Цель обсуждения:
Осознавая важность безопасности, мы намерены обсудить реализацию защиты паролем для доступа к функциям панели администратора. Мы подробно рассмотрим методы, используемые для достижения результатов, продемонстрированных на предыдущем изображении, сосредоточившись на том, как можно эффективно защитить доступ пользователей.
Потенциальные области уязвимости в нашей панели администратора:
По мере того, как наша программа развивается и включает в себя новые функции, мы осознаем ее растущую сложность, особенно для начинающих разработчиков. Мы выделяем несколько ключевых областей интереса в отношении безопасности:
- Защищенный доступ к панели администратора:
Чтобы гарантировать, что ни один неавторизованный пользователь не сможет получить доступ к панели администратора, мы реализуем защиту паролем. Несанкционированный доступ может привести к распространению нежелательных сообщений среди сообщества трейдеров, которые полагаются на информацию администратора. Случайные ошибочные нажатия на кнопки быстрого доступа требуют надежного пароля. Хотя многие приложения используют для дополнительной проверки двухфакторную аутентификацию (2FA), в настоящее время мы по-прежнему сосредоточены на реализации базовых функций безопасности и планируем внедрять более продвинутые опции.
- Безопасность сообщений API Telegram:
Мы также уделяем первостепенное внимание безопасности общения через API Telegram, безопасно вводя идентификатор чата и токен бота во время запуска программы. Такой подход гарантирует, что конфиденциальные данные пользователя остаются защищенными. Telegram использует надежные функции безопасности для защиты пользовательской переписки, включая безопасность транспортного уровня (transport layer security) с помощью протокола MTProto для стандартных чатов и сквозное шифрование для секретных чатов. Кроме того, Telegram поддерживает 2FA, что позволяет пользователям управлять активными сеансами и повышать безопасность аккаунта. Несмотря на то, что протоколы безопасности Telegram надежны, пользователи также должны убедиться в безопасности своих устройств.
Краткое повторение Части III
В нашем предыдущем обсуждении мы коснулись внедрения методов управления темами. Однако мы работали с файлами, которые могут изменяться во время обновлений платформы MetaTrader 5. Каждый раз, когда выпускается обновление, оно автоматически загружается и устанавливается при перезапуске. Ниже приведен фрагмент кода, иллюстрирующий ошибки, с которыми я столкнулся при попытке скомпилировать его после обновлений.
'UpdateThemeColors' - undeclared identifier Admin Panel .mq5 390 16 'darkTheme' - some operator expected Admin Panel .mq5 390 34 'SetTextColor' - undeclared identifier Admin Panel .mq5 397 14 'textColor' - some operator expected Admin Panel .mq5 397 27 'SetBackgroundColor' - undeclared identifier Admin Panel .mq5 398 14 'bgColor' - some operator expected Admin Panel .mq5 398 33 'SetBorderColor' - undeclared identifier Admin Panel .mq5 399 14 'borderColor' - some operator expected Admin Panel .mq5 399 29 'SetTextColor' - undeclared identifier Admin Panel .mq5 424 12 'textColor' - some operator expected Admin Panel .mq5 424 25 'SetBackgroundColor' - undeclared identifier Admin Panel .mq5 425 12 'bgColor' - some operator expected Admin Panel .mq5 425 31 'SetBorderColor' - undeclared identifier Admin Panel .mq5 426 12 'borderColor' - some operator expected Admin Panel .mq5 426 27 14 errors, 1 warnings 15 2
Временное решение
Чтобы решить проблему, необходимо сначала понять ее источник. Как объяснялось ранее, обновления платформы сбрасывают используемые нами библиотеки к состояниям по умолчанию. Следовательно, методы, которые мы реализовали для управления темами, больше недействительны, поэтому мы теперь сталкиваемся с ошибками. Чтобы решить эту проблему, нам нужно перезаписать обновленные файлы (Dialog.mqh, Edit.mqh и Button.mqh) расширенными версиями, приложенными к предыдущей статье. Вы можете найти папку для включаемых файлов, как показано на рисунке ниже.
Переход к корневой папке dialog.mqh
Постоянное решение:
Мы можем переименовать Dialog.mqh и другие связанные файлы для Extended_Dialog.mqh и скорректировать наш код необходимым образом. Что нам нужно сделать обязательно, так это обновить операторы #include, ссылающиеся на старое имя файла, чтобы отразить новое имя. Кроме того, нам необходимо проверить наличие других зависимостей, которые могут ссылаться на него, и обновить их по мере необходимости. После внесения этих изменений перекомпилируем наш проект, чтобы выявить возможные ошибки и тщательно протестировать функциональность, чтобы убедиться, что все работает правильно. Это позволит сохранить его отдельно под новым именем, но при этом сохранить исходный файл.
Например, если мы уже сохранили файл как Extended_Dialog.mqh, мы можем перейти в нашу панель администратора и настроить код следующим образом:
#include <Controls\Extended_Dialog.mqh> #include <Controls\Extended_Button.mqh> #include <Controls\Extended_Edit.mqh> #include <Controls\Label.mqh>
Преимущества сохранения под другим именем
Сохранение под другим именем позволяет адаптировать функционал под ваши нужды, добавляя или настраивая функции, отсутствующие во встроенной версии. Такая настройка помогает создать уникальный интерфейс, отвечающий вашим требованиям. Кроме того, использование пользовательских имен файлов помогает избежать конфликтов со встроенными или сторонними библиотеками, снижая риск непредвиденного поведения из-за совпадающих имен. Сохранение введенных изменений в переименованном файле дополнительно защищает ваши настройки от влияния других встроенных функций, которые могут использовать исходный Dialog, гарантируя, что вы сможете разрабатывать и поддерживать свой проект без влияния внешних изменений.
Интеграция защиты паролем в панель администратора
В этом проекте мы реализуем условную защиту паролем с использованием строкового пароля, который может включать как буквы, так и цифры, что повышает сложность. Хотя четырехзначный PIN-код может показаться простым, его трудно угадать. На панели администратора мы используем класс Dialog для запроса пароля у пользователя при входе в систему, при этом заданы условия для отображения функций главной панели только после успешного ввода пароля.
Продолжая развивать программу панели администратора, мы уделяем особое внимание обеспечению надежной безопасности входа в систему, чтобы гарантировать, что доступ к конфиденциальным функциям смогут получить только авторизованные пользователи.
Механизм аутентификации
Для защиты панели администратора мы реализуем простой механизм аутентификации на основе пароля, который запрашивает у пользователей пароль перед предоставлением доступа к каким-либо функциям.
// Show authentication input dialog bool ShowAuthenticationPrompt() { if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create authentication dialog"); return false; } }
В функции ShowAuthenticationPrompt мы реализуем удобный интерфейс, который эффективно проводит наших пользователей через процесс аутентификации. Создав специальный диалог для ввода пароля, мы гарантируем, что основная точка доступа к панели администратора останется безопасной и в то же время интуитивно понятной.
Для лучшего понимания я привел фрагмент кода для создания диалогового окна ниже, снабдив его комментариями. Если вы хотите освежить свои знания по осям и координатам, обратитесь к (Часть 1).
// This condition checks if the authentication object is created successfully if (!authentication.Create( // Function call to create authentication ChartID(), // Retrieve the ID of the current chart "Authentication", // Label of the dialog window in this case it is 'Authentication' 0, // Initial X position on the chart also X_1 100, // Initial Y position on the chart also Y_1 500, // Width of the authentication window also X_2 300 // Height of the authentication window also Y_2 ))
Создав диалоговое окно аутентификации, приступаем к аналогичной компоновке других элементов пользовательского интерфейса, но с другими значениями. Процесс начинается с создания поля ввода пароля, куда пользователи могут вводить свои учетные данные, а затем нажимают необходимые кнопки. В частности, мы сосредоточимся на двух основных кнопках: Login (войти) и Close (закрыть). Кнопка Login используется для отправки введенного пароля, а кнопка Close выходит из диалогового окна. Ниже приведен фрагмент кода, иллюстрирующий логику создания этих кнопок, а также текст запроса пароля.
// Create password input if (!passwordInputBox.Create(ChartID(), "PasswordInputBox", 0, 20, 70, 260, 95)) { Print("Failed to create password input box"); return false; } authentication.Add(passwordInputBox); // Create prompt label if (!passwordPromptLabel.Create(ChartID(), "PasswordPromptLabel", 0, 20, 20, 260, 20)) { Print("Failed to create password prompt label"); return false; } passwordPromptLabel.Text("Enter password: Access Admin Panel"); authentication.Add(passwordPromptLabel); // Create login button if (!loginButton.Create(ChartID(), "LoginButton", 0, 20, 120, 100, 140)) { Print("Failed to create login button"); return false; } loginButton.Text("Login"); authentication.Add(loginButton); // Create close button for authentication if (!closeAuthButton.Create(ChartID(), "CloseAuthButton", 0, 120, 120, 200, 140)) // Adjusted position { Print("Failed to create close button for authentication"); return false; } closeAuthButton.Text("Close"); authentication.Add(closeAuthButton); authentication.Show(); // Show the authentication dialog ChartRedraw(); // Redraw the chart to reflect changes return true; // Prompt shown successfully }
Управление паролями
В настоящее время для первоначального тестирования мы используем простой жестко запрограммированный пароль, что позволяет нам быстро создавать прототипы функциональности. Однако мы полностью понимаем, что такой подход несет в себе риски, такие как уязвимость к атакам методом подбора пароля в случае взлома кода.
// Default password for authentication string Password = "2024";
Хотя использование жестко заданного пароля ускоряет нашу разработку, в будущих обновлениях нам необходимо перейти к более безопасному решению, в частности, внедрить зашифрованные файлы конфигурации или использовать более сложную систему управления учетными записями пользователей для повышения безопасности.
Обработка пользовательского ввода
Для повышения безопасности нам необходимо убедиться, что поле ввода пароля четко определено в диалоговом окне аутентификации. Помогая пользователям вводить пароли и сверяя введенные данные с сохраненным паролем, мы стремимся обеспечить бесперебойный и безопасный процесс входа в систему.// Handle login button click void OnLoginButtonClick() { string enteredPassword = passwordInputBox.Text(); if (enteredPassword == Password) // Check the entered password { authentication.Destroy(); // Hide the authentication dialog Print("Authentication successful."); adminPanel.Show(); // Show the admin panel after successful authentication } else { Print("Incorrect password. Please try again."); passwordInputBox.Text(""); // Clear the password input } }
В функции OnLoginButtonClick программа проверяет, совпадает ли введенный пароль с сохраненным. При успешном входе диалоговое окно аутентификации скрывается, и пользователю предоставляется панель администратора. Если пароль неверный, поле ввода очищается и пользователю предлагается повторить попытку.
У нас также есть обработчик кнопки Close, который отвечает за логику выхода. При нажатии этой кнопки диалоговое окно аутентификации закрывается, а советник удаляется с графика. Это действие усиливает безопасность и обеспечивает понятный путь выхода для пользователей, которые решили не проходить аутентификацию. Определение обработчика:
//+------------------------------------------------------------------+ //| Handle close button click for authentication | //+------------------------------------------------------------------+ void OnCloseAuthButtonClick() { authentication.Destroy(); ExpertRemove(); // Remove the expert if user closes the authentication dialog Print("Authentication dialog closed."); }
В обработчике метод authentication. Destroy() закрывает диалог, в то время как ExpertRemove() обеспечивает полное отсутствие видимости советника, повышая общую безопасность приложения.
Полная интеграция в основную программу:
//+------------------------------------------------------------------+ //| Admin Panel.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.19" #include <Trade\Trade.mqh> #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> // Input parameters input string QuickMessage1 = "Updates"; input string QuickMessage2 = "Close all"; input string QuickMessage3 = "In deep profits"; input string QuickMessage4 = "Hold position"; input string QuickMessage5 = "Swing Entry"; input string QuickMessage6 = "Scalp Entry"; input string QuickMessage7 = "Book profit"; input string QuickMessage8 = "Invalid Signal"; input string InputChatId = "Enter Chat ID from Telegram bot API"; input string InputBotToken = "Enter BOT TOKEN from your Telegram bot"; // Global variables CDialog adminPanel; CDialog authentication; // Renamed from passwordPanel CButton sendButton, clearButton, changeFontButton, toggleThemeButton, loginButton, closeAuthButton; CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton; CEdit inputBox, passwordInputBox; CLabel charCounter, passwordPromptLabel; bool minimized = false; bool darkTheme = false; int MAX_MESSAGE_LENGTH = 4096; string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman", "Britannic Bold", "Dubai Medium", "Impact", "Ink Tree", "Brush Script MT"}; int currentFontIndex = 0; // Default password for authentication string Password = "2024"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if (!ShowAuthenticationPrompt()) { Print("Authorization failed. Exiting..."); return INIT_FAILED; // Exit if the authorization fails } // Initialize the main admin panel if (!adminPanel.Create(ChartID(), "Admin Panel", 0, 30, 30, 500, 500)) { Print("Failed to create admin panel dialog"); return INIT_FAILED; } // Create controls for the admin panel if (!CreateControls()) { Print("Control creation failed"); return INIT_FAILED; } // Initially hide the admin panel adminPanel.Hide(); Print("Initialization complete"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Show authentication input dialog | //+------------------------------------------------------------------+ bool ShowAuthenticationPrompt() { if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create authentication dialog"); return false; } // Create password input if (!passwordInputBox.Create(ChartID(), "PasswordInputBox", 0, 20, 70, 260, 95)) { Print("Failed to create password input box"); return false; } authentication.Add(passwordInputBox); // Create prompt label if (!passwordPromptLabel.Create(ChartID(), "PasswordPromptLabel", 0, 20, 20, 260, 20)) { Print("Failed to create password prompt label"); return false; } passwordPromptLabel.Text("Enter password: Access Admin Panel"); authentication.Add(passwordPromptLabel); // Create login button if (!loginButton.Create(ChartID(), "LoginButton", 0, 20, 120, 100, 140)) { Print("Failed to create login button"); return false; } loginButton.Text("Login"); authentication.Add(loginButton); // Create close button for authentication if (!closeAuthButton.Create(ChartID(), "CloseAuthButton", 0, 120, 120, 200, 140)) // Adjusted position { Print("Failed to create close button for authentication"); return false; } closeAuthButton.Text("Close"); authentication.Add(closeAuthButton); authentication.Show(); // Show the authentication dialog ChartRedraw(); // Redraw the chart to reflect changes return true; // Prompt shown successfully } //+------------------------------------------------------------------+ //| Handle chart events | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK) { // Handle button clicks inside the authentication dialog if (sparam == "LoginButton") { OnLoginButtonClick(); // Call the login button handler } else if (sparam == "CloseAuthButton") // Made sure this matches the ID { OnCloseAuthButtonClick(); // Call the close button handler } } switch (id) { case CHARTEVENT_OBJECT_CLICK: if (sparam == "SendButton") OnSendButtonClick(); else if (sparam == "ClearButton") OnClearButtonClick(); else if (sparam == "ChangeFontButton") OnChangeFontButtonClick(); else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick(); else if (sparam == "MinimizeButton") OnMinimizeButtonClick(); else if (sparam == "MaximizeButton") OnMaximizeButtonClick(); else if (sparam == "CloseButton") OnCloseButtonClick(); else if (StringFind(sparam, "QuickMessageButton") != -1) { long index = StringToInteger(StringSubstr(sparam, 18)); OnQuickMessageButtonClick(index - 1); } break; case CHARTEVENT_OBJECT_ENDEDIT: if (sparam == "InputBox") OnInputChange(); break; } } //+------------------------------------------------------------------+ //| Handle login button click | //+------------------------------------------------------------------+ void OnLoginButtonClick() { string enteredPassword = passwordInputBox.Text(); if (enteredPassword == Password) // Check the entered password { authentication.Destroy(); // Hide the authentication dialog Print("Authentication successful."); adminPanel.Show(); // Show the admin panel after successful authentication } else { Print("Incorrect password. Please try again."); passwordInputBox.Text(""); // Clear the password input } } //+------------------------------------------------------------------+ //| Handle close button click for authentication | //+------------------------------------------------------------------+ void OnCloseAuthButtonClick() { authentication.Destroy(); ExpertRemove(); // Remove the expert if user closes the authentication dialog Print("Authentication dialog closed."); } //+------------------------------------------------------------------+ //| Create necessary UI controls | //+------------------------------------------------------------------+ bool CreateControls() { long chart_id = ChartID(); // Create the input box if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95)) { Print("Failed to create input box"); return false; } adminPanel.Add(inputBox); // Character counter if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25)) { Print("Failed to create character counter"); return false; } charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH)); adminPanel.Add(charCounter); // Clear button if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125)) { Print("Failed to create clear button"); return false; } clearButton.Text("Clear"); adminPanel.Add(clearButton); // Send button if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125)) { Print("Failed to create send button"); return false; } sendButton.Text("Send"); adminPanel.Add(sendButton); // Change font button if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115)) { Print("Failed to create change font button"); return false; } changeFontButton.Text("Font<>"); adminPanel.Add(changeFontButton); // Toggle theme button if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115)) { Print("Failed to create toggle theme button"); return false; } toggleThemeButton.Text("Theme<>"); adminPanel.Add(toggleThemeButton); // Minimize button if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0)) // Adjusted Y-coordinate for visibility { Print("Failed to create minimize button"); return false; } minimizeButton.Text("_"); adminPanel.Add(minimizeButton); // Maximize button if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0)) // Adjusted Y-coordinate for visibility { Print("Failed to create maximize button"); return false; } maximizeButton.Text("[ ]"); adminPanel.Add(maximizeButton); // Close button for admin panel if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0)) // Adjusted Y-coordinate for visibility { Print("Failed to create close button"); return false; } closeButton.Text("X"); adminPanel.Add(closeButton); // Quick messages return CreateQuickMessageButtons(); } //+------------------------------------------------------------------+ //| Create quick message buttons | //+------------------------------------------------------------------+ bool CreateQuickMessageButtons() { string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; int startX = 5, startY = 160, width = 222, height = 65, spacing = 5; for (int i = 0; i < 8; i++) { if (!quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0, startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height)) { Print("Failed to create quick message button ", i + 1); return false; } quickMessageButtons[i].Text(quickMessages[i]); adminPanel.Add(quickMessageButtons[i]); } return true; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { adminPanel.Destroy(); Print("Deinitialization complete"); } //+------------------------------------------------------------------+ //| Handle custom message send button click | //+------------------------------------------------------------------+ void OnSendButtonClick() { string message = inputBox.Text(); if (message != "") { if (SendMessageToTelegram(message)) Print("Custom message sent: ", message); else Print("Failed to send custom message."); } else { Print("No message entered."); } } //+------------------------------------------------------------------+ //| Handle clear button click | //+------------------------------------------------------------------+ void OnClearButtonClick() { inputBox.Text(""); OnInputChange(); Print("Input box cleared."); } //+------------------------------------------------------------------+ //| Handle quick message button click | //+------------------------------------------------------------------+ void OnQuickMessageButtonClick(int index) { string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 }; string message = quickMessages[index]; if (SendMessageToTelegram(message)) Print("Quick message sent: ", message); else Print("Failed to send quick message."); } //+------------------------------------------------------------------+ //| Update character counter | //+------------------------------------------------------------------+ void OnInputChange() { int currentLength = StringLen(inputBox.Text()); charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH)); ChartRedraw(); } //+------------------------------------------------------------------+ //| Handle toggle theme button click | //+------------------------------------------------------------------+ void OnToggleThemeButtonClick() { darkTheme = !darkTheme; UpdateThemeColors(); Print("Theme toggled: ", darkTheme ? "Dark" : "Light"); } //+------------------------------------------------------------------+ //| Update theme colors for the panel | //+------------------------------------------------------------------+ void UpdateThemeColors() { // Use the dialog's theme update method as a placeholder. adminPanel.UpdateThemeColors(darkTheme); color textColor = darkTheme ? clrWhite : clrBlack; color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro; color borderColor = darkTheme ? clrSlateGray : clrGray; color bgColor = darkTheme ? clrDarkBlue : clrWhite; inputBox.SetTextColor(textColor); inputBox.SetBackgroundColor(bgColor); inputBox.SetBorderColor(borderColor); UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(maximizeButton, textColor, buttonBgColor, borderColor); UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor); for (int i = 0; i < ArraySize(quickMessageButtons); i++) { UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor); } charCounter.Color(textColor); ChartRedraw(); } //+------------------------------------------------------------------+ //| Apply theme settings to a button | //+------------------------------------------------------------------+ void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor) { button.SetTextColor(textColor); button.SetBackgroundColor(bgColor); button.SetBorderColor(borderColor); } //+------------------------------------------------------------------+ //| Handle change font button click | //+------------------------------------------------------------------+ void OnChangeFontButtonClick() { currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts); inputBox.Font(availableFonts[currentFontIndex]); clearButton.Font(availableFonts[currentFontIndex]); sendButton.Font(availableFonts[currentFontIndex]); toggleThemeButton.Font(availableFonts[currentFontIndex]); changeFontButton.Font(availableFonts[currentFontIndex]); for (int i = 0; i < ArraySize(quickMessageButtons); i++) { quickMessageButtons[i].Font(availableFonts[currentFontIndex]); } Print("Font changed to: ", availableFonts[currentFontIndex]); ChartRedraw(); } //+------------------------------------------------------------------+ //| Handle minimize button click | //+------------------------------------------------------------------+ void OnMinimizeButtonClick() { minimized = true; adminPanel.Hide(); minimizeButton.Hide(); maximizeButton.Show(); closeButton.Show(); Print("Panel minimized."); } //+------------------------------------------------------------------+ //| Handle maximize button click | //+------------------------------------------------------------------+ void OnMaximizeButtonClick() { if (minimized) { adminPanel.Show(); minimizeButton.Show(); maximizeButton.Hide(); closeButton.Hide(); Print("Panel maximized."); } } //+------------------------------------------------------------------+ //| Handle close button click for admin panel | //+------------------------------------------------------------------+ void OnCloseButtonClick() { ExpertRemove(); Print("Admin Panel closed."); } //+------------------------------------------------------------------+ //| Send the message to Telegram | //+------------------------------------------------------------------+ bool SendMessageToTelegram(string message) { string url = "https://api.telegram.org/bot" + InputBotToken + "/sendMessage"; string jsonMessage = "{\"chat_id\":\"" + InputChatId + "\", \"text\":\"" + message + "\"}"; char post_data[]; ArrayResize(post_data, StringToCharArray(jsonMessage, post_data, 0, WHOLE_ARRAY) - 1); int timeout = 5000; char result[]; string responseHeaders; int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders); if (res == 200) { Print("Message sent successfully: ", message); return true; } else { Print("Failed to send message. HTTP code: ", res, " Error code: ", GetLastError()); Print("Response: ", CharArrayToString(result)); return false; } }
Тестирование и результаты
Наш код успешно скомпилировался, и после запуска приложения мы обнаружили, что все функции панели остаются недоступными до тех пор, пока не будет введен правильный PIN-код. Такое поведение гарантирует, что доступ к функциям панели смогут получить только авторизованные пользователи. Мы понимаем, что наши меры безопасности все еще нуждаются в улучшении, поскольку они могут быть уязвимы для опытных хакеров. Мы знаем, что каждый наш шаг — это возможность узнать больше о реализации языка MQL5, и по мере развития наших навыков мы сможем достичь более надежных уровней безопасности. Ниже показаны запуск приложения и желаемый результат.
Запуск панели
Заключение
В этом проекте реализация механизма аутентификации при входе в систему значительно повысила безопасность панели администратора, что имеет решающее значение для защиты конфиденциального функционала. Запрашивая пароль перед предоставлением доступа к функциям администратора, программа снижает риск несанкционированного использования и гарантирует, что только проверенные пользователи смогут управлять важными настройками и операциями. Мы ввели глобальный пароль и удобный интерфейс для ввода учетных данных.
По мере совершенствования нашей панели администратора мы сосредоточимся на таких важных улучшениях, как переход от жестко заданных паролей к безопасно управляемым учетным данным для предотвращения уязвимостей, внедрение многофакторной аутентификации для дополнительной безопасности и постоянная оптимизация процесса входа в систему.
С другой стороны, мы признаем, что после компиляции любому пользователю без исходного кода становится сложно получить к нему доступ благодаря функциям защиты от декомпиляции, предлагаемым MQL5. Этот дополнительный уровень защиты помогает защитить наше приложение от несанкционированного доступа и обратного проектирования.
Попробуйте применить этот функционал в своих проектах! Я приветствую комментарии и отзывы. Ваши идеи могут помочь нам улучшить и усовершенствовать нашу работу.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16079





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования