
在MQL5中创建交易管理员面板(第七部分):可信任用户、密码恢复与加密技术
内容:
概述
在MQL5市场中,设备身份验证对于保障分布式产品的安全性发挥着至关重要的作用。我们当前进行探索的动机,源于在使用安全的管理面板时所遇到的挑战。自实施增强的安全措施以来,由于每次在终端上编译文件或测试功能时都会触发频繁的登录提示和身份验证请求,导致开发和测试过程出现延迟。尽管这层额外的安全措施必不可少,但也给我们的工作流程带来了阻碍。
在市场发布新产品时,首页会显示以下警告信息。这就凸显了MQL5如何优先实施并强制推行强有力的安全措施,以保护开发者和用户双方。市场上出售的每一款程序都使用特殊密钥进行加密,以防止产品被非法使用。加密密钥对于每位买家都是唯一的,且与其计算机绑定,因此市场上的所有产品都具备自动的防拷贝保护功能。
购买的产品至少可以被激活五次。这样确保了买家和卖家之间的利益平衡。可用激活次数由卖家设定。
许多应用程序和网站会选择性地实施第二层保护,仅在检测到可疑活动时才激活,例如使用匿名IP、从新设备登录或多次登录失败等情况。这种方法在保持安全的同时,最大限度地减少了中断。
在我们的情况下,开发过程中的测试延迟是由反复输入密码和查看Telegram应用以获取生成的6位数代码造成的。频繁的提示,尤其是因终端活动变化而触发时,会令人厌烦。以下是一些导致设备重新初始化并随后要求输入密码的典型情况:
- 交易品种变更
- 时间框架切换
- 终端重启等
在某些情况下,由于各种活动,我们的程序会反复重新初始化——这是由于技术或操作原因而不可避免的过程。用户验证算法嵌入在初始化函数的开头,因此程序无法绕过此步骤继续执行。然而,我们可以在初始化函数中引入一种绕过机制,以优化该过程。这种绕过算法会监控登录尝试次数,从而在有效会话期间提供更流畅的体验。
如果密码错误尝试次数超过设定限制,则表明存在可疑行为,或原始用户可能丢失或忘记了密码。另一方面,如果在三次尝试内输入了正确的密码,系统会智能地绕过2FA(双因素认证),从而通过避免重复的基于Telegram的身份验证来节省大量时间。这种方法在加强安全性的同时提高了效率,特别是在密集的开发和测试阶段。
下图突出了在应用开发过程中向Telegram发送多条验证消息的问题,展示了这种优化算法的实际需求。
管理面板:私有的Telegram 2FA代码传递工具
此前,每次程序初始化时反复提示输入密码和进行2FA认证,使得整个过程极其耗时。为了在开发过程中节省时间,我添加了一行代码,将发送到Telegram的6位数验证码直接打印在终端的专家日志中。这样,我就可以在应用测试期间直接从日志中快速获取验证码,而无需切换到Telegram应用,从而显著地简化了流程。
相关代码段是负责发送Telegram消息的函数的一部分。其中,我添加了一项功能,即为了方便起见,将消息记录到日志中。然而,在这个开发阶段,我计划移除这项功能,以便在未来的版本中增强安全性。如果发生未经授权的访问,将验证码等敏感信息暴露在日志中可能会构成安全风险。这一步骤体现了在优化开发流程与遵循保护敏感数据的最佳实践之间取得平衡。
以下代码段显示了打印所发送消息的那一行代码。
//+------------------------------------------------------------------+ //| Send the message to Telegram | //+------------------------------------------------------------------+ bool SendMessageToTelegram(string message, string chatId, string botToken) { string url = "https://api.telegram.org/bot" + botToken + "/sendMessage"; string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}"; char postData[]; ArrayResize(postData, StringToCharArray(jsonMessage, postData) - 1); int timeout = 5000; char result[]; string responseHeaders; int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders); if (responseCode == 200) { Print("Message sent successfully: ", message); //This line prints the message in journal,thus a security leak that I used to bypass telegram during app tests. return true; } else { Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError()); Print("Response: ", CharArrayToString(result)); return false; } }
以下突出显示发送至Telegram的信息,其中包含验证码。在开发过程中,这种方法是绕过查看Telegram获取验证码的便捷途径。然而,若保留此方式,则存在重大安全风险,因为未经授权的人员可能无需访问Telegram,就能直接从终端获取验证码。这些文本通常打印在终端的“EA”选项上:
讨论目标:2024.11.22 08:44:47.980管理面板V1.22(波动率75(1秒)指数,M1)消息发送成功:有人尝试登录管理面板。请使用此代码验证您的身份:028901 2024.11.22 08:44:48.040管理面板V1.22(波动率75(1秒)指数,M1)密码验证成功。已向您的Telegram发送2FA验证码。
本次讨论的目标是集成一个可信任用户识别系统,以简化登录和2FA(双因素认证)流程。若用户在三次尝试内成功输入正确密码,则可绕过2FA;若超过限制,则触发2FA,要求通过Telegram发送验证码,并使用硬编码密码进行恢复。该系统通过限制重试次数来减轻暴力破解攻击,同时确保认证流程顺畅,从而兼顾了便利性与安全性。信任是基于特定输入的,每次新登录均需重新验证。下一节将进一步探讨应对暴力破解攻击的措施。
什么是暴力破解攻击?
暴力破解攻击是攻击者通过系统地尝试所有可能的密码、加密密钥或凭据组合,以获取对账户、系统或加密数据的未经授权访问,直至找到正确的组合。这种方法依赖于计算能力和持久性,而非利用系统本身的漏洞。
暴力破解攻击的主要特征:
- 试错法:攻击者反复尝试不同的字符组合,直至获得访问权限。
- 自动化:通常使用专门的工具或脚本自动化此过程,允许在短时间内进行数千次甚至数百万次尝试。
- 耗时:所需时间取决于目标密码或密钥的复杂程度。字符更多、组合更多样的强密码破解所需时间显著更长。
暴力破解攻击的类型:
- 简单暴力破解:尝试所有可能的字符组合。
- 字典攻击:使用常用密码列表(如“123456”、“password”或“qwerty”)尝试访问。
- 认证填充攻击:利用其他数据泄露事件中泄露的用户名-密码对,尝试访问不同平台上的账户。
防范措施:
- 登录尝试次数限制。
- 强制使用强密码(长,包含大小写字母、数字和符号的组合)。
- 实施双因素认证(2FA)以增加另一层安全防护。
当前面板访问的安全防护易受上述攻击影响,虽然限制登录尝试次数是关键一步,但我们也旨在避免频繁地通过Telegram进行身份验证以节省时间。发送验证码旨在防止未经授权的访问,并向真实的管理员提醒面板上可能存在的恶意活动,即使他们未发现任何安全漏洞,也能提供一层保障。
在管理面板中实现可信任用户功能
要在管理面板中实现新功能,我们仅修改了与安全和EA程序初始化相关的源代码的几个部分。这种方法确保程序的其余部分保持不变,从而在管理大量代码时将错误风险降至最低。此处的重点在于登录处理函数,我们引入了新变量,这些变量将在下一个代码段中声明。
int failedAttempts = 0; // Counter for failed login attempts bool isTrustedUser = false; // Flag for trusted users
全局变量failedAttempts和isTrustedUser 对于实现新功能至关重要。failedAttempts 用于监控错误密码的输入次数,并有助于确定何时应启用双因素认证(2FA)。isTrustedUser 标识用于识别在允许的尝试次数内成功登录的用户,这些用户可跳过2FA流程。通过在OnLoginButtonClick函数中适当地重置这些变量,系统能够对认证流程进行动态控制,确保灵活性和安全性。
增强登录功能以支持新特性
OnLoginButtonClick函数通过failedAttempts计数器跟踪登录尝试次数,并利用isTrustedUser标识识别可信任用户,从而集成了新特性。当输入正确密码时,该函数会检查用户是否因错误尝试次数少于三次而符合可信任用户的条件。可信任用户可跳过2FA步骤,直接进入管理主页面板。如果错误尝试次数超过限制,该函数将生成一个2FA验证码,通过Telegram将其连同硬编码的恢复密码一起发送给用户,并显示2FA认证提示。这样确保了既能为可信任用户提供便利,也能在错误尝试后强制实施安全措施,实现了便利性与安全性的平衡。
//+------------------------------------------------------------------+ //| Handle login button click | //+------------------------------------------------------------------+ void OnLoginButtonClick() { string enteredPassword = passwordInputBox.Text(); if (enteredPassword == Password) { failedAttempts = 0; // Reset attempts on successful login isTrustedUser = true; if (failedAttempts <= 3) // Skip 2FA for trusted users { authentication.Destroy(); adminHomePanel.Show(); Print("Login successful. 2FA skipped for trusted user."); } else { } } else { failedAttempts++; feedbackLabel.Text("Wrong password. Try again."); passwordInputBox.Text(""); if (failedAttempts >= 3) { Print("Too many failed attempts. 2FA will be required."); twoFACode = GenerateRandom6DigitCode(); SendMessageToTelegram( "A login attempt was made on the Admin Panel.\n" + "Use this code to verify your identity: " + twoFACode + "\n" + "Reminder: Your admin password is: " + Password, Hardcoded2FAChatId, Hardcoded2FABotToken ); authentication.Destroy(); ShowTwoFactorAuthPrompt(); Print("Password authentication successful. A 2FA code has been sent to your Telegram."); failedAttempts = 0; // Reset attempts after requiring 2FA } } }
程序初始化过程中的五个关键节点
包括跳过2FA新特性在内的认证流程在初始化阶段生效,因为ShowAuthenticationPrompt()函数会显示登录界面,其中OnLoginButtonClick事件处理程序与“登录”按钮关联。具体工作流程如下:
- 触发认证提示:在 OnInit()函数中,首先调用ShowAuthenticationPrompt()。该函数会创建并显示包含密码输入框和"登录"按钮的认证对话框。程序会暂停后续执行,直到用户与对话框交互。
- 处理登录尝试:当用户点击"登录"按钮时,执行OnLoginButtonClick函数。该函数会检查输入的密码,更新failedAttempts计数器,并根据错误尝试次数决定是直接授予访问权限还是强制启用2FA。
- 成功认证后继续操作:如果登录成功且用户符合可信任用户条件(错误尝试次数少于三次),则结束认证对话框并立即显示管理主页面板。这为可信任用户绕过了2FA流程。
- 必要时要求2FA:如果用户超过允许的错误尝试次数,程序将强制启用2FA。系统会生成一个6位验证码并通过Telegram发送,同时通过ShowTwoFactorAuthPrompt()函数显示2FA认证对话框。请注意之前用红色高亮显示的数字验证码生成函数。
//+------------------------------------------------------------------+ //| Generate a random 6-digit code for 2FA | //+------------------------------------------------------------------+ string GenerateRandom6DigitCode() { int code = MathRand() % 1000000; // Generate a 6-digit number return StringFormat("%06d", code); // Ensure leading zeros }
5. 进一步初始化:一旦用户通过认证(无论是通过可信任登录还是成功完成2FA),其余面板(管理主页面板、通信面板、交易管理面板)将在后台进行初始化,但在导航过程中明确显示之前将保持隐藏状态。
以下是初始化函数的代码段:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if (!ShowAuthenticationPrompt()) { Print("Authorization failed. Exiting..."); return INIT_FAILED; } if (!adminHomePanel.Create(ChartID(), "Admin Home Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Admin Home Panel"); return INIT_FAILED; } if (!CreateAdminHomeControls()) { Print("Home panel control creation failed"); return INIT_FAILED; } if (!communicationsPanel.Create(ChartID(), "Communications Panel", 0, 30, 30, 500, 500)) { Print("Failed to create Communications panel dialog"); return INIT_FAILED; } if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0,260, 30, 1040, 170)) { Print("Failed to create Trade Management panel dialog"); return INIT_FAILED; } if (!CreateControls()) { Print("Control creation failed"); return INIT_FAILED; } if (!CreateTradeManagementControls()) { Print("Trade management control creation failed"); return INIT_FAILED; } adminHomePanel.Hide(); // Hide home panel by default on initialization communicationsPanel.Hide(); // Hide the Communications Panel tradeManagementPanel.Hide(); // Hide the Trade Management Panel return INIT_SUCCEEDED; }
加密技术及其在管理面板中的应用示例
MQL5中的加密技术涉及使用算法和方法来保护密码、消息或验证码等敏感数据。
以下是在管理面板中应用该概念的四种方法。
1. 密码哈希处理
- HashPassword函数使用基于CRYPT_HASH_SHA256算法的CryptEncode函数对输入的密码进行哈希处理。哈希处理结果通过CharArrayToHex转换为十六进制字符串,以便存储或比对。在VerifyPassword函数中,系统会对输入的密码进行哈希处理,并将其与存储的哈希值进行比对,确保不存储或处理明文密码。
- 存储哈希密码可确保即使存储的数据被访问,实际密码仍然安全。哈希处理是单向的,即无法从哈希值推导出原始密码,这为身份验证增添了必要的安全层。
// Example for storing our hard-coded password string HashPassword(string password) { uchar hash[]; CryptEncode(CRYPT_HASH_SHA256, password, hash); return CharArrayToHex(hash); } // Usage string PasswordHash = HashPassword("2024"); // Store this instead of plaintext bool VerifyPassword(string enteredPassword) { return HashPassword(enteredPassword) == PasswordHash; }
2. 敏感数据加密
- EncryptData函数采用AES-256算法(CRYPT_AES256)并使用安全加密密钥对2FA验证码等敏感信息进行加密。DecryptData函数则执行相反操作,将数据解密回原始格式。密钥在此过程中至关重要,加密和解密时必须使用匹配的密钥。
- 加密技术可在数据存储或传输过程中保护敏感信息。例如,若2FA验证码在通信过程中被截获,加密版本可确保未经授权的用户在没有正确密钥的情况下无法读取。
//Example for encryption of our 2FA verification code string EncryptData(string data, string key) { uchar encrypted[]; CryptEncode(CRYPT_AES256, data, encrypted, StringToCharArray(key)); return CharArrayToHex(encrypted); } string DecryptData(string encryptedData, string key) { uchar decrypted[]; uchar input[]; StringToCharArray(encryptedData, input); CryptDecode(CRYPT_AES256, input, decrypted, StringToCharArray(key)); return CharArrayToString(decrypted); } // Usage string key = "StrongEncryptionKey123"; // Use a secure key string encrypted2FA = EncryptData(twoFACode, key); Print("Encrypted 2FA code: ", encrypted2FA);
3. 用于双因素认证(2FA)的安全随机数生成
- GenerateSecureRandom6DigitCode函数采用基于CRYPT_HASH_SHA256算法的CryptEncode函数,生成密码学安全的随机序列。随后通过模运算和格式化处理,将结果转换为6位数字。
- 像MathRand 这样的标准随机函数属于伪随机算法,具有可预测性。而采用加密随机数生成技术,可以确保2FA验证码具备高度安全性,能够有效抵御预测攻击或暴力破解,从而显著提升身份认证流程的安全性。
// Example for generating a secure verification code string GenerateSecureRandom6DigitCode() { uchar randomBytes[3]; CryptEncode(CRYPT_HASH_SHA256, MathRand(), randomBytes); // Use CryptEncode for randomness int randomValue = (randomBytes[0] << 16) | (randomBytes[1] << 8) | randomBytes[2]; randomValue = MathAbs(randomValue % 1000000); // Ensure 6 digits return StringFormat("%06d", randomValue); }
4. 通过Telegram实现安全通信
- SendEncryptedMessageToTelegram 函数在通过SendMessageToTelegram函数将消息传输至Telegram服务器前,会先使用EncryptData函数对消息进行加密。只有持有正确解密密钥的人员才能解密该加密消息。
- 加密通信可确保敏感信息(如2FA验证码)的机密性,即使传输过程被拦截,未经授权者也无法获取其内容。在使用第三方通信平台时,这一点尤为重要,因为此类平台的数据安全性可能无法完全得到保障。
//Example of the securely sending to Telegram bool SendEncryptedMessageToTelegram(string message, string chatId, string botToken, string key) { string encryptedMessage = EncryptData(message, key); return SendMessageToTelegram(encryptedMessage, chatId, botToken); } // Usage string key = "StrongEncryptionKey123"; SendEncryptedMessageToTelegram("Your 2FA code is: " + twoFACode, Hardcoded2FAChatId, Hardcoded2FABotToken, key);
测试
在此阶段,我们将展示管理面板安全增强功能的实施成果。本次更新为可信任用户简化了登录流程,使其能够快速访问面板;而对于不可信任用户,则需通过二次验证来确认身份真实性。若用户忘记密码,可通过身份验证流程进行密码找回。
以下EA日志(Expert Log)中记录了登录尝试失败的情况,随后附上一张展示登录流程的示意图。
2024.11.22 03:53:59.675管理面板V1.23(波动率75(1秒)指数,M2)登录尝试失败次数过多。2要求进行2FA验证。 2024.11.22 03:54:00.643管理面板V1.23(波动率75(1秒) 指数,M2)消息发送成功:请查看您的Telegram 获取验证码和密码 2024.11.22 03:54:00.646管理面板V1.23(波动率75(1秒)指数,M2)密码验证成功。已向您的Telegram发送2FA验证码。 2024.11.22 03:54:22.946管理面板V1.23(波动率75(1秒)指数,M2)2FA验证成功。已授予管理主页面的访问权限。
已发送Telegram验证消息
管理面板:登录尝试失败
对于可信任用户,登录过程非常简单。他们可以跳过二次验证步骤,从而简化进入管理面板的流程。以下是EA日志,展示了可信任用户成功登录的情况,随后是一张图片,展示了输入正确密码的简单流程。2024.11.22 03:57:41.563管理面板V1.23(波动率75(1秒)指数,M2)登录成功。已为可信任用户跳过2FA。
管理面板:可信任用户使用密码成功登录
结论
在本次增强管理面板的讨论过程中,我们在功能和安全性方面均取得了重大的进展。引入可信任用户功能后,通过将登录尝试次数限制为三次,并在该阈值内成功认证时跳过2FA,为已知用户提供了更顺畅、更高效的登录体验。这种方法平衡了安全性与可用性,减少了合法用户的操作障碍,同时对不可信任的登录尝试保持了严格的访问控制措施。
我们还挖掘了加密技术在加强面板安全框架方面的潜力。通过采用密码哈希、敏感数据加密以及用于2FA验证码的安全随机数生成技术,我们确保了关键信息的完整性和机密性。哈希保护了存储的密码免受未经授权的访问,而加密则在传输或存储过程中保障了敏感数据的安全。此外,加密技术安全性确保了生成验证码的不可预测性,为防范暴力破解攻击增添了附加的防御层。
这些进步有助于我们为用户提供一个用户友好且安全的管理工具,用于管理交易通信和操作。通过同时解决功能和安全性问题,我们为未来的增强功能奠定了基础,这些功能可以进一步简化工作流程,同时遵循最高的数据保护标准。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16339
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。

