大家好!今天我们来聊聊如何为您的MetaTrader 4指标或者专家顾问(EA)添加密码保护。过去有很多方法保护代码,但大多数都存在一些缺点:要么安全性不够,要么每次新客户都需要重新编译代码(这对于只打算拥有十几个客户的情况可以接受),要么就是过于复杂,需要通过远程主机验证客户端终端。
在这里,我想介绍一个简单的密码验证方案,利用MT4内置的安全引擎提供DES/ECB加密,并且不需要为每个新客户重新编译代码。
我曾参与过多个加拿大高端智能卡项目,对金融机构和卡发行商所使用的各种安全方案非常熟悉。首先,你得问自己一个问题:“风险在哪里?”在与这些机构合作时,总会进行风险评估。如果答案是“数百万美元”,那么这个安全方案可能就不适合你了。
但如果你的答案是“如果有人花一年时间来破解我的安全方案,那我损失的就是一两个月的代码工作”,那么这个方案就是为你量身定制的。使用的单一DES密钥可以为你的代码提供足够的安全性,并且不需要为新客户重新编译代码。
为了方便大家,我提供了两个源文件。第一个文件“Password_Check”是你需要添加到你的指标或专家顾问中的。它会验证用户在输入参数“Password”中输入的密码,如果密码不正确(或者用户处于离线状态),它会显示友好的提示信息,移除正在运行的专家,返回INIT_FAILED状态。
第二个文件“Password_Generate”则是用于输入你想要保护的客户姓名和账号。它会显示生成的密码,以便你将其提供给客户。显然,你不想把这个代码包含在最终产品中!:)
让我们开始吧……
首先,我们需要在指标或专家顾问中定义一个输入字符串:
//--- 输入参数 extern string Password;
接下来,我们在init()函数中添加代码,检查密码并在密码不正确、用户离线或用户没有输入密码时显示提示信息。
//+------------------------------------------------------------------+ //| 专家初始化函数 | //+------------------------------------------------------------------+ int init() { string client = NULL; // 确保客户在线才能获取其姓名和账号 if(IsConnected()) client = AccountInfoString(ACCOUNT_NAME) + " / " + DoubleToStr(AccountInfoInteger(ACCOUNT_LOGIN), 0); // 检查客户的密码 if(!Password_Check(client)) { if(StringLen(Password) != 0) MessageBox("无法验证客户和账号!" + (IsConnected() ? "\n请确认您输入的密码正确。" : "\n\n您需要在线以进行验证。"), (IsConnected() ? "密码无效!" : "离线!"), MB_OK | MB_ICONSTOP); else MessageBox("未注册的软件。\n\n请联系软件供应商以获取\n个人激活密码。" + (StringLen(client) == 0 ? "" : "\n\n您的注册信息是:\n\n'"+client+"'"), "未注册", MB_OK | MB_ICONSTOP); // 密码无效或用户离线。移除专家并以错误退出 ExpertRemove(); return(INIT_FAILED); } // 一切正常... return(INIT_SUCCEEDED); }
接下来,我们需要对客户姓名和账号进行DES密钥加密,将结果编码为BASE64并与输入的密码进行比较。如果结果匹配,客户就开心了;如果不匹配,那就有黑客想要破解你的DES密钥了。鉴于每次输入错误密码时专家顾问会自动卸载,你可能在他们成功之前就已经能去波拉波拉退休了!
//+------------------------------------------------------------------+ //| 验证客户的密码 //+------------------------------------------------------------------+ bool Password_Check(string client) { string MasterKey; uchar dst[], src[], key[]; // 在这里定义你的加密密钥。必须为7个字符以适应DES/ECB加密 // 让你的密码难以猜测。你的姓可不是个好主意! // 像“wLdU&$z”这样的密码会很好。现在,我们使用一个简单的... MasterKey = "NotDemo"; // 将MasterKey转换为字符数组 StringToCharArray(MasterKey, key); // 确保客户字符串不是空 if(StringLen(client) == 0) return(false); // 使用DES密钥加密客户信息 StringToCharArray(client, src); CryptEncode(CRYPT_DES, src, key, dst); // 清除密钥并编码为BASE64 ArrayInitialize(key, 0x00); CryptEncode(CRYPT_BASE64, dst, key, src); // 比较密码并返回结果 return(CharArrayToString(src) == Password); }
这样就完成了!我们现在可以验证从MetaTrader 4客户账号中获取的客户姓名和账号。
如果你的授权政策允许一个客户拥有多个账号,那么你只需从‘client’字符串中移除账号号即可,如下所示:
// 确保客户在线才能获取客户姓名 if(IsConnected()) client = AccountInfoString(ACCOUNT_NAME);
当然,你可以根据需要混合使用“经纪人名称”、“账号名称”和“账号登录”。只需记住,‘client’变量越长,生成的加密密码也会越长。
接下来,我们来看看“Password_Generate”代码。我们想要做的事情与“Password_Check”相同,但这次不是输入密码,而是输入客户姓名(或你选择的经纪人名称、账号名称和账号登录的组合),进行加密,然后显示生成的密码。这就是你在客户购买你的高端指标或专家顾问时要提供给他们的密码。
同样,在你的init()函数中你会添加以下代码。
//+------------------------------------------------------------------+ //| 专家初始化函数 | //+------------------------------------------------------------------+ int init() { string Password = NULL; // 确保客户输入不为空 if(StringLen(Client) != 0) { // 生成客户密码 Password = Password_Generate(Client); // 打印生成的密码(方便复制粘贴) Print("客户:'"+Client+"' 密码:"+Password); // 显示为客户生成的密码 MessageBox("为客户/账号生成的密码\n\n'"+Client+"' 是:\n"+Password, "密码生成器", MB_OK | MB_ICONINFORMATION); } else MessageBox("你必须指定一个客户/账号!", "密码生成器", MB_OK | MB_ICONSTOP); // 一切正常。移除专家。 ExpertRemove(); return(INIT_SUCCEEDED); }
接下来,我们对“Password_Check()”函数进行轻微修改,以返回编码密码的字符串。记得在“Password_Check()”函数和“Password_Generate()”函数中使用相同的密码。如果你不这样做,后果可想而知!
//+------------------------------------------------------------------+ //| 加密客户信息并返回密码 //+------------------------------------------------------------------+ string Password_Generate(string client) { string MasterKey; uchar dst[], src[], key[]; // 在这里定义你的加密密钥。必须为7个字符以适应DES/ECB加密 // 必须与“Password_Check()”函数中定义的密码相同! // 让你的密码难以猜测。你的姓可不是个好主意! // 像“wLdU&$z”这样的密码会很好。现在,我们使用一个简单的... MasterKey = "NotDemo"; // 将MasterKey转换为字符数组 StringToCharArray(MasterKey, key); // 使用DES密钥加密客户信息 StringToCharArray(client, src); CryptEncode(CRYPT_DES, src, key, dst); // 清除密钥并编码为BASE64 ArrayInitialize(key, 0x00); CryptEncode(CRYPT_BASE64, dst, key, src); // 返回加密密码 return(CharArrayToString(src)); }
就这样,你输入客户提供的信息,并通过电子邮件或其他任何方式将生成的密码发送给他们。当然,这样做的好处是你可以在波拉波拉的客厅里完成这一切!
最后,再次强调,这个安全方案不需要你为每个新客户重新编译代码,也不需要编写服务器端验证主机,同时为你辛苦制作的高端指标或专家顾问提供了相当不错的安全性!
祝大家交易顺利!
- Claude