Недоработки в логике (текст довольно большой)

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Недоработки в логике (текст довольно большой)

Сообщение alibek » 31.03.2004 (Ср) 9:15

Привет, All :)
Вот делаю программку, но она мне уже скоро ночами будет снится, поэтому прошу взглянуть свежим взглядом на логику и указать на возможные баги/ошибки.

Система клиент/сервер. Клиенты бывают разных типов, т.е. есть подсистема "Менеджер", "Аудитор" и т.д. Сервер же один. Общение происходит по TCP/IP (компонент, а не API).

Сервер работает с базой данных, в БД есть следующие таблицы (упрощенная схема):
Код: Выделить всё
Приложения
APPLICATION
-----------
APPL_KEY - Long
CODE - Text
NAME - Text
PASSWORD - Text

Код: Выделить всё
Пользователи
USERS
-----
USER_KEY - Long
LOGIN - Text
PASSWORD - Text
NAME - Text

На сервере имеется слушающий сокет и массив динамично добавляемых клиентских. Наряду с массивом сокетов имеется массив пользовательского типа, каждый элемент которого сопоставлен с сокетом, в пользовательском типе хранится вспомогательная информация.
Код: Выделить всё
Private Enum ClientConnectStates
  ccsNotConnect = 0
  ccsConnecting = 10
  ccsConnecting_WaitAppID = 11
  ccsConnecting_WrongAppID = 12
  ccsConnecting_SendPassword = 13
  ccsConnecting_ReceivePassword = 14
  ccsConnecting_WrongPassword = 15
  ccsConnecting_Complete = 19
  ccsAuthorizing = 20
  ccsAuthorizing_WaitUser = 21
  ccsAuthorizing_WrongUser = 22
  ccsAuthorizing_WrongAccess = 23
  ccsAuthorizing_Complete = 29
  ccsConnect = 30
  ccsDisconnecting = 90
End Enum
Private Type WinSockInfo
  ConnectState As ClientConnectStates
  Client As ClientTypeEnum
  User As String
  APPL_KEY As Long
  USER_KEY As Long
  Data As String
End Type
Private ws(1 To MaxConnections) As WinSockInfo


Массив клиентских сокетов начинается с нуля, но нулевой элемент используется для специальных целей (когда лимит подключений исчерпан, сервер подключает клиента на нулевой сокет, отсылает ему сообщение "лимит подключений исчерпан" и отключает).

Когда клиент подключается к серверу, сервер выделяет ему новый сокет и производит подключение. При этом статус соответствующего сокета отмечается, как ccsConnecting_WaitAppID.

Клиент должен прислать на сервер строку, идентифицирующую тип клиента (например "MANAGER").
Сервер ищет эту запись в БД, если найти не удается, то на клиента отсылается сообщение об ошибке ("приложение не зарегистрировано в БД") и сокет отключается.
Если запись найдена, то генерируется строка случайных символов и отсылается на сервер, статус переводится в ccsConnecting_ReceivePassword. Одновременно получанная случайная строка шифруется паролем приложения (который берется с БД), с нее берется хэш и результат запоминается в Data.
На клиенте получанная строка также шифруется паролем (который прошит в клиентской части), с нее берется хэш и отправляется обратно на сервер.
Сервер сравнивает полученную строку с вычисленной заранее, если они совпадают, то приложение авторизуется, статус переводится в ccsAuthorizing_WaitUser, при этом все пакеты данных, которыми обмениваются клиент и сервер, шифруются той самой случайной фразой, которая использовалась для авторизации клиента; шифрование выполняется и на сервере и на клиенте. Кроме того, пакет данных имеет следующий вид <хэш><код>, здесь <код> - зашифрованные данные, а <хэш> - хэш на <код>.

Дальше идет авторизация пользователя. Клиентская подсистема запрашивает у пользователя логин и пароль. Полученные данные упаковываются в строку вида <AUTH: ><логин><@><хэш пароля>.

Сервер ищет пользователя в БД, если находит, то сравниваются хэши паролей, того, что в БД, и того, который присылает клиентская подсистема. Если все верно, то сравнивается уровень доступа пользователя и если все в порядке, то теперь уже авторизуется и пользователь и статус переводится в состояние ccsConnect.

После этого уже система клиент-сервер работает в своем дежурном режиме.




Я вот смотрю и мест, где логика хромает, не вижу, но я уже столько на этот алгоритм смотрю, что не увижу даже явный баг.
Lasciate ogni speranza, voi ch'entrate.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 31.03.2004 (Ср) 10:03

alibek, ты почаще так делай, а? А то я очень много почерпнул сейчас :)

Вопрос такой по логике возник.
Вот сервак отослал случайную строку (без шифровки). Вот клиент её получил (он знает, что она не зашифрована). Вот клиент её обработал, результат отправил (шифруя?). Сервер, получив эту бяку (и если она правильная) отправляет «Красавец! Продолжим!» (я правильно понимаю?), причём это уже зашифровано? А если бяка неправильная, то отсылается «Отстой! Пошёл вон!» (это не шифруется – ведь ключ не валиден, да?). Иными словами, в случае успеха клиент получит зашифрованное сообщение об успехе, а в случае неудачи – незашифрованное. А как он узнает – зашифрован пришедший ответ или нет?

Но всё это, конечно, если ты не совсем точно описал местоположение точки перехода в шифрованное состояние :wink:
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 31.03.2004 (Ср) 11:35

Да я с радостью :)

Клиент отправляет не шифрованную строку. Клиент шифрует строку, берет с нее хэш и отправляет его (незашифрованным). И сервер отвечает также не шифруя данные. А вот уже после отправки подтверждения (о том, что приложение авторизованно), все данные шифруются.

Т.е. данные шифруются уже после того, как ответ будет обработан и клиент получит подтверждение. Сейчас это дело у меня работает, но я просто опасаюсь, что где-то допустил промашку и это впоследствии будет лазейкой для хака :)
Но сами пароли (ни приложений, ни пользователей) по сети не передаются, передается только хэш.
Кстати, я схему передачи чуть упростил, если брать реальную, она такая:

клиент -> сервер
<MsgID><null><Hash><null><Encode><null><null>
сервер -> клиент
<MsgID><@><Code><null><Hash><null><Encode><null><null>

Здесь:
<MsgID> - идентификатор сообщений (нужен только для клиента, поскольку данные ходят асинхронно, то он необходим, чтобы определить, ответ на _что_ это пришел)
<Code> - код ответа, от 0000 до FFFF.
<Hash> - хэш на зашифрованный блок данных (<Encode>)
<Encode> - зашифрованный блок данных, после расшифровки состоит из <HashDecode><Decode>
<HashDecode> - хэш на расшифрованный блок данных
<Decode> - расшифрованный блок данных

Двойной <null> в конце блока нужен для того, чтобы разделять блоки, когда они приходят по нескольку за раз (когда клиент, к примеру, отсылает два запроса подряд).

Правда такой способ разделения блоков накладывает ограничения - в частности, в пакете данных нигде не должен использоваться символ <null> и для передачи бинарных данных их приходится загонять в HexDump и передавать строкой, но это вполне терпимо.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 01.04.2004 (Чт) 11:16

По отстутствию ответов могу предположить, что явных багов не замечено :)
Lasciate ogni speranza, voi ch'entrate.


Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 97

    TopList