시스템트레이딩 게시글

지표와 EA에 비밀번호 보호 추가하기 - 메타트레이더 4를 위한 전문가 가이드

첨부파일
15534.zip (2.42 KB, 다운로드 1회)

안녕하세요, 트레이더 여러분! 오늘은 여러분의 지표나 EA(전문가 어드바이저)에 비밀번호 보호 기능을 추가하는 방법에 대해 이야기해볼게요. 과거에 여러 가지 코드 보호 방법이 제안되었지만, 대부분은 보안이 약하거나, 새 고객마다 코드를 재컴파일해야 하거나, 너무 복잡해서 사용하기 어려운 경우가 많았죠.

이번에 제가 제안하는 방법은 메타트레이더 4의 내장 보안 엔진을 활용하여 DES/ECB 암호화를 제공하며, 매번 고객마다 코드 재컴파일이 필요 없는 간단한 비밀번호 검증 방식입니다.

캐나다의 여러 스마트 카드 프로젝트에서 일하면서 금융 기관과 카드 발급자가 사용하는 다양한 보안 체계에 익숙해졌습니다. 가장 먼저 생각해야 할 질문은 "내가 위험에 처할 경우 무엇이냐?"입니다. 이 질문에 대한 답변이 "수백만 달러"라면, 이번 보안 체계는 여러분에게 적합하지 않을 것입니다.

반대로, "내 보안 체계에 해킹하려면 한 해를 투자해야 할 것이고, 그로 인해 한두 달의 코딩 시간이 날아간다면" 이 솔루션이 적합합니다. 이 암호화 방식에서 사용하는 단일 DES 키는 여러분의 코드를 보호하기에 충분한 보안을 제공하며, 새로운 고객을 위해 코드를 재컴파일할 필요가 없습니다.

여러분의 편의를 위해 두 개의 소스 파일을 제공했습니다. 첫 번째 파일인 "Password_Check"는 여러분의 지표나 EA에 추가할 코드입니다. 이 코드는 사용자가 입력한 비밀번호를 검증하고, 비밀번호가 틀리거나 사용자가 오프라인이라면 사용자 친화적인 메시지를 표시합니다. 또한, EA가 실행 중이라면 이를 제거하고 INIT_FAILED 상태를 반환합니다.

두 번째 파일인 "Password_Generate"는 보호할 클라이언트의 이름과 계좌 번호를 입력하는 데 사용됩니다. 생성된 비밀번호를 표시하여 고객에게 제공할 수 있게 해줍니다. 물론, 이 코드를 최종 제품에 포함시키고 싶지는 않겠죠! :)

그럼 시작해볼까요?

먼저, 여러분의 지표나 EA에 입력 문자열을 정의해야 합니다:

//--- 입력 매개변수
extern string     Password;

다음으로, init() 함수에 비밀번호를 체크하고, 비밀번호가 틀린 경우나 사용자가 오프라인일 때 메시지를 표시하는 코드를 추가합니다.

//+------------------------------------------------------------------+
//| EA 초기화 함수                                   |
//+------------------------------------------------------------------+
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);
      
      // 잘못된 비밀번호이거나 사용자가 오프라인입니다.  EA를 제거하고 오류를 반환합니다.
      ExpertRemove();
      return(INIT_FAILED);
   }

   // 모두 괜찮습니다...
   return(INIT_SUCCEEDED);
}

이제 중요한 부분입니다. 클라이언트 이름과 계좌 번호를 DES 키로 암호화하고, 결과를 BASE64로 인코딩하여 입력된 비밀번호와 비교해야 합니다. 결과가 일치하면 고객은 행복할 것입니다. 그렇지 않다면, 해커가 DES 키를 깨려고 시도하고 있는 것입니다. 잘못된 비밀번호를 입력할 때마다 EA가 자동으로 종료되기 때문에 해커가 성공하기 전에 보라 보라에서 은퇴할 충분한 시간이 있을 것입니다!

//+------------------------------------------------------------------+
//| 클라이언트 비밀번호 검증
//+------------------------------------------------------------------+
bool Password_Check(string client)
{
   string   MasterKey;
   uchar dst[], src[], key[];

   // 여기에 암호화 키를 정의합니다.  DES/ECB 암호화를 위해 7자여야 합니다.
   // 비밀번호를 어렵게 설정하세요.  성은 좋은 아이디어가 아닙니다!
   // "wLdU&$z" 같은 것이 좋습니다.  현재는 간단한 것을 사용하겠습니다...
   MasterKey = "NotDemo";  
   
   // MasterKey를 문자 배열로 변환합니다.
   StringToCharArray(MasterKey, key);
   
   // 클라이언트 문자열이 null이 아닌지 확인합니다.
   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);
}

이제 클라이언트 이름(메타트레이더 4의 클라이언트 계좌 이름에서 가져온)과 클라이언트 계좌 번호를 검증할 수 있습니다.

만약 라이센스 정책이 하나의 클라이언트에 대해 여러 계좌를 허용한다면, 'client' 문자열에서 계좌 번호를 제거하기만 하면 됩니다. 다음과 같은 방식으로:

// 클라이언트가 온라인인지 확인하여 클라이언트 이름을 가져옵니다.
if(IsConnected()) client = AccountInfoString(ACCOUNT_NAME);

물론 "브로커 이름", "계좌 이름" 및 "계좌 로그인"의 조합을 원하는 대로 믹스 앤 매치할 수 있습니다. 'client' 변수가 길어질수록 암호화된 비밀번호도 길어질 것이라는 점만 기억해 주세요.

다음으로는 "Password_Generate" 코드에 대해 살펴보겠습니다. 이 코드는 "Password_Check"와 동일한 방식으로 작동하지만, EA에 비밀번호를 입력하는 대신 클라이언트 이름(또는 선택한 브로커 이름, 계좌 이름 및 계좌 로그인 조합)을 입력하여 암호화한 후 생성된 비밀번호를 표시합니다. 이 비밀번호는 고객이 여러분의 훌륭한 지표나 EA를 구매할 때 제공할 것입니다.

다시 한 번, init() 함수에 다음 코드를 추가합니다.

//+------------------------------------------------------------------+
//| EA 초기화 함수                                   |
//+------------------------------------------------------------------+
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);
         
   // 모두 괜찮습니다.  EA를 제거합니다.
   ExpertRemove();
   return(INIT_SUCCEEDED);
}

이제 Password_Check() 함수에서 암호화된 비밀번호의 문자열을 반환하도록 약간의 수정을 해야 합니다. 두 함수에서 같은 비밀번호를 사용해야 한다는 점을 잊지 마세요. 그렇지 않으면 예상치 못한 일이 벌어질 것입니다!

//+------------------------------------------------------------------+
//| 클라이언트 정보를 암호화하고 비밀번호를 반환합니다.
//+------------------------------------------------------------------+
string Password_Generate(string client)
{
   string   MasterKey;
   uchar dst[], src[], key[];

   // 여기에 암호화 키를 정의합니다.  DES/ECB 암호화를 위해 7자여야 합니다.
   // "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));   
}

이것으로 끝입니다! 클라이언트에서 제공받은 정보를 입력하고 비밀번호를 이메일이나 다른 방법으로 전달하면 됩니다. 이 모든 것을 보라 보라의 거실에서 할 수 있다는 점이 정말 멋지죠!

다시 말씀드리지만, 이 보안 체계는 매번 새로운 고객을 위해 코드를 재컴파일할 필요가 없으며, 서버 측 검증 호스트를 코딩하지 않고도 여러분의 훌륭한 지표나 EA에 대해 꽤 좋은 보안을 제공합니다!

감사합니다!

-클로드.

연관 포스트

댓글 (0)