
Erstellen eines Handelsadministrator-Panels in MQL5 Teil IV: Login-Sicherheitsschicht
Inhaltsübersicht:
- Einführung
- Überblick über die Sicherheit in MQL5
- Kurze Zusammenfassung von Teil III
- Integration eines Passwortschutzes in das Admin-Panel
- Tests und Ergebnisse
- Schlussfolgerung
Einführung
Sicherheit ist in jedem Fachgebiet von größter Bedeutung, und wir können es uns nicht leisten, ihre Bedeutung zu übersehen. Angesichts der ständigen Bedrohung durch unbefugten Zugriff ist es wichtig, unser Admin-Panel vor potenziellen Eindringlingen zu schützen. Wenn sich Unbefugte Zugang verschaffen, könnten sie das Panel leicht manipulieren und unsere Kommunikationsbemühungen mit der Rundfunkgemeinschaft gefährden. Der Hauptzweck dieses Systems ist die Erleichterung einer zuverlässigen Kommunikation, und obwohl wir die Funktionalität auf der Ebene des Expert Advisors verbessern können, bleibt das Risiko des Eindringens erheblich.
Ein Angreifer, der auf das Dashboard zugreift, könnte irreführende Nachrichten an unsere Nutzer senden, Verwirrung stiften und den Ruf des Systemadministrators schädigen. Um diese Risiken zu mindern, ist es meiner Meinung nach unerlässlich, eine Sicherheitsebene zu implementieren, die den Zugang zu wichtigen Funktionen ohne die richtigen Anmeldedaten einschränkt. Dieser unkomplizierte Sicherheitsansatz schützt unser Panel und trägt außerdem dazu bei, die Integrität unserer Kommunikation und das Vertrauen unserer Gemeinschaft zu erhalten.
Anmelde-Panel
Überblick über die Sicherheit in MQL5
MQL5 bietet eine umfassende Reihe von Sicherheitsfunktionen, die sowohl den Quellcode als auch die kompilierten Dateien (EX5) schützen, das geistige Eigentum bewahren und eine unberechtigte Nutzung verhindern. Zu den wichtigsten Mechanismen gehören die Verschlüsselung der kompilierten Dateien, die konto- und zeitbasierte Lizenzierung und die Integration externer DLLs für zusätzlichen Schutz. Die Plattform unterstützt digitale Signaturen zur Überprüfung der Code-Authentizität, während MetaQuotes den Code durch Kompilierung und Verschleierung schützt, um Reverse Engineering zu verhindern. Bei Produkten, die über den MQL5-Markt vertrieben werden, sorgt eine zusätzliche Verschlüsselung dafür, dass nur lizenzierte Nutzer auf die Software zugreifen und sie nutzen können, wodurch ein robuster Sicherheitsrahmen für Entwickler geschaffen wird.
2012 diskutierte Investeo, ein Autor bei MQL5, verschiedene Methoden zur Sicherung von MQL5-Programmen und -Code und gab wertvolle Einblicke in die Implementierung von Techniken wie Passwortschutz, Schlüsselgenerierung, Einzelkonto-Lizenzierung, zeitlich begrenzter Schutz, Fernlizenzen, sichere Lizenzverschlüsselung und fortschrittliche Anti-Dekompilierungsmethoden. Seine Arbeit dient als grundlegende Referenz für die Verbesserung der Programmsicherheit.
Ziel der Diskussion:
Da wir wissen, wie wichtig die Sicherheit ist, wollen wir die Implementierung eines Passwortschutzes für den Zugriff auf die Funktionen des Admin-Panels diskutieren. Wir werden uns mit den Techniken befassen, die verwendet werden, um die in der vorherigen Abbildung gezeigten Ergebnisse zu erzielen, und uns darauf konzentrieren, wie wir den Nutzerzugang wirksam schützen können.
Bereiche mit Sicherheitsbedenken in unserem Admin-Panel:
Da sich unser Programm weiterentwickelt und neue Funktionen enthält, sind wir uns der wachsenden Komplexität bewusst, insbesondere für unerfahrene Entwickler. Wir haben mehrere Schlüsselbereiche identifiziert, die für die Sicherheit von Interesse sind:
- Gesicherter Zugang zum Admin-Panel:
Um sicherzustellen, dass keine unbefugten Nutzer ohne das korrekte Passwort auf das Admin-Panel zugreifen können, implementieren wir einen Passwortschutz. Ein unbefugter Zugriff könnte dazu führen, dass unbeabsichtigte Nachrichten an die Gemeinschaft der Wirtschaftsbeteiligten, die sich auf die Erkenntnisse der Administratoren verlassen, verbreitet werden. Zufällige Klicks auf Schnelltasten könnten ohne echte Absicht erfolgen, weshalb ein sicheres Passwort unerlässlich ist. Während viele Anwendungen die Zwei-Faktor-Authentifizierung (2FA) zur zusätzlichen Verifizierung einsetzen, konzentrieren wir uns derzeit auf die Implementierung grundlegender Sicherheitsfunktionen und planen, im Laufe der Zeit weitere fortschrittliche Optionen einzubauen.
- Sicherheit von Telegram-API-Nachrichten:
Wir legen auch großen Wert auf die Sicherheit der Kommunikation über die Telegram-API, indem wir die Chat-ID und das Bot-Token beim Programmstart sicher eingeben. Auf diese Weise wird sichergestellt, dass sensible Daten in den Händen des Nutzers geschützt bleiben. Telegram verwendet robuste Sicherheitsfunktionen, um die Kommunikation der Nutzer zu schützen, einschließlich Transport Layer Security durch das MTProto Protokoll für Standard-Chats und Ende-zu-Ende-Verschlüsselung für Secret Chats. Darüber hinaus unterstützt Telegram 2FA, sodass Nutzer aktive Sitzungen verwalten und die Kontosicherheit erhöhen können. Die Sicherheitsprotokolle von Telegram sind zwar stark, aber die Nutzer müssen auch sicherstellen, dass ihre Geräte sicher sind, da kompromittierte Geräte diese Schutzmaßnahmen untergraben können.
Kurze Zusammenfassung von Teil III
In der vorangegangenen Diskussion haben wir uns mit der Einbeziehung von Methoden für das Themenmanagement befasst. Wir haben jedoch mit Dateien gearbeitet, die bei Aktualisierungen der MetaTrader 5-Plattform geändert werden können. Jedes Mal, wenn ein Update veröffentlicht wird, wird es automatisch heruntergeladen und beim erneuten Start installiert. Der folgende Codeausschnitt veranschaulicht die Fehler, die bei der Kompilierung nach den Aktualisierungen auftraten.
'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
Vorläufige Lösung
Um das Problem zu lösen, muss man zunächst die Ursache des Problems verstehen. Wie bereits erläutert, werden durch die Plattformaktualisierung die von uns verwendeten Bibliotheken auf ihren Standardzustand zurückgesetzt. Folglich sind die Methoden, die wir für die Themenverwaltung implementiert haben, nicht mehr gültig, weshalb wir jetzt auf Fehler stoßen. Um dies zu beheben, müssen wir die aktualisierten Dateien (Dialog.mqh, Edit.mqh und Button.mqh) mit den erweiterten Versionen überschreiben, die ich im vorherigen Artikel angefügt habe. Sie können den Ordner für die Include-Dateien wie in der folgenden Abbildung gezeigt finden.
Einfaches Auffinden des Stammordners dialog.mqh
Dauerhafte Lösung:
Sie können die Datei Dialog.mqh und andere zugehörige Dateien bei der Verwendung in Extended_Dialog.mqh umbenennen und unseren Code entsprechend anpassen, aber stellen Sie sicher, dass alle #include-Anweisungen, die auf den alten Dateinamen verweisen, auf den neuen Namen aktualisiert werden. Außerdem müssen wir prüfen, ob es andere Abhängigkeiten gibt, die darauf verweisen, und diese bei Bedarf aktualisieren. Nachdem wir diese Änderungen vorgenommen haben, kompilieren wir unser Projekt neu, um mögliche Fehler zu erkennen, und testen die Funktionalität gründlich, um sicherzustellen, dass alles korrekt funktioniert. Dadurch wird sie separat unter dem neuen Namen gespeichert, die Originaldatei bleibt jedoch erhalten.
Wenn wir zum Beispiel die Datei bereits als Extended_Dialog.mqh gespeichert haben, können wir zu unserem Admin-Panel navigieren und den Code wie folgt anpassen:
#include <Controls\Extended_Dialog.mqh> #include <Controls\Extended_Button.mqh> #include <Controls\Extended_Edit.mqh> #include <Controls\Label.mqh>
Vorteile des Speicherns unter einem anderen Namen
Sie bietet die Möglichkeit, die Funktionalität speziell auf Ihre Bedürfnisse zuzuschneiden, indem sie Funktionen hinzufügt oder anpasst, die in der integrierten Version nicht vorhanden sind. Diese Anpassung ermöglicht es Ihnen, eine einzigartige Schnittstelle zu schaffen, die Ihren Anforderungen entspricht. Außerdem lassen sich durch die Verwendung nutzerdefinierter Dateinamen Konflikte mit integrierten Bibliotheken oder Bibliotheken von Drittanbietern vermeiden, wodurch das Risiko unerwarteter Verhaltensweisen aufgrund von Namensüberschneidungen verringert wird. Die Isolierung Ihrer Erweiterungen in einer umbenannten Datei schützt Ihre Anpassungen davor, von anderen eingebauten Funktionen, die den ursprünglichen Dialog verwenden könnten, beeinträchtigt zu werden, und gewährleistet, dass Sie Ihr Projekt ohne Beeinträchtigung durch externe Änderungen entwickeln und pflegen können.
Integration eines Passwortschutzes in das Admin-Panel
In diesem Projekt werden wir einen bedingten Passwortschutz implementieren, bei dem ein Passwort vom Typ string verwendet wird, das sowohl Buchstaben als auch Zahlen enthalten kann, was die Komplexität erhöht. Eine vierstellige PIN mag zwar einfach erscheinen, ist aber dennoch schwer zu erraten. Im Admin-Panel verwenden wir die Klasse Dialog, um den Nutzer bei der Anmeldung zur Eingabe eines Passworts aufzufordern, wobei die Bedingungen so festgelegt sind, dass die Hauptfunktionen des Panels erst nach erfolgreicher Passworteingabe angezeigt werden.
Bei der weiteren Entwicklung des Admin-Panel-Programms liegt unser Hauptaugenmerk auf der Einrichtung einer soliden Anmeldesicherheit, um sicherzustellen, dass nur autorisierte Nutzer auf sensible Verwaltungsfunktionen zugreifen können. Wir haben erkannt, wie wichtig es ist, unser System vor unbefugtem Zugriff zu schützen, und diskutieren, wie wir MQL5 zur Sicherung unserer Produkte einsetzen können.
Mechanismus zur Authentifizierung
Zur Absicherung des Admin-Panels implementieren wir einen einfachen, passwortbasierten Authentifizierungsmechanismus, der die Nutzer zur Eingabe eines Passworts auffordert, bevor der Zugriff auf Funktionen gewährt wird. Diese Entscheidung spiegelt unser Engagement für die Überprüfung der Nutzeridentität als Voraussetzung für den Zugriff auf wichtige Komponenten des Programms wider.
// Show authentication input dialog bool ShowAuthenticationPrompt() { if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create authentication dialog"); return false; } }
Mit der Funktion ShowAuthenticationPrompt entwerfen wir eine nutzerfreundliche Oberfläche, die unsere Nutzer effektiv durch den Authentifizierungsprozess führt. Durch die Einrichtung eines speziellen Dialogs für die Passworteingabe stellen wir sicher, dass der primäre Zugangspunkt zum Verwaltungsbereich sicher und gleichzeitig intuitiv bleibt.
Zum besseren Verständnis habe ich den Code für die Erstellung von Dialogen im folgenden Ausschnitt dargestellt und mit Kommentaren versehen, um die Funktionsweise zu erklären. Wenn Sie Ihr Wissen über Achsen und Koordinaten auffrischen möchten, lesen Sie bitte Teil I.
// 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 ))
Nachdem wir den Authentifizierungsdialog eingerichtet haben, ordnen wir die anderen Elemente der Nutzeroberfläche ähnlich an, wenn auch mit anderen Werten. Der Prozess beginnt mit der Erstellung eines Passwort-Eingabefelds, in das die Nutzer ihre Anmeldedaten eingeben können, gefolgt von wichtigen Schaltflächen. Wir konzentrieren uns insbesondere auf zwei Hauptschaltflächen: die Schaltfläche „Anmelden“ und die Schaltfläche „Schließen“. Die Schaltfläche „Login“ dient zur Übermittlung des eingegebenen Passworts, während die Schaltfläche „Close“ dem Nutzer die Möglichkeit bietet, den Dialog zu verlassen, wenn er das Passwort nicht kennt. Nachfolgend finden Sie einen Codeausschnitt, der die Logik für die Erstellung dieser Schaltflächen und die Beschriftung der Kennwortaufforderung veranschaulicht.
// 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 }
Passwortverwaltung
Zurzeit verwenden wir für erste Tests ein einfaches, fest kodiertes Kennwort, mit dem wir die Funktionalität schnell testen können. Wir sind uns jedoch darüber im Klaren, dass dieser Ansatz Risiken birgt, z. B. die Anfälligkeit für Brute-Force-Angriffe, wenn der Code kompromittiert wird.
// Default password for authentication string Password = "2024";
Wir sind uns zwar bewusst, dass die Verwendung eines hart kodierten Passworts unsere Entwicklung beschleunigt, aber wir müssen in zukünftigen Updates zu einer sichereren Lösung übergehen, d. h. verschlüsselte Konfigurationsdateien implementieren oder ein ausgefeilteres System zur Verwaltung von Nutzerkonten verwenden, um die Sicherheit zu erhöhen.
Umgang mit Nutzereingaben
Um die Sicherheit zu erhöhen, müssen wir sicherstellen, dass das Passwort-Eingabefeld im Authentifizierungsdialog klar definiert ist. Indem wir die Nutzer zur Eingabe ihrer Passwörter anleiten und diese Eingaben mit dem gespeicherten Passwort abgleichen, sorgen wir für ein nahtloses und sicheres Anmeldeerlebnis.// 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 } }
In der Funktion OnLoginButtonClick prüft das Programm, ob das eingegebene Passwort mit dem gespeicherten Passwort übereinstimmt. Nach erfolgreicher Eingabe wird der Authentifizierungsdialog ausgeblendet und dem Nutzer das Verwaltungsfenster angezeigt. Wenn das Kennwort falsch ist, wird das Eingabefeld gelöscht und der Nutzer aufgefordert, es erneut zu versuchen, um sicherzustellen, dass er den Vorgang versteht und sich bei der Anmeldung sicher fühlt.
Wir haben auch eine Ereignisbehandlung für die Schaltfläche „Close“, der für die Exit-Logik verantwortlich ist. Wenn diese Schaltfläche angeklickt wird, wird der Authentifizierungsdialog geschlossen und der Experte vollständig aus dem Chart entfernt, sodass kein Zugriff auf die Verwaltungsfunktionen mehr möglich ist. Diese Maßnahme erhöht die Sicherheit und bietet einen klaren Ausstiegsweg für Nutzer, die sich gegen eine weitere Authentifizierung entscheiden. So wird die Ereignisbehandlung definiert:
//+------------------------------------------------------------------+ //| Handle close button click for authentication | //+------------------------------------------------------------------+ void OnCloseAuthButtonClick() { authentication.Destroy(); ExpertRemove(); // Remove the expert if user closes the authentication dialog Print("Authentication dialog closed."); }
Das schließt die Authentifizierung. Destroy() schließt den Dialog effektiv, während ExpertRemove() sicherstellt, dass der Expert Advisor vollständig aus dem Chart entfernt wird, was die Sicherheit der Anwendung insgesamt erhöht
Vollständig in das Hauptprogramm integriert:
//+------------------------------------------------------------------+ //| 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; } }
Tests und Ergebnisse
Unser Code wurde erfolgreich kompiliert, und beim Starten der Anwendung stellten wir fest, dass die vollständigen Funktionen des Panels unzugänglich bleiben, bis die richtige PIN eingegeben wird. Dieses Verhalten stellt sicher, dass nur autorisierte Nutzer auf die Verwaltungsfunktionen zugreifen können. In diesem Stadium sind wir stolz auf unsere Fortschritte, doch wir sind uns bewusst, dass wir die Grenzen unserer Entwicklung noch nicht erreicht haben. Wir sind uns bewusst, dass unsere Sicherheitsmaßnahmen noch verbessert werden müssen, da sie für fortgeschrittene Hacker anfällig sein können. Wir wissen, dass jeder Schritt, den wir unternehmen, eine Gelegenheit ist, mehr über die Implementierung der MQL5-Sprache zu lernen, und je weiter wir unsere Fähigkeiten ausbauen, desto stabilere Sicherheitsstufen können wir erreichen. Unten sehen Sie ein Bild, das den Start der Anwendung zusammen mit dem gewünschten Ergebnis zeigt.
Start des Panels
Schlussfolgerung
In diesem Projekt wurde durch die Implementierung eines Login-Authentifizierungsmechanismus die Sicherheit des Admin-Panels erheblich verbessert, was für den Schutz sensibler Funktionen unerlässlich ist. Da für den Zugriff auf die Verwaltungsfunktionen ein Kennwort erforderlich ist, verhindert das Programm eine unbefugte Nutzung und stellt sicher, dass nur verifizierte Nutzer wichtige Einstellungen und Vorgänge verwalten können. Das Design wird durch ein klar definiertes globales Passwort und eine nutzerfreundliche Schnittstelle für die Eingabe von Anmeldeinformationen verstärkt.
Bei der Weiterentwicklung unseres Admin-Panels werden wir uns auf wichtige Verbesserungen konzentrieren, wie z. B. die Umstellung von hart kodierten Passwörtern auf sicher verwaltete Anmeldedaten, um Schwachstellen zu vermeiden, die Integration von Mehrfaktor-Authentifizierung für zusätzliche Sicherheit und die kontinuierliche Optimierung der Anmeldefunktion.
Andererseits wissen wir, dass es für jeden, der keinen Zugang zum Quellcode hat, schwierig wird, sich Zugang zu verschaffen, sobald der Code kompiliert ist, dank der von MQL5 gebotenen Sicherheitsfunktionen gegen Dekompilierung. Dieser zusätzliche Schutz trägt dazu bei, unsere Anwendung vor unbefugtem Zugriff und Reverse Engineering zu schützen.
Bitte zögern Sie nicht, es in Ihren Projekten auszuprobieren! Ich freue mich über Kommentare und Rückmeldungen, denn Ihre Erkenntnisse können uns helfen, unsere Arbeit zu verbessern und zu verfeinern. Ihre Ansichten sind für uns sehr wertvoll, da wir unsere Anwendungen weiter entwickeln und verbessern wollen. Sehen Sie sich den Anhang unten an.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16079





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.