
在MQL5中创建交易管理员面板(第四部分):登录安全层
目录表:
概述
在任何专业领域,安全都是至关重要的,我们不能忽视其重要性。面对未经授权访问的持续威胁,保护我们的管理员面板免受潜在入侵者的影响至关重要。如果未经授权的个人获得访问权限,他们可以轻松操纵面板,危及我们与广播社区的沟通工作。该系统的首要目的是促进可靠的沟通,尽管我们可以在EA层面增强功能,但入侵的风险仍然很大。
攻击者访问面板后,可能会向我们的用户发送误导性信息,造成混乱并损害系统管理员的声誉。为了减轻这些风险,我相信有必要实施一个安全层,在用户没有正确凭证的情况下限制其对关键功能的访问。这种简单直接的安全方式可以保护我们的面板,同时也有助于维护我们通信的完整性和社区的可信度。
登录面板
MQL5安全概述
MQL5提供了一系列全面的安全功能,旨在保护源代码和编译文件(EX5),保护知识产权并防止未经授权的使用。主要机制包括编译文件的加密、基于账户和基于时间的许可,以及与外部DLL集成以提供额外保护。该平台支持数字签名以验证代码的真实性,而MetaQuotes通过编译和混淆提供代码保护,以防止逆向工程。对于通过MQL5市场分发的产品,额外的加密确保只有获得许可的用户才能访问和使用软件,为开发人员构建了一个强大的安全框架。
2012年,MQL5的作者Investeo讨论了保护MQL5程序和代码的各种方法,分享了关于实现密码保护、密钥生成、单账户许可、时间限制保护、远程许可、安全许可加密和高级反解码方法等技术的宝贵见解。他的工作为增强程序安全性提供了一个基础性的参考。
讨论目标:
认识到安全的重要性,我们旨在讨论为访问管理员面板的功能实现密码保护。我们将深入探讨用于实现前图中展示结果的技术,重点关注如何能有效地保护用户的访问。
我们管理员面板上需要关注的安全领域:
随着程序不断完善并纳入新功能,我们承认其复杂性在增加,特别是对于新手开发人员。我们确定了几个与安全性相关的重点领域:
- 管理员面板的安全访问:
为了确保未经授权的用户无法在没有正确密码的情况下访问管理员面板,我们实现了密码保护。未经授权的访问可能会导致本应仅限内部的信息被不当传播给依赖管理员信息的交易者群体。用户可能会在无意的情况下随意点击快速按钮,因此设置一个安全的密码至关重要。尽管许多应用程序采用双重认证(2FA)进行额外验证,但我们当前的重点仍然是实现基础安全功能,随着后续的发展,计划纳入更高级的选项。
- Telegram API消息的安全性:
我们还优先考虑采用Telegram API实现通信的安全性,通过在程序启动时安全地输入聊天ID和机器人令牌来实现。这种方法确保敏感数据在用户端持续受保护。Telegram采用强大的安全功能来保护用户通信,包括通过MTProto协议为标准聊天提供传输层安全,以及为秘密聊天提供端到端加密。此外,Telegram支持双重认证(2FA),允许用户管理活跃会话并增强账户的安全性。尽管Telegram的安全协议非常强大,但用户也必须确保自身的设备安全,因为被入侵的设备可能会破坏这些保护。
简要回顾(第三部分)
在之前的讨论中,我们提到了纳入主题管理方法。然而,我们当时处理的文件可能会在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>
使用不同名称保存的优势
其提供了根据您的需求定制功能的能力,通过添加或调整内置版本中不存在的特性。这种定制使您能够创建一个满足需求的独特界面。此外,使用自定义文件名有助于避免与内置库或第三方库发生冲突,减少因名称重叠而导致误操作的风险。将您的增强功能隔离在重命名的文件中,进一步保护自定义内容免受可能使用原始对话框的其他内置功能的影响,确保您可以在开发和维护项目时不受外部变化的干扰。
在管理员面板上集成密码保护
在本项目中,我们将使用包含字母和数字的字符串类型密码实现有条件的密码保护,以增强复杂性。尽管四位数字的PIN码看起来简单,但仍然难以猜测。在管理员面板上,我们使用对话框类在登录时提示用户输入密码,并设置条件,只有在成功输入密码后才显示主面板功能。
随着继续开发管理员面板程序,我们的主要关注点是建立强大的登录安全性,以确保只有授权用户才能访问敏感的管理功能。我们认识到保护系统免受未经授权的访问至关重要,并讨论如何使用MQL5来保护产品。
认证机制
为了保护管理员面板,我们实现了一个简单的基于密码的认证机制,在授予对任何功能的访问权限之前提示用户输入密码。这一选择体现了我们将验证用户身份作为访问程序关键组成部分的前提条件。
// 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函数中,我们设计了一个用户友好界面,有效地引导我们的用户完成认证过程。通过创建一个专门的密码输入对话框,我们确保了管理员面板的主要入口点既安全又直观。
为了强化理解,我在以下代码段中概述了创建对话框的代码,并附上了注释以解释其工作原理。如果您需要复习坐标轴和坐标,请参考(第一部分)。
// 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 ))
在构建了认证对话框之后,我们按照类似的方式部署其他用户界面(UI)元素,尽管它们的值不同。该过程从创建一个密码输入框开始,用户可以在其中输入他们的凭证,随后是必要的按钮。具体来说,我们专注于两个主要按钮:“登录”按钮和“关闭”按钮。“登录”按钮用于提交输入的密码,而“关闭”按钮为用户提供了一个选项,如果他们不知道密码,可以退出对话框。以下是一个代码段,展示了创建这些按钮以及密码提示标签的逻辑。
// 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函数中,程序会检查输入的密码是否与存储的密码相匹配。如果输入正确,会隐藏认证对话框并向用户展示管理员面板。如果密码错误,就会清空输入字段并提示用户重新尝试,确保用户在登录过程中清楚地理解流程,同时有安全保障。
我们还有一个“关闭”按钮的处理器,负责退出逻辑。当该按钮被点击时,就会关闭认证对话框,并且完全从图表中移除EA,确保不能继续使用管理员功能。这一行为增强了安全性,并为选择不继续认证的用户提供了明确的退出路径。以下是该处理器的定义方式:
//+------------------------------------------------------------------+ //| 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()确保EA完全从视图中被移除,增强了应用程序的整体安全性。
完全集成至主程序中:
//+------------------------------------------------------------------+ //| 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




在 MQL5 中创建管理员交易面板(第四部分) 一文已发布:登录安全:
作者:Clemence Benjamin
当尝试编译
Admin_Panel.mq5' 1
Trade.mqh
Object.mqh
StdLibErr.mqh
OrderInfo.mqh
历史订单信息
PositionInfo.mqh
DealInfo.mqh
Dialog.mqh
WndContainer.mqh
Wnd.mqh
Rect.mqh
定义
ArrayObj.mqh
数组
WndClient.mqh
面板
WndObj.mqh
ChartObjectsTxtControls.mqh
图表对象
滚动条
BmpButton.mqh
ChartObjectsBmpControls.mqh
编辑
图表对象
按钮
标签
'Up.bmp'作为资源"::res\Up.bmp" 1
ThumbVert.bmp' 作为资源 "::res\ThumbVert.bmp" 1
Down.bmp' 作为资源 "::res\Down.bmp" 1
Left.bmp' 作为资源 "::res\Left.bmp" 1
ThumbHor.bmp' 作为资源 "::res\ThumbHor.bmp" 1
'Right.bmp' 作为资源 "::res\Right.bmp" 1
Close.bmp' 作为资源 "::res\Close.bmp" 1
Restore.bmp' 作为资源 "::res\Restore.bmp" 1
'Turn.bmp'作为资源"::res\Turn.bmp" 1
从 "long "到 "int "的类型转换可能导致数据丢失 Admin_Panel(4)_.mq5 161 49
UpdateThemeColors' - 未声明的标识符 Admin_Panel(4)_.mq5 390 16
'darkTheme' - 期望使用某些运算符 Admin_Panel(4)_.mq5 390 34
SetTextColor' - 未声明标识符 Admin_Panel(4)_.mq5 397 14
textColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 397 27
SetBackgroundColor' - 未声明标识符 Admin_Panel(4)_.mq5 398 14
'bgColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 398 33
SetBorderColor' - 未声明标识符 Admin_Panel(4)_.mq5 399 14
'borderColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 399 29
SetTextColor' - 未声明标识符 Admin_Panel(4)_.mq5 424 12
textColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 424 25
SetBackgroundColor' - 未声明标识符 Admin_Panel(4)_.mq5 425 12
'bgColor' - 期望使用某个运算符 Admin_Panel(4)_.mq5 425 31
SetBorderColor' - 未声明标识符 Admin_Panel(4)_.mq5 426 12
'borderColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 426 27
14 个错误,1 个警告 15 2