Загрузка файлов методом post через multipart/form-data

Discussion in 'PHP' started by mamontenok, 31 Mar 2012.

  1. mamontenok

    mamontenok Banned

    Joined:
    17 Jul 2010
    Messages:
    12
    Likes Received:
    1
    Reputations:
    5
    Проблема следующая: пытаюсь загрузить картинку на сервер vk.com указанным в заголовке методом.
    Все заголовки и всё тело запроса идентично, тому что показал сниффер при работе браузера. Все переменные указаны. Но загрузка не удается - сервер возвращает "Security Breach2".
    Долгое медитирование над кодом и тщательный анализ всего, что только возможно, показали:
    1. Запрос браузера и запрос скрипта почти идентичны, до единного символа. Отличие заключается в том, что длина пост-данных браузера на два байта больше длины пост-данных моего скрипта. Но, текстово они идентичны. Могу их здесь привести, но не вижу смысла, так как они на самом деле идентичны.
    У браузера "Content-Length: 1022", а у меня "Content-Length: 1020".
    Таким образом, можно с уверенностью утверждать, что в запросе от браузера есть каких-то два символа, которые не видны в тексте и не копируются из редактора сниффера, но их наличие сниффером обнаруживается.
    А теперь
    2. Загрузка фотографий у vk - это не просто форма с инпутом тайп=файл, а флэш-приложение, и, соответственно, эта флешка шлёт файлы на сайт и она добавляет эти два байта (я почти уверен, что дело в них).
    3. Вспомнив про API контакта я полез читать документацию, в надежде отыскать хоть что-нибудь.
    Отыскал. Чтобы приложение для vk.com могло выполнять какие-либо действия в системе (а загрузчик фотографий - как раз приложение), оно должно иметь права доступа, а они определяются битовой маской. Вот что это такое я так до сих пор и не смог толком понять.
    Цитата "Для получения доступа к большинству методов необходимо получить соответствующие права для текущего пользователя. Права приложения представляют из себя битовую маску, составленную путём суммирования кодов необходимых прав. "
    ниже идёт таблица с правами доступа. Интересующий меня пункт - "Доступ к фотографиям." имеет значение "+4".

    Так вот, я подозреваю, что эти два байта и есть эта самая битовая маска. И отправляется она не в виде текста, а в каком-то другом формате (возможно, двоичном), который не отслеживается снифером - сам заголовок Content-Length ловится, а вот данные в теле запроса нет.

    В связи с этим просьба о помощи имеет следующий вид:
    дайте совет как средствами PHP сформировать эту битовую маску (мне нужно "+4") и как правильно её отправить и в каком месте она скорее всего будет находится. Хотя последнее можно определить практическим путём максимум за 1022 попытки.

    И если кто-то знает как и чем можно отследить и вытащить эти два байта (и другие подобные вещи), дайте знать, пожалуйста.
     
  2. eclipse

    eclipse Member

    Joined:
    19 Dec 2010
    Messages:
    155
    Likes Received:
    74
    Reputations:
    85
    А эти байты случайно не связаны с кодировкой?)))))))))
     
  3. mironich

    mironich Elder - Старейшина

    Joined:
    27 Feb 2011
    Messages:
    733
    Likes Received:
    73
    Reputations:
    19
    Wireshrk,
    Оч сомневаюсь...

    http://forum.antichat.ru/showthread.php?t=235948
     
  4. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    mamontenok Какими средствами вы формируете запрос. И покажите код, если не секрет.

    Предположение насчет битовой маски - неправильно.
     
    _________________________
  5. mamontenok

    mamontenok Banned

    Joined:
    17 Jul 2010
    Messages:
    12
    Likes Received:
    1
    Reputations:
    5
    Думаю, что нет. Что в utf-8, что в windows-1251 стандартный набор символов (1-2,A-Z,a-z,!@#$%^&*) подчиняется закону 1символ=1байт, верно? Текст запросов одинаковый.

    Я был бы очень рад, если это так и всё дело в моей ошибке.

    Всё делает php, сами php-файлы сохранены в utf-8. Я пробовал перевести запрос в другую кодировку перед отправкой с помощью iconv(),
    но результата это не дало.
    Вот правильный зарос из сниффера (это даже не отправка картинки а проверка, но алгоритм идентичный):
    Code:
    POST /upload.php HTTP/1.1
    Host: cs305200.vk.com
    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.28) Gecko/20120306 Firefox/3.6.28
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
    Accept-Encoding: gzip,deflate
    Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 115
    Connection: keep-alive
    Referer: http://vk.com/album-37215091_155149442
    Cookie: remixlang=0; remixchk=5; remixsid=_some_sid_; remixreg_sid=; remixrec_sid=; remixseenads=0; remixflash=11.1.102; remixdt=0; remixno_chrome_bar=1
    Content-Type: multipart/form-data; boundary=---------------------------26141215028432
    Content-Length: 1022
    
    -----------------------------26141215028432
    Content-Disposition: form-data; name="mid"
    
    78462767
    -----------------------------26141215028432
    Content-Disposition: form-data; name="aid"
    
    155149442
    -----------------------------26141215028432
    Content-Disposition: form-data; name="gid"
    
    37215091
    -----------------------------26141215028432
    Content-Disposition: form-data; name="hash"
    
    ee4c41f45b901b57a75823fd340c46d5
    -----------------------------26141215028432
    Content-Disposition: form-data; name="rhash"
    
    7cbdf058e6d98075f3c7ff64c4a90b72
    -----------------------------26141215028432
    Content-Disposition: form-data; name="al"
    
    1
    -----------------------------26141215028432
    Content-Disposition: form-data; name="act"
    
    check_upload
    -----------------------------26141215028432
    Content-Disposition: form-data; name="type"
    
    photo
    -----------------------------26141215028432
    Content-Disposition: form-data; name="ondone"
    
    Upload.callbacks.oncheck0
    -----------------------------26141215028432--
    
    А вот мой код php (все переменные правильно собираются на предыдущих страницах и все присутствуют):
    PHP:
    $b=_boundary2();//---формирует разделитель только из цифр
    $content="--$b\r\n";
    $content.='Content-Disposition: form-data; name="mid"';
    $content.="\r\n\r\n$mid\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="aid"';
    $content.="\r\n\r\n$aid\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="gid"';
    $content.="\r\n\r\n$gid\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="hash"';
    $content.="\r\n\r\n$hash\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="rhash"';
    $content.="\r\n\r\n$rhash\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="al"';
    $content.="\r\n\r\n1\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="act"';
    $content.="\r\n\r\ncheck_upload\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="type"';
    $content.="\r\n\r\nphoto\r\n--$b\r\n";
    $content.='Content-Disposition: form-data; name="ondone"';
    $content.="\r\n\r\nUpload.callbacks.oncheck0\r\n--$b--";
    //$content=iconv("utf-8","windows-1251",$content);//--это не помогло
    $request="POST /upload.php HTTP/1.1\r\nHost: cs$server.vk.com\r\n".$prh[2];//--$prh - профиль заголовков, он уже много раз проверен и работает
    $request.="Referer: http://vk.com/$album\r\n".$cookie."Content-Type: multipart/form-data; boundary=$b\r\n";
    $request.="Content-Length: ".strlen($content)."\r\n\r\n$content";
    // Gifts: удалил значение кукисов. На всякий случай
     
    #5 mamontenok, 31 Mar 2012
    Last edited by a moderator: 31 Mar 2012
  6. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    Здесь в конце также должен быть \r\n, то есть:
    PHP:
    $content.="\r\n\r\nUpload.callbacks.oncheck0\r\n--$b--\r\n";
     
  7. mamontenok

    mamontenok Banned

    Joined:
    17 Jul 2010
    Messages:
    12
    Likes Received:
    1
    Reputations:
    5
    Откуда же там взяться переносу строки? Я был уверен, что дело не в этом, но таки попробовал - а вдруг? Нет, к сожалению дело не в этом. Две чёрточки - это конец тела запроса, всё что после них серверу уже не интересно должно быть.
    Ребята, есть ещё предположения?
     
  8. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    Да в том как раз дело, если не работает то ты что то еще неправильно делаешь.

    Ссылка: http://www.php.su/articles/?cat=protocols&page=002
     
  9. mamontenok

    mamontenok Banned

    Joined:
    17 Jul 2010
    Messages:
    12
    Likes Received:
    1
    Reputations:
    5
    Хорошая, статья, но ничего нового она мне не поведала. А по поводу переноса в конце запроса я всё равно не соглашусь - сервер настроен так, что получив после разделителя две чёрточки он не считает всё, что после за данные, то есть тело закончено. Зачем там ещё один перенос? Ведь умные люди писали эту технологию (в статье акцентировано внимание на том, что всё предельно логично) - если получено две черты, то значит всё. К тому же одинарный перенос - это разделитель между отдельными составляющими запроса, а не индикатор конца - для этого есть двойной перенос. К тому же я уже отправлял запросы этим методом без переноса в конце и всё работало.

    Но всё же допустим, что всё же перенос нужен и я ошибся - либо в заголовках, либо в пост данных.
    1. Заголовки уже много раз проверены и работают на других запросах.
    2. Пост данные - ошибка может содержаться в hash и rhash, но они тоже собираются корректно. Формат запроса multipart/form-data соблюден. Aid, mid, gid, al, act, type, ondone - константы. В текстовом варианте ошибок к сожалению наверняка нет.
     
  10. eclipse

    eclipse Member

    Joined:
    19 Dec 2010
    Messages:
    155
    Likes Received:
    74
    Reputations:
    85
    Все верно, но в utf8 добавляется три байта в самом начале файла - EF BB BF, если utf8 файл попытаться открыть чем-то вроде cp1251 получаем 3 непонятных символа, которые по идее не должны отображаться, в частности если
    то надо учитывать, что файлы сохраненные в utf8 и переданные на сервер в текстовом режиме ASCII скорее всего не будут выполняться, их нужно отправлять в бинарном режиме, ты как их загружал?

    mamontenok, а не вариант преобразовать файлы в cp1251 и попробовать то же самое?
     
    #10 eclipse, 1 Apr 2012
    Last edited: 1 Apr 2012
  11. Joker-jar

    Joker-jar Elder - Старейшина

    Joined:
    11 Mar 2007
    Messages:
    581
    Likes Received:
    205
    Reputations:
    37
    А может флешка тупо корректирует значение Content-length? К примеру, прибавляет к реальному значению 2, а на сервере происходит проверка, если Content-length больше реального размера данных на 2, то все ок.

    P.S. Пробовал два дампа снифера сохранять в файлы и сравнивать бинарно?
     
  12. Chrome~

    Chrome~ Elder - Старейшина

    Joined:
    13 Dec 2008
    Messages:
    936
    Likes Received:
    162
    Reputations:
    27
    Ты ее внимательно читал? Если бы ты внимательно читал, то поведал бы, что перенос строки в самом конце запроса нужен. Это спецификация формата multipart/form-data запроса, я лично ничего сам не додумываю, так что если ты с чем то не согласен, так только со спецификацией. Браузеры также отправляют \r\n в конце запроса, можешь в этом убедиться, если нормально посмотришь сниффером.
     
  13. mamontenok

    mamontenok Banned

    Joined:
    17 Jul 2010
    Messages:
    12
    Likes Received:
    1
    Reputations:
    5
    Три байта. А тут два байта. Поэтому это предположение отпадает.

    К тому же не стоит забывать, что я ещё пока не отправляю файл - этот запрос, лишь проверка сервера. В ответ на этот запрос сервер должен вернуть js-код (контакт переполнен js-ом), а он возвращает "Security Breach2" - бесссмысленно отправлять файл этим же способом, если не налажено общение с сервером.

    Уже пробовал переводить запрос в кодировку, с которой работает контакт
    э (windows-1251) перед отправкой, но это не дало результата.

    Если исполняемые php-файлы сохранены в utf-8, то это может отразится только на тексте, который генерируется и отправляется серверу этим файлом. Моему серверу всё равно какой код обрабатывать - utf-8, или cp1251. Но я таки попробую. Хотя тоже, уверен, не поможет, так как два байта внутри запроса, который формирует php, то есть ни о каких служебных символах при сохранении фалов речь не может идти.
     
  14. mamontenok

    mamontenok Banned

    Joined:
    17 Jul 2010
    Messages:
    12
    Likes Received:
    1
    Reputations:
    5
    Хорошо. Сейчас ещё раз перечитаю.

    Внимательно перечитал. Акцентирования на этом моменте было. Перенос строки после -- присутствует только в коде примера. Вполне вероятно, что автор статьи просто автоматом поставил \r\n, и даже использует его, но дело в том, что сервер просто игнорирует этот перенос - в нём нет смысла.

    И раз уж ты настаиваешь, то я поискал спецификацию, а не чью-то статью.
    http://www.w3.org/TR/html401/interact/forms.html
    Думаю, что этому источнику можно доверять - там нет этого переноса. Это логично.

    тоже нет. Я пробовал это делать - сервер просто ждёт те два придуманных байта, ведь не просто так Content-Length отправляется? Этот заголовок отражает реальный размер постданных.

    вот хочу бинарно сохранить дамп браузера и найти там эти чёртовы байты. А если не найду, то буду с дампом скрипта сравнивать. Жуть.
     
    #14 mamontenok, 1 Apr 2012
    Last edited: 1 Apr 2012
  15. mamontenok

    mamontenok Banned

    Joined:
    17 Jul 2010
    Messages:
    12
    Likes Received:
    1
    Reputations:
    5
    Проблема приобрела другой оттенок. Всё таки можно загружать фотографии в vk и без флеша (почему я сразу об этом не опдумал). Там вроде бы всё проще, никаких тебе невидимых двух байтов. Вроде сделал копию запроса, все переменные парсятся правильно, даже длина пост-данных совпадает, но всё равно Security Breach2. Не могу понять что происходит. Уже и так и этак пробовал - никакого эффекта, пляски с кодировками и прочее бесполезны.

    Несколько раз натыкался в интернете на статьи, где говориться, что проблема в настройках провайдера, мол страница загружается с одного IP, а загрузка фотографии уже с другого. Пользуюсь USB-модемом от билайна. Почему-то кажется, что причина не в разных ипах, но проверить пока не знаю как. К тому же полностью сымитировал браузер, который работает на том же IP.

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

    Посоветуйте что-нибудь, пожалуйста. В чём может быть проблема?