
Criação de um painel de administração de trading no MQL5 (Parte IV): Segurança no login
Conteúdo:
- Introdução
- Segurança no MQL5
- Breve repetição da Parte III
- Integração de proteção por senha no painel de administração
- Testes e resultados
- Considerações finais
Introdução
A segurança é prioridade máxima em qualquer área de atuação e não podemos nos dar ao luxo de ignorar sua importância. Diante da ameaça constante de acessos não autorizados, é fundamental proteger o painel de administração contra possíveis invasores. Se pessoas não autorizadas obtiverem acesso, poderão facilmente controlá-lo e comprometer nossos esforços. O objetivo principal do sistema é garantir uma comunicação confiável. Mesmo que possamos melhorar a funcionalidade no nível do EA (Expert Advisor), os riscos de invasão continuam significativos.
Um invasor com acesso ao painel de controle pode enviar mensagens enganosas aos usuários, causando confusão e prejudicando a reputação do administrador do sistema. Para reduzir esses riscos, acredito que é necessário implementar um nível de segurança que restrinja o acesso a funções-chave sem as credenciais corretas. Essa abordagem de proteção não só defenderá nosso painel, como também ajudará a preservar a integridade de nossas comunicações e a confiança da comunidade.
Painel de login
Visão geral da segurança no MQL5
O MQL5 oferece um conjunto completo de recursos de segurança, projetados para proteger tanto o código-fonte quanto os arquivos compilados (EX5), garantindo a proteção da propriedade intelectual e evitando o uso não autorizado. Entre os principais mecanismos estão a criptografia dos arquivos compilados, o licenciamento baseado em conta e tempo e a integração com bibliotecas DLL externas para proteção adicional. A plataforma também suporta assinaturas digitais para verificar a autenticidade do código. A MetaQuotes assegura a proteção do código por meio de compilação e ofuscação, prevenindo a engenharia reversa. Para os produtos distribuídos pelo MQL5 Market, a criptografia adicional garante que apenas os usuários licenciados tenham acesso ao software, criando uma estrutura de segurança robusta para os desenvolvedores.
Em 2012, o usuário MQL5 Investeo analisou vários métodos de proteção de programas e de código MQL5 e compartilhou ideias valiosas sobre a implementação de recursos como proteção por senha, geração de chaves, licenciamento para uma única conta, proteção com limite de tempo, licenças remotas, criptografia segura de licenças e técnicas avançadas contra descompilação. Seu trabalho serve como base fundamental de conhecimento para o aprimoramento da segurança de programas.
Objetivo da discussão:
Reconhecendo a importância da segurança, nosso propósito é discutir a implementação de proteção por senha para o acesso às funções do painel de administração. Examinaremos em detalhes os métodos utilizados para alcançar os resultados demonstrados na imagem anterior, com foco em como proteger de forma eficaz o acesso dos usuários.
Possíveis áreas de vulnerabilidade no nosso painel de administração:
À medida que nosso programa evolui e incorpora novas funcionalidades, sua complexidade aumenta, especialmente para desenvolvedores iniciantes. Destacamos a seguir algumas áreas-chave de interesse em relação à segurança:
- Acesso protegido ao painel de administração:
Para garantir que nenhum usuário não autorizado possa acessar o painel de administração, implementamos proteção por senha. O acesso não autorizado pode resultar na disseminação de mensagens indesejadas entre a comunidade de traders, que confia nas informações do administrador. Cliques acidentais em botões de atalho exigem uma senha robusta. Embora muitos aplicativos utilizem a autenticação em dois fatores (2FA) para uma verificação adicional, no momento estamos focados na implementação de recursos básicos de segurança, com a intenção de incorporar opções mais avançadas no futuro.
- Segurança das mensagens via API do Telegram:
Damos também prioridade máxima à segurança da comunicação via API do Telegram, inserindo de maneira segura o ID da conversa e o token do bot durante a inicialização do programa. Essa abordagem garante que os dados confidenciais do usuário permaneçam protegidos. O Telegram utiliza recursos robustos de segurança para proteger as conversas dos usuários, incluindo segurança de transporte (Transport Layer Security) com ajuda do protocolo MTProto para chats padrão e criptografia de ponta a ponta para chats secretos. Além disso, o Telegram oferece suporte a 2FA, permitindo que os usuários gerenciem sessões ativas e aumentem a segurança da conta. Apesar de os protocolos de segurança do Telegram serem confiáveis, os usuários também devem garantir a segurança de seus próprios dispositivos.
Breve repetição da Parte III
Em nossa discussão anterior, abordamos a implementação de métodos para o gerenciamento de temas. No entanto, estávamos trabalhando com arquivos suscetíveis a alterações durante as atualizações da plataforma MetaTrader 5. Cada vez que uma atualização é lançada, ela é baixada e instalada automaticamente ao reiniciar o sistema. Abaixo, há um trecho de código que ilustra os erros encontrados ao tentar compilar após as atualizações.
'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
Solução temporária
Para resolvê-lo, é preciso primeiro compreender sua origem. Como explicado anteriormente, as atualizações da plataforma restauram as bibliotecas utilizadas em seus estados padrão. Consequentemente, os métodos implementados para gerenciar temas deixam de funcionar e começamos a enfrentar erros. Para solucionar esse problema, precisamos sobrescrever os arquivos atualizados (Dialog.mqh, Edit.mqh e Button.mqh) com as versões estendidas, anexadas ao artigo anterior. É possível localizar a pasta de arquivos de inclusão conforme mostrado na figura a seguir.
Navegando até a pasta raiz dialog.mqh
Solução permanente:
Podemos renomear o Dialog.mqh e outros relacionados para Extended_Dialog.mqh e ajustar o código de acordo. É essencial atualizar as instruções #include que faziam referência ao nome antigo para que reflitam o novo nome. Além disso, devemos verificar se existem outras dependências que também o utilizam e atualizá-las, se necessário. Após essas alterações, devemos recompilar nosso projeto para identificar possíveis erros e testar minuciosamente sua funcionalidade, garantindo que tudo esteja funcionando corretamente. Dessa forma, manteremos uma cópia separada com o novo nome, preservando o arquivo original.
Por exemplo, se já salvamos o arquivo como Extended_Dialog.mqh, podemos ir até o nosso painel de administração e ajustar o código da seguinte forma:
#include <Controls\Extended_Dialog.mqh> #include <Controls\Extended_Button.mqh> #include <Controls\Extended_Edit.mqh> #include <Controls\Label.mqh>
Vantagens de manter um nome diferente
Salvar com um nome diferente permite adaptar a funcionalidade às suas necessidades, adicionando ou ajustando recursos que não estão presentes na versão integrada. Essa personalização ajuda a criar uma interface única, alinhada aos seus requisitos. Além disso, o uso de nomes de arquivos personalizados evita conflitos com bibliotecas internas ou de terceiros, reduzindo o risco de comportamentos inesperados causados por nomes coincidentes. Ao manter as alterações feitas em um arquivo renomeado, você também protege suas configurações contra interferências de outras funções internas que possam utilizar o Dialog original, garantindo que você possa desenvolver e manter seu projeto sem ser afetado por mudanças externas.
Integração de proteção por senha no painel de administração
Neste projeto, implementamos uma proteção por senha condicional, utilizando uma sequência de caracteres que pode conter letras e números, aumentando assim o nível de complexidade. Embora um PIN de quatro dígitos possa parecer simples, ele é difícil de adivinhar. No painel de administração, usamos a classe Dialog para solicitar a senha do usuário no momento do login e para estabelecer que as funções do painel principal só sejam exibidas após a inserção bem-sucedida da senha correta.
À medida que continuamos a desenvolver o programa do painel de administração, damos atenção especial a garantir uma segurança de login robusta, de modo que apenas usuários autorizados possam acessar funções confidenciais.
Mecanismo de autenticação
Para proteger o painel de administração, implementamos um mecanismo simples de autenticação baseado em senha, que solicita ao usuário a senha antes de conceder acesso a qualquer funcionalidade.
// Show authentication input dialog bool ShowAuthenticationPrompt() { if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300)) { Print("Failed to create authentication dialog"); return false; } }
Na função ShowAuthenticationPrompt criamos uma interface amigável que conduz o usuário de forma eficiente pelo processo de autenticação. Ao desenvolver um diálogo específico para a entrada da senha, garantimos que o principal ponto de acesso ao painel de administração permaneça seguro e, ao mesmo tempo, intuitivo.
Para melhor compreensão, apresento abaixo um trecho de código que cria a janela de diálogo, acompanhado de comentários. Se quiser relembrar conceitos sobre eixos e coordenadas, consulte a Parte 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 ))
Após criar a janela de diálogo de autenticação, passamos para a disposição dos demais elementos da interface do usuário, mas com valores diferentes. O processo começa com a criação do campo de entrada de senha, no qual o usuário insere suas credenciais e, em seguida, clica nos botões necessários. Em especial, destacamos dois botões principais: Login (entrar) e Close (fechar). O botão Login é usado para enviar a senha inserida, enquanto o botão Close fecha a janela de diálogo. Abaixo está um trecho de código que ilustra a lógica de criação desses botões, bem como o texto do pedido de senha.
// 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 }
Gerenciamento de senhas
Atualmente, para os testes iniciais, utilizamos uma senha simples, codificada diretamente no programa, o que nos permite criar rapidamente protótipos de funcionalidade. No entanto, entendemos plenamente que essa abordagem traz riscos, como vulnerabilidade a ataques de força bruta no caso de o código ser comprometido.
// Default password for authentication string Password = "2024";
Embora o uso de uma senha fixa acelere nosso desenvolvimento, em futuras atualizações precisaremos migrar para uma solução mais segura, especificamente implementando arquivos de configuração criptografados ou utilizando um sistema mais avançado de gerenciamento de contas de usuários para aumentar a proteção.
Tratamento da entrada do usuário
Para reforçar a segurança, é essencial garantir que o campo de entrada de senha esteja claramente definido na janela de diálogo de autenticação. Ao ajudar o usuário a inserir a senha e verificar os dados digitados com a senha armazenada, buscamos proporcionar um processo de login seguro e sem interrupções.// 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 } }
Na função OnLoginButtonClick o programa verifica se a senha inserida coincide com a armazenada. Em caso de sucesso, a janela de autenticação é ocultada e o usuário tem acesso ao painel de administração. Se a senha estiver incorreta, o campo de entrada é limpo e o usuário é convidado a tentar novamente.
Também implementamos um manipulador para o botão Close, responsável pela lógica de saída. Ao clicar nesse botão, a janela de autenticação é fechada e o EA é removido do gráfico. Essa ação reforça a segurança e oferece um caminho claro de saída para os usuários que optarem por não realizar a autenticação. Definição do manipulador:
//+------------------------------------------------------------------+ //| Handle close button click for authentication | //+------------------------------------------------------------------+ void OnCloseAuthButtonClick() { authentication.Destroy(); ExpertRemove(); // Remove the expert if user closes the authentication dialog Print("Authentication dialog closed."); }
No manipulador, o método authentication. Destroy() fecha o diálogo, enquanto ExpertRemove() garante que o EA desapareça completamente, aumentando a segurança geral da aplicação.
Integração completa ao programa principal:
//+------------------------------------------------------------------+ //| 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; } }
Testes e resultados
Nosso código foi compilado com sucesso e, após a execução do aplicativo, constatamos que todas as funções do painel permanecem indisponíveis até que o PIN correto seja inserido. Esse comportamento garante que apenas usuários autorizados possam acessar as funções do painel. Temos consciência de que nossas medidas de segurança ainda precisam evoluir, pois podem ser vulneráveis a hackers experientes. Sabemos que cada passo dado é uma oportunidade de aprender mais sobre a implementação no MQL5 e, à medida que desenvolvemos nossas habilidades, poderemos alcançar níveis de proteção mais robustos. Abaixo, mostramos a execução do aplicativo e o resultado esperado.
Execução do painel
Considerações finais
Neste projeto, a implementação do mecanismo de autenticação no login aumentou significativamente a segurança do painel de administração, algo essencial para proteger funcionalidades confidenciais. Ao solicitar uma senha antes de conceder acesso às funções administrativas, o programa reduz o risco de uso não autorizado e garante que apenas usuários verificados possam gerenciar configurações e operações críticas. Implementamos um sistema de senha global e uma interface amigável para entrada das credenciais.
À medida que aprimoramos nosso painel de administração, focaremos em melhorias importantes, como a substituição de senhas fixas por credenciais gerenciadas de forma segura para evitar vulnerabilidades, a implementação de autenticação multifator para segurança adicional e a otimização contínua do processo de login.
Por outro lado, reconhecemos que, após a compilação, qualquer usuário sem acesso ao código-fonte terá dificuldade em explorá-lo devido aos recursos de proteção contra descompilação oferecidos pelo MQL5. Esse nível extra de proteção contribui para resguardar nosso aplicativo contra acessos não autorizados e engenharia reversa.
Experimente aplicar essa funcionalidade em seus próprios projetos! Comentários e feedback são muito bem-vindos. Suas ideias podem nos ajudar a melhorar e aperfeiçoar ainda mais nosso trabalho.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16079
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso
O artigo Criando um painel de negociação do administrador em MQL5 (Parte IV) foi publicado: Segurança de login:
Autor: Clemence Benjamin
Ao tentar compilar:
'Admin_Panel.mq5' 1
Trade.mqh
Object.mqh
StdLibErr.mqh
OrderInfo.mqh
HistoryOrderInfo.mqh
PositionInfo.mqh
DealInfo.mqh
Dialog.mqh
WndContainer.mqh
Wnd.mqh
Rect.mqh
Definições.mqh
ArrayObj.mqh
Matriz.mqh
WndClient.mqh
Painel.mqh
WndObj.mqh
ChartObjectsTxtControls.mqh
ChartObject.mqh
Scrolls.mqh
BmpButton.mqh
ChartObjectsBmpControls.mqh
Editar.mqh
Chart.mqh
Botão.mqh
Rótulo.mqh
'Up.bmp' como recurso "::res\Up.bmp" 1
'ThumbVert.bmp' como recurso "::res\ThumbVert.bmp" 1
'Down.bmp' como recurso "::res\Down.bmp" 1
'Left.bmp' como recurso "::res\Left.bmp" 1
'ThumbHor.bmp' como recurso "::res\ThumbHor.bmp" 1
'Right.bmp' como recurso "::res\Right.bmp" 1
'Close.bmp' como recurso "::res\Close.bmp" 1
'Restore.bmp' como recurso "::res\Restore.bmp" 1
'Turn.bmp' como recurso "::res\Turn.bmp" 1
Possível perda de dados devido à conversão de tipo de 'long' para 'int' Admin_Panel(4)_.mq5 161 49
'UpdateThemeColors' - identificador não declarado Admin_Panel(4)_.mq5 390 16
'darkTheme' - algum operador esperado Admin_Panel(4)_.mq5 390 34
'SetTextColor' - identificador não declarado Admin_Panel(4)_.mq5 397 14
'textColor' - algum operador esperado Admin_Panel(4)_.mq5 397 27
'SetBackgroundColor' - identificador não declarado Admin_Panel(4)_.mq5 398 14
'bgColor' - algum operador esperado Admin_Panel(4)_.mq5 398 33
'SetBorderColor' - identificador não declarado Admin_Panel(4)_.mq5 399 14
'borderColor' - algum operador esperado Admin_Panel(4)_.mq5 399 29
'SetTextColor' - identificador não declarado Admin_Panel(4)_.mq5 424 12
'textColor' - algum operador esperado Admin_Panel(4)_.mq5 424 25
'SetBackgroundColor' - identificador não declarado Admin_Panel(4)_.mq5 425 12
'bgColor' - algum operador esperado Admin_Panel(4)_.mq5 425 31
'SetBorderColor' - identificador não declarado Admin_Panel(4)_.mq5 426 12
'borderColor' - algum operador esperado Admin_Panel(4)_.mq5 426 27
14 erros, 1 advertência 15 2