在MQL5中创建交易管理员面板(第七部分):可信任用户、密码恢复与加密技术(基础篇)
📘

在MQL5中创建交易管理员面板(第七部分):可信任用户、密码恢复与加密技术(基础篇)

第 1/2 篇

◍ 给交易面板加可信用户与密码恢复

在 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;若三次内输对,就智能跳过双因子,减少密集开发中的验证骚扰。信任不跨会话保留,每次新登录重验,兼顾效率和暴力破解防护。

MQL5 / C++
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 判定之后,否则这层逻辑只是个摆设。

MQL5 / C++
<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&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> OnLoginButtonClick()
{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> enteredPassword = passwordInputBox.Text();
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> (enteredPassword == Password)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;failedAttempts = <span class="number">class="num">0</span>; <span class="comment">class=class="str">"cmt">// Reset attempts on successful login</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;isTrustedUser = <span class="macro">true</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> (failedAttempts &lt;= <span class="number">class="num">3</span>) <span class="comment">class=class="str">"cmt">// Skip 2FA for trusted users</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;authentication.Destroy();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;adminHomePanel.Show();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">Print</span>(<span class="class="type">class="kw">string">"Login successful. 2FA skipped for trusted user."</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;failedAttempts++;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;feedbackLabel.Text(<span class="class="type">class="kw">string">"Wrong password. Try again."</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;passwordInputBox.Text(<span class="class="type">class="kw">string">""</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> (failedAttempts &gt;= <span class="number">class="num">3</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">Print</span>(<span class="class="type">class="kw">string">"Too many failed attempts. 2FA will be required."</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;twoFACode = GenerateRandom6DigitCode();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SendMessageToTelegram(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"A login attempt was made on the Admin Panel.\n"</span> +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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> +
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="class="type">class="kw">string">"Reminder: Your admin password is: "</span> + Password,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hardcoded2FAChatId, Hardcoded2FABotToken
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;authentication.Destroy();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShowTwoFactorAuthPrompt();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<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>);

常见问题

在面板初始化时读一个本地加密的用户列表,只放行列表里的账号;新用户需手动导入公钥或哈希,别用明文存密码。
能反推谁在哪个图表盯哪个品种,异常时段登录可对照成交记录;建议只记哈希后的标识,别留明文账户。
小布可接管日志旁路分析,自动标出陌生IP或非常规周期登录,并推提醒,你把重复巡检交给它就够。
用一次性恢复码或离线密保问题,恢复后强制改密并写审计;别把主密钥写进面板文件,用非对称加密分权。
在OnInit里异步读缓存令牌,命中白名单直接放行,未命中再弹二次验证;别同步查远程,拖慢开盘。