PDA

View Full Version : Технический вопрос по защите сервера



Archy
9.10.2013, 17:49
Собственно работаю над одним проектом (не АА если чо) налаживаю безопасную связь между клиентом и сервером
проблема или прелесть в том что клиент обращается к серверу по HTTP запросам, а серверная часть вообще PHP скрипт

Значит что требуется:
Требуется чтоб сервер обрабатывал только те запросы что пришли из клиента и игнорил все другие
то есть если допустим наш запрос выгладит так:

localhost/ServerSide.php?userId=10&data=someDataToProcess

то проблема заключается в том что если юзер за снифит трафик и получит ссылку он сможет обратится по этому URL на прямую через бразуер или через еще какие инструменты обходя родной клиент! а так незя!

собственно чтобы идентифицировать что запрос пришел именно с нужного клиента а не через бразузер решил использовать одноразовый MD5 Hash

пример:
localhost/ServerSide.php?userId=10&data=someNewData&requestKey=someAbraKadabraMD5Hash

где сам хешь будет состоять из допустим логина и пароля

типо:
String hash = new MD5(loginName+password);

тогда сервер по полученному ***?userId=10*** сможет сделать лук ап в базу и получить логин и пасс и сгенерировать подобный хешь у себя для сравнению.
но тут возникает еще одна проблема
Хеш должен менятся при каждом новом запросе, иначе от него нет смысла.

а чтоб его делать разным нужно будет добавить еще какой так называемой соли ("salt")
типо: userName+password+Time = идеальный вариант т.к. каждый новых хеш будет всегда разным

НО:
что если сервер хостится в УК а клиент зашел из америки где тайм зоне -6 часов?
да и воосбче как можно синхронезировать серверное и клиентское время до секунд? да еще желательно не особо палевно

да я конечно могу прилепить время которое использовалось для генерации прямо в URL
localhost/ServerSide.php?userId=10&data=someNewData&requestKey=someAbraKadabraMD5Hash&time=1234567890

но тогда я раскрою 1/3ю часть компонентов используемых при генерации хаша, что не есть гуд

короче подскажите если у кого есть идеи как сделать эту "соль" чтоб она:
*не передавалось по сети
*каждый раз менялась
*была идентична на сервере и клиенте

может это и не время вовсе, а что тогда?

Sedoy
9.10.2013, 18:36
Вопрос генерации уникальных ключей решался многими и уже по 100 раз :) на самом деле учитывая что вам не нужен мега пупер рандом ключь 100500 раз в микросекунду, то простейший способ использовать в качестве базы для генерации ключа время в mils от "начала времён" оно не зависит от таймзоны а переводить часы чтоб захакать ключь никто не будет
Хотя я наверное не совсем понял сути вопроса )
Опять же при запросе/ответе клиенту можно выдавать одноразовый ключик и сохранять его в сессии/кукисе

Archy
9.10.2013, 18:44
Спасибо за подсказку, но тут все равно не состыковка если я правильно тебя понял

допустим клиент получил его время в mils (1234244553400) допустим
сгенерировал хаш из: login+pass+1234244553400
построил ХТТП запрос и отослал серверу.
к тому моменту когда сервер получит запрос, сделает лук ап по userID в базу и вытащит login+pass и получит mills
то его полученный mils не будет равен клиентскому mils т.к. уже прошло дохера милисекунд пока запрос долетел до сервера и он еще успел обратится в базу данных
а соответственно сервер не сгенерит такой же хаш

или я что-то не понял в твоем описании?

Sedoy
9.10.2013, 18:50
Спасибо за подсказку, но тут все равно не состыковка если я правильно тебя понял
допустим клиент получил его время в mils (1234244553400) допустим
сгенерировал хаш из: login+pass+1234244553400
построил ХТТП запрос и отослал серверу.
к тому моменту когда сервер получит запрос, сделает лук ап по userID в базу и вытащит login+pass и получит mills
то его полученный mils не будет равен клиентскому mils т.к. уже прошло дохера милисекунд пока запрос долетел до сервера и он еще успел обратится в базу данных
а соответственно сервер не сгенерит такой же хаш
или я что-то не понял в твоем описании?

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

AK47
9.10.2013, 18:51
"в качестве базы для генерации ключа время в mils". Т.е. ключ генерицца при начале сессии клиента, а не на лету, имо. Тебе же надо уникальность беседы сессии выдержать?

Sedoy
9.10.2013, 18:57
"в качестве базы для генерации ключа время в mils". Т.е. ключ генерицца при начале сессии клиента, а не на лету, имо. Тебе же надо уникальность беседы сессии выдержать?

ну к примеру большая часть App серверов использует для идентификации сессии пользователя, некий UUID. который собсно генерируется на базе текущего времени. Но есно что не берут тупо время и фигачат его как ключь.
Вобще проблема поставленной задачи "шифрования" именно в том что клиент и сервер должны знать ключь. Независимо друг от друга они не могут "сгенерировать" данный ключь НИКАК!
Поэтому исключаем из схемы клиента. Соответсвенно ключ уникальности выдаёт сервер -> почти SSL
Если задача сводится к ограничению , что бы клиент мог использовать 1н запрос только 1н раз то:
- клент прислал запрос
- сервер сгенерил некий ключь уникальности
- вместе с ответом послал этот ключь клиенту
- следующий запрос клиент посылает уже с этим ключём
- сервер проверят ключь
- сервер меняет ключь и отправлят опять его с ответом клиенту

Как то так коряво :) вполне возможно это уже реализовано как либо

02nz
9.10.2013, 19:03
вместо get запроса - пихать все в пост, добавлять дополнительные поля в заголовок, проверять юзер агента, подключить хттпс. нубасы не пройдут
в ключ можно еще запихать IP, брать его вместе с *сессией* во время 1 запроса

Archy
9.10.2013, 19:05
аха... тоесть при первом соединении или так сказать при первом запросе
сервер выдаст уникальный ключь который потом будет использоватся при генерации хеша во время остальных запрсоов верно?

тогда следующий вопрос
сервер выдал ключь и клиент его получил. если учесть что юзер снифет трафик то он естественно получит ответ от сервера с UUID
то есть он будет знать одну 3ть параметров для генерации хеша что равносильно засунуть время которое использовалась при генерации хеша клиентом сразу в URL
что я и описал раньше


что если использовать порядковый номер запроса? в качестве соли?
тоесть если это первый запрос допустим на логин
то соль будет 1
то есть Login+pass+1
на следующий запрос 2 и т.д.
можно так же придумать более сложный механизм изменения парайткового номера запроса и его сбрасывания

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

Sedoy
9.10.2013, 19:08
вместо get запроса - пихать все в пост +подключить хттпс. нубасы не пройдут

это действительно самый простой способ :) для интереса попробуй заснифать что там под ХТТПС бегает между клиент/сервером

Archy
9.10.2013, 19:11
вместо get запроса - пихать все в пост, добавлять дополнительные поля в заголовок, проверять юзер агента, подключить хттпс. нубасы не пройдут
в ключ можно еще запихать IP, брать его вместе с *сессией* во время 1 запроса

на сколько я понимаую разница между get и post в том что гет пишет параметры прям в url а post в самом пакете
тем не мение если мы учитываем что юзер снифит трафик
то потделать пост будет так же прсот как и гет, тоже самое касается дополнительных полей в хедере, их тоже можно подсмотреть и дописать в искуственно-ручной-фейковый запрос
тоже самое косаемо агента, это все менятеся как 2 пальца! сам ковырял не раз, например BurpSuit отличная штука!

IP статичен = он не повлияет на генерацию хеша
хеш будет так же статичен как если его сделать login+pass или login+pass+ip. его будет достаточно выцепить 1 раз и вся защита слетит

https конечно вариант против man in the midle но что если юзер ламает свой собственный акаунт и просто подменивает значения в правельно сгенерированном запросе? тогда ssl тоже не поможет

Sedoy
9.10.2013, 19:14
https конечно вариант против man in the midle но что если юзер ламает свой собственный акаунт и просто подменивает значения в правельно сгенерированном запросе? тогда ssl тоже не поможет

ну вы прям пытаетесь решить глобальную проблему безопасности в вебе :)
делайте post + https + некую защиту внутри сервера от хитрожопых вариантов

Archy
9.10.2013, 19:15
это действительно самый простой способ :) для интереса попробуй заснифать что там под ХТТПС бегает между клиент/сервером
по сути разницы не какой. ssl помогает от прослушки 3м человеком. тоесть делает закрытый канал на двоих
однако ssl не как не помогает если при стабильном канале на двоих юзер "честно" подменяет данные в хттп пакете

вот посмотрите как выглядит пакет POST
http://portswigger.net/screenshots/proxy_1.png
все что вы видете можно заминить
и переделать пост на гет, поменять агента ну и сами данные! я сам юзал эту апликуху много раз! офигенная штука для хака веба

acidburnsb
10.10.2013, 11:53
Нихрена в этом не понимаю :)

А что если встроить генератор ключей в сервер и клиент, первый запрос от клиента (Login+pass+Key+ключ синхронизации) ответ от сервера (Key1) ответ от клиента (Key2) и так до конца сессии, ключи постоянно меняться и зависимости от синхронизации. снифая любой пакет он у тебя всегда разный.

Archy
10.10.2013, 13:10
генератор ключей будет встроить довольна проблематично!
во первых это PHP а не какая Java с HashMap'ами или С# с Dictionary, так что создание алгоритма генерации ключей на сервер довольно сложно, тем более что если учесть что клиентов куча а сервер 1
это для каждого клиента надо где-то хранить свой ключ и знать на 100% какой должен быть следующий по порядку...

в общем я уже 3й день все думаю, думаю... пришла только одна идея

генерировать хаш с порядковым номером пакета
тоесть клиент кидает первый запрос, скорей всего логин, тогда хасх будет состоять из
login+pass+0 - где ноль - порядковый номер
все последующие запросы будут
login+pass+(N++)

тогда сервер будет способен сам скалькулировать хаш и определить какой порядковый номер пакета пришел без передачи лишних данных по сети
но тут тоже проблема, где хранить промежуточный каунтер для каждого клиента?
скорей всего в базе данных в записи того самого юзера которого мы получим в URL - "userId" но это лишний запрос в базу данных - что не очень хорошо! туда надо бегать только когда надо и как можно реже

так же тут есть проблема с ответами клиенту , чтoб клиент был уверен что запрос пришел от сервера а не от фейк пакета, сервер тоже должен будет генерировать ключики, только он должен тракать "N" номер для каждого клиента отдельно, и тоже его хранить в базе для каждого клиента...
но
это опять 1 лишний запрос в базу при отсылки данных
а так же возникает проблема ресета каунтера т.к. клиент не всегда сможет определенно сообщить серверу что он вышел (типо logout), это не TCP/UDP сетевое программирование когда можно за детектить что peer отвалился и почистить логи с базой за ним...

короче... при разработке проекта постоянно вот такая мелкая херня вылезает которая стопарит весь процесс! и блин не кто не скажет как правильно решить проблему!

ну что я один что ли на всем белом свете кто занимается защитой и синхронизацией клиента / сервера? почему мне не кто не может ответить о объяснить четкий патерн? ведь он есть! я уверен что есть!

я был дико удивлен что даже в Unity Answeres (http://answers.unity3d.com/questions/551579/server-side-security-dynamic-hash-salt.html) где отвечают довольна быстро чут' ли ни как в real time чате - за 3 дня так не кто мне и не ответил :(

Sedoy
10.10.2013, 13:20
ну что я один что ли на всем белом свете кто занимается защитой и синхронизацией клиента / сервера? почему мне не кто не может ответить о объяснить четкий патерн? ведь он есть! я уверен что есть!

я был дико удивлен что даже в Unity Answeres (http://answers.unity3d.com/questions/551579/server-side-security-dynamic-hash-salt.html) где отвечают довольна быстро чут' ли ни как в real time чате - за 3 дня так не кто мне и не ответил :(

Суть задачи не совсем стандартная. И решается скорее всего именно ограничениями логики на стороне сервера. Не уверен как это реализовано в PHP, но что в Java что в C# есть вполне себе адекватные механизмы взаимодействия и хранения сессионных данных юзера, в них и хранят обычно все ключики , счётчики и т.п БД тут не нужна

Sedoy
10.10.2013, 13:25
так же тут есть проблема с ответами клиенту , чтoб клиент был уверен что запрос пришел от сервера а не от фейк пакета

ну а это стандартно SSL подписывание. В общем что бы решить вашу задачу нужно проштудировать кучу литературы или найти спеца по безопасности. Решение будет скорее всего комплексным и не очень простым.
У нас на работе есть 1н, как застану его, спрошу.

Archy
10.10.2013, 13:39
Седой в том и дело что нужно штудировать кучу литературы чтоб найти маленький ответ, который я уверен лежит на поверхности. да и фиг занет еще какой литературы.

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

TAR
10.10.2013, 16:41
Что мешает в начале сессии получить ключ от сервера, зашифровать его клиентской программой и отправить запрос с зашифрованным ключом на сервер?

Хотя это уже вроде предлагали.

Archy
10.10.2013, 17:00
не чо не мешает по сути, только вот ключик то от сервера передавать придется
а это значит:
1) один лишний запрсо - тоесть перед тем как отослать данные, клиент должен будет спросить у сервера новый ключик
2) кличик который сервер отошлет может быть тоже пойман трафик снифером, а значит если учесть что новый ключ это userName+pass+ServerKey
то мы засветим 1/3тью часть всего хеша....

но я только что буквально 40 мин назад решил вопрос вот так:

*при первом запросе с клиента на сервер(первый запрсо точно существует т.к. клиент проверяет наличие интернета), сервер возвращает его текущее время

*клиент получив время сервера сохраняет его с погрешностью в +/- 3 секунды (но как показал реальный тест - это (-1) секунда всегда)

*клиент высчитывает (ServDif) разницу во времени между родным временем и серверным временем

*все последу последующие запросы с клиента на сервер содержат:
(а) userId - по нему сервер найдет userName+pass
(b) data - данные которые клиент передает серверу для процессa
(c) sessionKey - сгенерированный hash из userName+pass+(curentSystemTime + ServDif)
то есть разница во времени между сервером и клиентом всегда учитывается клиентом при отправке запроса, сервер же получает от клиента всегда свое время, т.к. клиент учел разницу и добавил эту разницу к своему времени чтоб оно совпадало

*сервер отвечает примерно по той же логике

единственный минус - это генерация 3 разных хеша на сервере и на клиенте при отправки или получении, т.к. допускается погрешность в -3 секунды

таким образом нам нужно будет сгенерить 3 хеша и сравнить если один из них совпадает с полученым хешом

вот аутпут из консоли подтверждающий что это работает в деле, только на данный момент погрешность учитывается только с -1 секундой, но потом допишу еще 2 хеша для точности



Server:
preHash1 = myLoginmyPass12:17:59
preHash2 = myLoginmyPass12:17:58

hash1 = 2027c08aef30360ba56ecf68b01d809f
hash2 = 3be815110de4c4cbcaa819f91def1877

recieved client hash = 3be815110de4c4cbcaa819f91def1877

hash2 match to client hash

Success

02nz
11.10.2013, 01:09
кэп нашел ответ: пошифровать ЕХЕ клиентский. запихать все в вм и пошифровать дважды

пс. сам пользую такую систему. за год с хреном никто не поломал
клиент использует результат с сервера, даже если этот результат скопируют, то при следующем запуске клиента ничего старого *вклеить* не получится

02nz
11.10.2013, 01:28
кэп еще тут (взаимоисключающий)

основная защита - это дизайн самой проги, а не *омг да тут все пошифровали*
передавать данные можно хоть в текстовом читаемом формате, сервер должен их вменяемо принимать и отсеивать все левое

например клиент высылает запрос *выдать список доступных действий на основе данных XXX*, сервер обрабатывает пришедшие данные, на их основе генерирует код/скрипт/данные и ждет пока клиент не пришлет результат своей работы
номера пакетов даже можно приделать о_О не 1-2-3, а более хитрожопым способом

пс. если данные приходят каждый раз одинаковые (если расшифровать) то это полный фейл и никакого выхода кроме использования упоротых крипторов нет (themida и тп)
и менять алгоритм каждые 2-3 дня, например подгружая код из интернетов, а касперские пусть срут кирпичами

Archy
11.10.2013, 02:42
да со временем я налажал! это все на локалхосте задержка в стабильную -1 секунду
а когда по тестил с ЮСА то там черт ногу сломит... разброс такой что попа...
мне пидется генерить 10 хешев где 5 из них в +5 сек и другие 5 в -5 сек... короче отподает время :(

тогда буду порядковый номер запроса считать как нибудь по хитрому, с каким алгоритмом

02nz
11.10.2013, 16:13
время можно делить на ~1000, округлять и сравнивать
учитывать при этом зону клиента и сервера. как-то так работает валидатор подлинности виндовса

Archy
11.10.2013, 16:49
хммм может мне реально не парится по поводу секунд и округлять до минут?
если хаш ключик будет меняться раз в минуту не чо же страшного не произойдет? если учесть еще наличия ССЛа?

radomba
11.10.2013, 17:01
я че плохо понял. У клиента есть пароль и логин метод POST /auth с передачей (pass + login)sha256 возвразаем сессию (32 битный ключ, который живет 60 минут) , если свзяка существует. Потом обращаемся на этот урл GET /нужный метод + сессия. Сервак проверяет, сессия существует и запрос верный - ОК , иначе NOK

LionbI4
16.10.2013, 14:07
Arche, посмотри как у нас сделанно в vk.com/steamdefense
написал бы сразу в скайп, получил бы ответ за 10 секунд.
Кстати, если вы используете стандартный класс WWW то это фейл.
твои WWW запросы передаются через браузер, и любой нуб очень быстро увидит, что и как ты передаёшь (нажав F12, в закладку network). Хотя если у тебя только бинарник, без веб плеера, то пофиг через что слать. Мы статистику шлём через udp, например. А работа с сервером по http, но не через WWW, а tcp/ip.
Запусти wireshark и посмотри, как мы подписываем наши пакеты.

X-root
16.10.2013, 14:36
А как мы их подписываем? "С любовью от MadSword"? :)

Archy
16.10.2013, 17:47
еххх да, чото ступил! надо было сразу вам ребята стучать в скайп! я вечерком позвоню если ты не против, обсудим