在MQL5中创建交易管理员面板(第七部分):可信任用户、密码恢复与加密技术(基础篇)
◍ 给交易面板加可信用户与密码恢复
在 MT5 用 MQL5 写管理面板时,直接把下单权限对所有终端登录者开放是高危做法。更稳的思路是内置「可信任用户」名单:只有白名单里的账户 ID 或登录名能触发实际交易指令,其余请求只进日志不进市场。 密码恢复不能走明文回传。常见实现是用一组固定 salt 做哈希比对,恢复入口只校验身份后重置为临时口令并强制下次修改。外汇与贵金属杠杆高,这类面板若部署在共享 VPS,密钥泄露可能导致穿仓,务必最小权限运行。 加密部分建议用简单的对称混淆配合哈希,别自己造块密码。下面这段演示了信任用户校验与口令哈希的核心逻辑,复制进 EA 的 OnTick 前先做白名单过滤即可验证。
MT5面板开发里的2FA摩擦与日志旁路
在MT5市场发布的程序,每一份都用绑定买家设备的唯一密钥加密,自动防拷贝;购买产品至少可激活5次,具体次数由卖家设定。这套机制对安全必要,但开发期每次编译、切品种、换周期或重启终端都会触发重新初始化,进而弹出密码框和Telegram的6位验证码,测试节奏被频繁打断。 我自己在波动率75(1秒)指数M1上跑管理面板时,日志里常见这样的输出:2024.11.22 08:44:47.980 消息发送成功:有人尝试登录管理面板。请使用此代码验证您的身份:028901。为省去切到Telegram拿码的时间,曾在发送函数里加了一行 Print 把验证码直接打到专家日志,测试期确实快了,但敏感信息落盘等于给未授权者留了后门。 下面这段是当时 SendMessageToTelegram 的核心,含那行被高亮标出的日志打印: 别把日志当保险箱 开发期图方便打印验证码,上线前必须删掉。日志里的 028901 这类动态码一旦被本地或其他有终端权限的人看到,2FA就形同虚设,只能靠限制重试和真·Telegram推送兜底。 现在的处理思路是:密码连错超阈值才强制走Telegram 2FA;若三次内输对,就智能跳过双因子,减少密集开发中的验证骚扰。信任不跨会话保留,每次新登录重验,兼顾效率和暴力破解防护。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Send the message to Telegram | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SendMessageToTelegram(class="type">class="kw">string message, class="type">class="kw">string chatId, class="type">class="kw">string botToken) { class="type">class="kw">string url = "https:class=class="str">"cmt">//api.telegram.org/bot" + botToken + "/sendMessage"; class="type">class="kw">string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}"; class="type">char postData[]; ArrayResize(postData, StringToCharArray(jsonMessage, postData) - class="num">1); class="type">int timeout = class="num">5000; class="type">char result[]; class="type">class="kw">string responseHeaders; class="type">int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders); if (responseCode == class="num">200) { Print("Message sent successfully: ", message); class=class="str">"cmt">//This line prints the message in journal,thus a security leak that I used to bypass telegram during app tests. class="kw">return true; } else { Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError()); Print("Response: ", CharArrayToString(result)); class="kw">return false; } }
「面板登录日志里的品种与周期线索」
一段真实运行日志暴露了管理面板V1.22的验证动作:2024.11.22 08:44:48.040,系统在波动率75(1秒)指数的M1周期下完成密码校验,并向Telegram推送了2FA验证码。 从这条记录能读出两个可验证点——面板绑定的是波动率75指数(1秒到期合约)的M1图表;二次验证走的是Telegram信道而非邮件。 在MT5里打开波动率75指数的M1窗口,加载该面板版本,你应当能在专家日志看到同构的时间戳与验证回执,借此确认本地环境是否匹配。外汇与贵金属品种波动逻辑不同,此类指数衍生品高风险,验证仅说明连通性,不预示任何收益。
◍ EA初始化时如何动态放行可信用户
在 MT5 管理面板里加可信用户逻辑,不需要动大段旧代码,只改登录处理与初始化相关片段就能把错误引入概率压到最低。核心是两个全局变量:failedAttempts 记错密次数,isTrustedUser 标是谁在三次以内成功登录,从而可以绕开双因素认证(2FA)。 OnLoginButtonClick 里,密码正确就 failedAttempts 归零并把 isTrustedUser 置真;只要失败次数不超过 3 次,直接销毁认证框、弹出管理主页,不给 2FA 弹窗。超过 3 次则生成 6 位随机码,通过 Telegram 连同硬编码恢复密码发过去,再调 ShowTwoFactorAuthPrompt() 强制二次验证。 初始化链路从 OnInit() 调 ShowAuthenticationPrompt() 起头,对话框不交互程序就停住;点登录后走上面的分支;过了认证,后台才初始化其余面板但先隐藏。下面这段是变量声明与登录点击处理的骨架,注意 failedAttempts<=3 的判断其实写在归零之后,所以首次成功必进可信分支,这是代码层面的一个隐性逻辑点。 别把三次容错当死规则 实际跑的时候,failedAttempts 在成功路径上被置 0,因此「<=3」永远成立,可信判定目前等价于「本次密码对」。若想真按历史失败次数放行,得把归零挪到 isTrustedUser 判定之后,否则这层逻辑只是个摆设。
<span class="keyword">class="type">int</span> failedAttempts = <span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">// Counter for failed login attempts</span> <span class="keyword">class="type">bool</span> isTrustedUser = <span class="keyword">false</span>; <span class="comment">class=class="str">"cmt">// Flag for trusted users</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| Handle login button click |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">class="type">void</span> OnLoginButtonClick() { <span class="keyword">class="type">class="kw">string</span> enteredPassword = passwordInputBox.Text(); <span class="keyword">if</span> (enteredPassword == Password) { failedAttempts = <span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">// Reset attempts on successful login</span> isTrustedUser = <span class="macro">true</span>; <span class="keyword">if</span> (failedAttempts <= <span class="number">class="num">3</span>) <span class="comment">class=class="str">"cmt">// Skip 2FA for trusted users</span> { authentication.Destroy(); adminHomePanel.Show(); <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Login successful. 2FA skipped for trusted user."</span>); } <span class="keyword">else</span> { } } <span class="keyword">else</span> { failedAttempts++; feedbackLabel.Text(<span class="class="type">class="kw">string">"Wrong password. Try again."</span>); passwordInputBox.Text(<span class="class="type">class="kw">string">""</span>); <span class="keyword">if</span> (failedAttempts >= <span class="number">class="num">3</span>) { <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Too many failed attempts. 2FA will be required."</span>); twoFACode = GenerateRandom6DigitCode(); SendMessageToTelegram( <span class="class="type">class="kw">string">"A login attempt was made on the Admin Panel.\n"</span> + <span class="class="type">class="kw">string">"Use this code to verify your identity: "</span> + twoFACode + <span class="class="type">class="kw">string">"\n"</span> + <span class="class="type">class="kw">string">"Reminder: Your admin password is: "</span> + Password, Hardcoded2FAChatId, Hardcoded2FABotToken ); authentication.Destroy(); ShowTwoFactorAuthPrompt(); <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Password authentication successful. A 2FA code has been sent to your Telegram."</span>);