Tag,
hier ein Guide wie man beim Client Login die Antwort auf die Sicherheitsfrage mit abprüfen kann.
Die Antwort auf die Sicherheitsfrage wird bei den meisten wohl in der Account Tabelle in der Spalte answer1 gespeichert sein.
In meinem Falle wird die Antwort bei der Registration auf der Website als Pflichtfeld abgefragt und dann als md5 hash in der Datenbank abgelegt.
Warum sollte man dies beim Login mit abfragen
Da es öffentlich zugängliche Listen mit ID/Passwort Kombinationen gibt ist es auf jeden Fall angebracht einen zusätzlichen Authentifizierungsparameter sowohl auf der Website, als auch beim Client Login zu implementieren.
Ein Angreifer könnte sich eine der verfügbaren Accountlisten herunterladen und mittels Bitte melden Sie sich an, um diesen Link zu sehen. diese Liste gegen den Login eurer Website laufen lassen.
Sollte die Website dann HTTP 200 als Antwort zurückgeben ist der entsprechende Account praktisch übernommen.
Ein weiterer Parameter im Login verhindert dies.
Guide
- //1. Search
- void CAccountConnector::SetLoginInfo(const char * c_szName, const char * c_szPwd)
- {
- m_strID = c_szName;
- m_strPassword = c_szPwd;
- }
- //1. Replace
- void CAccountConnector::SetLoginInfo(const char * c_szName, const char * c_szPwd, const char * c_szAnswer1)
- {
- m_strID = c_szName;
- m_strPassword = c_szPwd;
- m_answer1 = c_szAnswer1;
- }
- //2. Search
- void CAccountConnector::ClearLoginInfo( void )
- {
- m_strPassword = "";
- }
- //2. Replace
- void CAccountConnector::ClearLoginInfo( void )
- {
- m_strPassword = "";
- m_answer1 = "";
- }
- //3. Search in bool CAccountConnector::__AuthState_RecvPhase()
- [...]
- #else /* USE_OPENID */
- [...]
- strncpy(LoginPacket.pwd, m_strPassword.c_str(), PASS_MAX_NUM);
- //3. Add after
- strncpy(LoginPacket.answer1, m_answer1.c_str(), PASS_MAX_NUM);
- //4. Search (directly after last change)
- LoginPacket.pwd[PASS_MAX_NUM] = '\0';
- //4. Add after
- LoginPacket.answer1[PASS_MAX_NUM] = '\0';
- //5. Search
- SetLoginInfo("", "");
- //5. Replace
- SetLoginInfo("", "", "");
- //1. Search
- typedef struct command_login3
- {
- BYTE header;
- char name[ID_MAX_NUM + 1];
- char pwd[PASS_MAX_NUM + 1];
- DWORD adwClientKey[4];
- } TPacketCGLogin3;
- //1. Replace
- typedef struct command_login3
- {
- BYTE header;
- char name[ID_MAX_NUM + 1];
- char pwd[PASS_MAX_NUM + 1];
- DWORD adwClientKey[4];
- char answer1[PASS_MAX_NUM + 1];
- } TPacketCGLogin3;
- //1. Search
- void SetLoginInfo(const char* c_szID, const char* c_szPassword);
- //1. Replace
- void SetLoginInfo(const char* c_szID, const char* c_szPassword, const char* c_szAnswer1);
- //2. Search
- bool SendLoginPacket(const char * c_szName, const char * c_szPassword);
- //2. Replace
- bool SendLoginPacket(const char * c_szName, const char * c_szPassword, const char* c_szAnswer1);
- //3. Search
- std::string m_stPassword;
- //3. Add after
- std::string m_answer1;
- //1. Search
- void CPythonNetworkStream::SetLoginInfo(const char* c_szID, const char* c_szPassword)
- {
- m_stID=c_szID;
- m_stPassword=c_szPassword;
- }
- //1. Replace
- void CPythonNetworkStream::SetLoginInfo(const char* c_szID, const char* c_szPassword, const char* c_szAnswer1)
- {
- m_stID=c_szID;
- m_stPassword=c_szPassword;
- m_answer1 = c_szAnswer1;
- }
- //2. Search
- void CPythonNetworkStream::ClearLoginInfo( void )
- {
- m_stPassword = "";
- }
- //2. Replace
- void CPythonNetworkStream::ClearLoginInfo( void )
- {
- m_stPassword = "";
- m_answer1 = "";
- }
- //1. Search
- PyObject* netSetLoginInfo(PyObject* poSelf, PyObject* poArgs)
- {
- char* szName;
- if (!PyTuple_GetString(poArgs, 0, &szName))
- return Py_BuildException();
- char* szPwd;
- if (!PyTuple_GetString(poArgs, 1, &szPwd))
- return Py_BuildException();
- CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
- CAccountConnector & rkAccountConnector = CAccountConnector::Instance();
- rkNetStream.SetLoginInfo(szName, szPwd);
- rkAccountConnector.SetLoginInfo(szName, szPwd);
- return Py_BuildNone();
- }
- //1. Replace
- PyObject* netSetLoginInfo(PyObject* poSelf, PyObject* poArgs)
- {
- char* szName;
- if (!PyTuple_GetString(poArgs, 0, &szName))
- return Py_BuildException();
- char* szPwd;
- if (!PyTuple_GetString(poArgs, 1, &szPwd))
- return Py_BuildException();
- char* szAnswer1;
- if (!PyTuple_GetString(poArgs, 2, &szAnswer1))
- return Py_BuildException();
- CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
- CAccountConnector & rkAccountConnector = CAccountConnector::Instance();
- rkNetStream.SetLoginInfo(szName, szPwd, szAnswer1);
- rkAccountConnector.SetLoginInfo(szName, szPwd, szAnswer1);
- return Py_BuildNone();
- }
- //2. Search
- PyObject* netSendLoginPacket(PyObject* poSelf, PyObject* poArgs)
- {
- char* szName;
- if (!PyTuple_GetString(poArgs, 0, &szName))
- return Py_BuildException();
- char* szPwd;
- if (!PyTuple_GetString(poArgs, 1, &szPwd))
- return Py_BuildException();
- CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
- rkNetStream.SendLoginPacket(szName, szPwd);
- return Py_BuildNone();
- }
- //2. Replace
- PyObject* netSendLoginPacket(PyObject* poSelf, PyObject* poArgs)
- {
- char* szName;
- if (!PyTuple_GetString(poArgs, 0, &szName))
- return Py_BuildException();
- char* szPwd;
- if (!PyTuple_GetString(poArgs, 1, &szPwd))
- return Py_BuildException();
- char* szAnswer1;
- if (!PyTuple_GetString(poArgs, 2, &szAnswer1))
- return Py_BuildException();
- CPythonNetworkStream& rkNetStream=CPythonNetworkStream::Instance();
- rkNetStream.SendLoginPacket(szName, szPwd, szAnswer1);
- return Py_BuildNone();
- }
- //1. Search (2x)
- SendLoginPacket(m_stID.c_str(), m_stPassword.c_str());
- //1. Replace (2x)
- SendLoginPacket(m_stID.c_str(), m_stPassword.c_str(), m_answer1.c_str());
- //2. Search
- bool CPythonNetworkStream::SendLoginPacket(const char* c_szName, const char* c_szPassword)
- {
- TPacketCGLogin LoginPacket;
- LoginPacket.header = HEADER_CG_LOGIN;
- strncpy(LoginPacket.name, c_szName, sizeof(LoginPacket.name)-1);
- strncpy(LoginPacket.pwd, c_szPassword, sizeof(LoginPacket.pwd)-1);
- LoginPacket.name[ID_MAX_NUM]='\0';
- LoginPacket.pwd[PASS_MAX_NUM]='\0';
- if (!Send(sizeof(LoginPacket), &LoginPacket))
- {
- Tracen("SendLogin Error");
- return false;
- }
- return SendSequence();
- }
- //2. Replace
- bool CPythonNetworkStream::SendLoginPacket(const char* c_szName, const char* c_szPassword, const char* c_szAnswer1)
- {
- TPacketCGLogin LoginPacket;
- LoginPacket.header = HEADER_CG_LOGIN;
- strncpy(LoginPacket.name, c_szName, sizeof(LoginPacket.name)-1);
- strncpy(LoginPacket.pwd, c_szPassword, sizeof(LoginPacket.pwd)-1);
- strncpy(LoginPacket.answer1, c_szAnswer1, sizeof(LoginPacket.answer1) - 1);
- LoginPacket.name[ID_MAX_NUM]='\0';
- LoginPacket.pwd[PASS_MAX_NUM]='\0';
- LoginPacket.answer1[PASS_MAX_NUM] = '\0';
- if (!Send(sizeof(LoginPacket), &LoginPacket))
- {
- Tracen("SendLogin Error");
- return false;
- }
- return SendSequence();
- }
- //1. Search
- typedef struct SAccountTable
- {
- DWORD id;
- char login[LOGIN_MAX_LEN + 1];
- char passwd[PASSWD_MAX_LEN + 1];
- char social_id[SOCIAL_ID_MAX_LEN + 1];
- char status[ACCOUNT_STATUS_MAX_LEN + 1];
- BYTE bEmpire;
- TSimplePlayer players[PLAYER_PER_ACCOUNT];
- } TAccountTable;
- //1. Replace
- typedef struct SAccountTable
- {
- DWORD id;
- char login[LOGIN_MAX_LEN + 1];
- char passwd[PASSWD_MAX_LEN + 1];
- char social_id[SOCIAL_ID_MAX_LEN + 1];
- char status[ACCOUNT_STATUS_MAX_LEN + 1];
- BYTE bEmpire;
- TSimplePlayer players[PLAYER_PER_ACCOUNT];
- char answer1[PASSWD_MAX_LEN + 1];
- } TAccountTable;
- //1. Search
- typedef struct command_login3
- {
- BYTE header;
- char login[LOGIN_MAX_LEN + 1];
- char passwd[PASSWD_MAX_LEN + 1];
- DWORD adwClientKey[4];
- } TPacketCGLogin3;
- //1. Replace
- typedef struct command_login3
- {
- BYTE header;
- char login[LOGIN_MAX_LEN + 1];
- char passwd[PASSWD_MAX_LEN + 1];
- DWORD adwClientKey[4];
- char answer1[PASSWD_MAX_LEN + 1];
- } TPacketCGLogin3;
- //1. add #include "md5.h"
- //2. Search
- char szPassword[45 + 1] = {0, };
- //2. Add after
- char szEncryptAnswer1[45 + 1] = { 0, };
- char szAnswer1[45 + 1] = { 0, };
- //3. Search
- strlcpy(szSocialID, row[col++], sizeof(szSocialID));
- if (!row[col])
- {
- sys_err("error column %d", col);
- M2_DELETE(pinfo);
- break;
- }
- //3. Add after
- strlcpy(szEncryptAnswer1, row[col++], sizeof(szEncryptAnswer1));
- if (!row[col])
- {
- LoginFailure(d, "WRONGPWD");
- sys_err("error column %d", col);
- M2_DELETE(pinfo);
- break;
- }
- strlcpy(szAnswer1, row[col++], sizeof(szAnswer1));
- if (!row[col])
- {
- LoginFailure(d, "WRONGPWD");
- sys_err("error column %d", col);
- M2_DELETE(pinfo);
- break;
- }
- //4. Search
- int nPasswordDiff = strcmp(szEncrytPassword, szPassword);
- //4. Add after
- int nAnswer1Diff = strcmp(szEncryptAnswer1, szAnswer1);
- if (nAnswer1Diff)
- {
- LoginFailure(d, "WRONGPWD");
- sys_log(0, " WRONGPWD");
- M2_DELETE(pinfo);
- }
- //5. Search
- strlcpy(r.social_id, szSocialID, sizeof(r.social_id));
- //5. Add after
- strlcpy(r.answer1, pinfo->answer1, sizeof(r.answer1));
- //1. add #include "md5.h"
- //2. Search in void CInputAuth::Login(LPDESC d, const char * c_pData)
- char passwd[PASSWD_MAX_LEN + 1];
- strlcpy(passwd, pinfo->passwd, sizeof(passwd));
- //2. Add after
- char answer1[PASSWD_MAX_LEN + 1];
- strlcpy(answer1, pinfo->answer1, sizeof(answer1));
- //3. Search in same function
- char szLogin[LOGIN_MAX_LEN * 2 + 1];
- DBManager::instance().EscapeString(szLogin, sizeof(szLogin), login, strlen(login));
- //3. Add after
- char szAnswer1[PASSWD_MAX_LEN * 2 + 1];
- DBManager::instance().EscapeString(szAnswer1, sizeof(szAnswer1), answer1, strlen(answer1));
- //4. Search in same function
- // CHANNEL_SERVICE_LOGIN
- if (Login_IsInChannelService(szLogin))
- {
- sys_log(0, "ChannelServiceLogin [%s]", szLogin);
- DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p,
- "SELECT '%s',password,securitycode,social_id,id,status,availDt - NOW() > 0,"
- "UNIX_TIMESTAMP(silver_expire),"
- "UNIX_TIMESTAMP(gold_expire),"
- "UNIX_TIMESTAMP(safebox_expire),"
- "UNIX_TIMESTAMP(autoloot_expire),"
- "UNIX_TIMESTAMP(fish_mind_expire),"
- "UNIX_TIMESTAMP(marriage_fast_expire),"
- "UNIX_TIMESTAMP(money_drop_rate_expire),"
- "UNIX_TIMESTAMP(create_time)"
- " FROM account WHERE login='%s'",
- szPasswd, szLogin);
- }
- // END_OF_CHANNEL_SERVICE_LOGIN
- //4. Replace
- // CHANNEL_SERVICE_LOGIN
- if (Login_IsInChannelService(szLogin))
- {
- sys_log(0, "ChannelServiceLogin [%s]", szLogin);
- DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p,
- "SELECT '%s',password,securitycode,social_id,'%s',answer1,id,status,availDt - NOW() > 0,"
- "UNIX_TIMESTAMP(silver_expire),"
- "UNIX_TIMESTAMP(gold_expire),"
- "UNIX_TIMESTAMP(safebox_expire),"
- "UNIX_TIMESTAMP(autoloot_expire),"
- "UNIX_TIMESTAMP(fish_mind_expire),"
- "UNIX_TIMESTAMP(marriage_fast_expire),"
- "UNIX_TIMESTAMP(money_drop_rate_expire),"
- "UNIX_TIMESTAMP(create_time)"
- " FROM account WHERE login='%s'",
- szPasswd, szAnswer1, szLogin);
- }
- // END_OF_CHANNEL_SERVICE_LOGIN
- //5. Search
- DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p,
- "SELECT '%s',password,securitycode,social_id,id,status,availDt - NOW() > 0,"
- "UNIX_TIMESTAMP(silver_expire),"
- "UNIX_TIMESTAMP(gold_expire),"
- "UNIX_TIMESTAMP(safebox_expire),"
- "UNIX_TIMESTAMP(autoloot_expire),"
- "UNIX_TIMESTAMP(fish_mind_expire),"
- "UNIX_TIMESTAMP(marriage_fast_expire),"
- "UNIX_TIMESTAMP(money_drop_rate_expire),"
- "UNIX_TIMESTAMP(create_time)"
- " FROM account WHERE login='%s'",
- mysql_hash_password(szPasswd).c_str(), szLogin);
- //6. Replace
- DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p,
- "SELECT '%s',password,securitycode,social_id,'%s',answer1,id,status,availDt - NOW() > 0,"
- "UNIX_TIMESTAMP(silver_expire),"
- "UNIX_TIMESTAMP(gold_expire),"
- "UNIX_TIMESTAMP(safebox_expire),"
- "UNIX_TIMESTAMP(autoloot_expire),"
- "UNIX_TIMESTAMP(fish_mind_expire),"
- "UNIX_TIMESTAMP(marriage_fast_expire),"
- "UNIX_TIMESTAMP(money_drop_rate_expire),"
- "UNIX_TIMESTAMP(create_time)"
- " FROM account WHERE login='%s'",
- mysql_hash_password(szPasswd).c_str(), md5(szAnswer1).c_str(), szLogin);
Die Anpassungen am Login Interface habe ich nur schnell hingerotzt, das geht bestimmt auch schöner und muss u.U. an den Client angepasst werden.
- //1. Search
- {
- "name" : "Password_EditLine",
- "type" : "editline",
- "x" : 77,
- "y" : 43,
- "width" : 120,
- "height" : 18,
- "input_limit" : 16,
- "secret_flag" : 1,
- "enable_codepage" : 0,
- "r" : 1.0,
- "g" : 1.0,
- "b" : 1.0,
- "a" : 1.0,
- }
- //1. Add after -> don't forget to add a comma after Password_EditLine child
- {
- "name" : "Answer1_EditLine",
- "type" : "editline",
- "x" : 77,
- "y" : 70,
- "width" : 120,
- "height" : 18,
- "input_limit" : 16,
- "secret_flag" : 1,
- "enable_codepage" : 0,
- "r" : 1.0,
- "g" : 1.0,
- "b" : 1.0,
- "a" : 1.0,
- },
Die angepasste login.dds ist im Angang als login.7z zu finden
- //1. Search
- self.pwdEditLine.SetTabEvent(0)
- //1. Add after
- self.answer1EditLine.SetTabEvent(0)
- self.answer1EditLine.SetReturnEvent(0)
- //2. Search
- self.pwdEditLine = None
- //2. Add after
- self.answer1EditLine = None
- //3. Search
- def SetPasswordEditLineFocus(self):
- //3. Add after (whole def)
- def SetAnswer1EditLineFocus(self):
- if self.idEditLine != None: #0000862: [M2EU] 로그인창 팝업 에러: 종료시 먼저 None 설정됨
- self.idEditLine.SetText("")
- self.idEditLine.SetFocus() #0000685: [M2EU] 아이디/비밀번호 유추 가능 버그 수정: 무조건 아이디로 포커스가 가게 만든다
- if self.pwdEditLine != None: #0000862: [M2EU] 로그인창 팝업 에러: 종료시 먼저 None 설정됨
- self.pwdEditLine.SetText("")
- self.idEditLine.SetFocus()
- if self.answer1EditLine != None:
- self.answer1EditLine.SetText("")
- //4. Replace all occurances of SetPasswordEditLineFocus with SetAnswer1EditLineFocus
- //5. Search
- self.pwdEditLine = GetObject("Password_EditLine")
- //5. Add after
- self.answer1EditLine = GetObject("Answer1_EditLine")
- //6. Search
- self.pwdEditLine.SetReturnEvent(ui.__mem_func__(
- //6. Replace
- self.pwdEditLine.SetReturnEvent(ui.__mem_func__(self.answer1EditLine.SetFocus))
- //7. Search
- self.pwdEditLine.SetTabEvent(ui.__mem_func__(
- //7. Replace
- self.pwdEditLine.SetTabEvent(ui.__mem_func__(self.answer1EditLine.SetFocus))
- //8. Add after self.pwdEditLine.SetTabEvent(ui.__mem_func__(self.answer1EditLine.SetFocus))
- self.answer1EditLine.SetReturnEvent(ui.__mem_func__(self.__OnClickLoginButton))
- self.answer1EditLine.SetTabEvent(ui.__mem_func__(self.idEditLine.SetFocus))
- //9. Search
- def Connect(self, id, pwd
- //9. Replace
- def Connect(self, id, pwd, answer1):
- //10. Search in def Connect
- self.stream.SetLoginInfo(id, pwd
- //10. Replace
- self.stream.SetLoginInfo(id, pwd, answer1)
- //11. Search
- pwd=loginInfo.get("pwd", "")
- //11. Add after
- answer1=loginInfo.get("answer1", "")
- //12. Search
- self.pwd = None
- //12. Add after
- self.answer1 = None
- //13. Search
- if self.id and self.pwd
- //13. Replace
- if self.id and self.pwd and self.answer1:
- //14. Search (2x)
- self.Connect(id, pwd
- //14. Replace (2x)
- self.Connect(id, pwd, answer1)
- //15. Search (2x)
- self.Connect(self.id, self.pwd
- //15. Replace (2x)
- self.Connect(self.id, self.pwd, self.answer1)
- //16. Search (2x)
- if self.pwdEditLine == None:
- self.pwdEditLine.SetText("")
- //16. Add after (2x)
- if self.answer1EditLine == None:
- self.answer1EditLine.SetText("")
- //17. Search
- pwd = self.pwdEditLine.GetText()
- //17. Add after
- answer1 = self.answer1EditLine.GetText()
- //18. Search
- if len(pwd)==0:
- //18. Add after (whole condition)
- if len(answer1)==0:
- self.PopupNotifyMessage(localeInfo.LOGIN_INPUT_PASSWORD, self.SetAnswer1EditLineFocus)
- return