- PVSM.RU - https://www.pvsm.ru -

Веб-технологии, которые могут работать не так, как ожидается

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

Веб-технологии, которые могут работать не так, как ожидается - 1 [1]

Я попадал в такие ситуации. Например, при использовании механизма ленивой загрузки [2] в HTML. Соответствующий атрибут очень легко добавить в разметку, описывающую изображение, сделав это только для того, чтобы понять… что для работы ленивой загрузки нужно и кое-что ещё. Здесь мы поговорим и об этой проблеме, и ещё о некоторых возможностях, доступных веб-разработчику, которые могут работать не совсем так, как ожидается.

Ссылки с псевдоклассом :visited обладают ограниченными возможностями стилизации, а getComputedStyle сообщает недостоверные сведения об их стилях

Это ограничение существует уже довольно давно, но оно является хорошим примером того, как злоумышленники могут эксплуатировать возможности браузеров в своих целях. Один из вариантов такой эксплуатации заключался в следующем. Тегу <a> назначался стиль ссылки с псевдоклассом :visited и соответствующий элемент выводился за пределами видимой области страницы. После этого можно было воспользоваться JavaScript для изменения атрибута href данного элемента <a> и для проверки того, приводит ли конкретное значение href к изменению внешнего вида ссылки на такой, каким бы он был, если бы пользователь уже посещал соответствующую страницу. Эти действия позволяли реконструировать историю посещённых страниц.

Было время, когда эта проблема, известная как «CSS History Leak [3]», распространилась настолько широко, что Федеральная торговая комиссия (Federal Trade Commission, FTC — агентство правительства США, призванное защищать права потребителей), ввела серьёзные штрафы [4] за эксплуатацию данной уязвимости.

В наши дни попытка использования getComputedStyle для ссылки с псевдоклассом :visited возвращает не :visited-стиль, соответствующий посещённой ссылке, а стиль обычной ссылки (:link). Это — лишь один из примеров особенностей веб-технологий, о которых нужно знать, так как реальное поведение элементов отличается от того, которого можно было бы ожидать на чисто интуитивном уровне.

Вот [5] пример, демонстрирующий факт возврата одних и тех же вычисленных стилей для непосещённой и посещённой ссылки.

Веб-технологии, которые могут работать не так, как ожидается - 2

Вычисленные стили непосещённой и посещённой ссылки выглядят одинаково

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

  1. Можно сделать так, чтобы применение стиля к посещённой ссылке приводило бы к побочным эффектам (например — к сдвигу макета).
  2. Можно задействовать CSS-селекторы для выбора смежных элементов (~ или +) или дочерних элементов (>) и их стилизации.

Существуют остроумные (но ненадёжные) способы [6] воспользоваться побочными эффектами, но доступные способы стилизации :visited-ссылок [7] ограничены, а некоторые стили (вроде background-color) сработают лишь при их применении к непосещённым ссылкам. А если говорить об использовании смежных или дочерних элементов, то надо начать с того, что getComputedStyle в применении к ним возвращает стиль, который соответствует непосещённой ссылке.

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

В недалёком прошлом один из плюсов использования CDN заключался в том, что это позволяло кешировать в браузере различные ресурсы (вроде шрифтов из библиотеки Google Fonts), которыми могли пользоваться различные сайты. Хотя это даёт большой выигрыш в производительности, применение такого механизма кеширования ресурсов может иметь плохие последствия для приватности пользователей.

Учитывая то, что на загрузку кешированного ресурса уйдёт меньше времени, чем на загрузку ресурса, которого пока нет в кеше браузера, некая веб-страница может выполнить атаку по времени [8], позволяющую ей не только восстановить историю посещённых сайтов, но и узнать о том, кто именно работает за компьютером, и о том, чем он занимается в интернете. Вот что об этом пишет Джефф Кауфман [9]:

Общий кеш, к сожалению, ведёт к уязвимости в сфере приватности. Вот как выглядит простейший вариант соответствующей атаки:

  • Я хочу узнать, являетесь ли вы модератором сайта www.forum.example.
  • Мне известно, что файл www.forum.example/moderators/header.css загружают только страницы, находящиеся в директории, доступной по адресу www.forum.example/moderators/private/.
  • Когда вы посещаете мою страницу, я загружаю файл www.forum.example/moderators/header.css и выясняю, загружается ли он из кеша или нет.

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

Метод performance.now() может возвращать неточные результаты

Пару лет назад была обнаружена группа аппаратных уязвимостей, одна из которых получила название Spectre [10]. Если вас интересуют подробности о Spectre — рекомендую обратиться к ресурсу Google leaky.page [11] (для работы с ним лучше всего подходит Chromium). На этом ресурсе имеется JavaScript-реализация эксплойта, основанного на Spectre, подтверждающая работоспособность соответствующей концепции.

Веб-технологии, которые могут работать не так, как ожидается - 3

Прототип эксплойта Spectre на leaky.page

Правда, нам, для целей данной статьи, достаточно знать о том, что атака базируется на использовании чрезвычайно точных отметок времени (именно их возвращает метод performance.now()) для получения доступа к произвольным местам памяти.

Для снижения риска реализации атак с применением Spectre разработчики браузеров уменьшили точность performance.now() и, кроме того, могут добавить к показателям, возвращаемым этим методом, «шум». Точность временных показателей, возвращаемых performance.now(), может варьироваться в достаточно широких пределах и зависит от различных факторов вроде HTTP-заголовков и настроек браузера.

Ленивая загрузка ресурсов с использованием атрибута loading не работает при отключённом JavaScript

Ленивая загрузка данных [12] — это механизм, который позволяет браузеру загружать ресурсы страницы только тогда, когда они понадобятся пользователю. Например — тогда, когда они окажутся в области просмотра страницы. До недавних пор реализовать этот механизм можно было лишь средствами JavaScript с использованием API IntersectionObserver [13] или задействовав событие onscroll. Атрибут loading (это справедливо для всех браузеров кроме Safari) можно назначать изображениям и элементам iframe (в Chromium). Всё остальное браузер сделает самостоятельно.

Обратите внимание на то, что механизм ленивой загрузки нельзя реализовать с помощью полифилла [14], так как изображение, возможно, уже будет загружаться в момент проверки поддержки браузером атрибута loading.

То, что для ленивой загрузки изображений достаточно воспользоваться атрибутом HTML-тега, может навести на мысль о том, что работоспособность этого атрибута совершенно не зависит от JavaScript. Но, на самом деле, это не так. Взглянем на соответствующий раздел спецификации WHATWG [15]:

Если для элемента отключено выполнение скриптов — вернуть false.

Примечание

Это — мера противодействия отслеживанию деятельности пользователей. Дело в том, что если пользовательский агент поддерживает ленивую загрузку ресурсов при отключённом выполнении скриптов, отключение скриптов не помешает некоему сайту выяснить приблизительную глубину скроллинга страницы в течение сессии. Сделать это можно, размещая в разметке страницы изображения в соответствии с определённой стратегией. Сервер, в результате, может узнать о том, сколько изображений было загружено, и о том, когда именно они загружались.

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

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

Некоторые пользователи могут решить жёстко ограничить функционал браузера, поступая так ради повышения уровня безопасности и улучшения защиты приватности. В Firefox и Tor это делается с помощью системы защиты от отслеживания. Она, кроме прочего, защищает пользователя от сборщиков цифровых отпечатков [16]. В частности, речь идёт о понижении точности некоторых показателей (связанных с размерами элементов и с временем) или об их полной блокировке, об ограничении или отключении некоторых Web API, об исправлении ошибок, позволяющих сайтам выяснять различные сведения о браузерах. Среди материалов, имеющих отношение к WebKit, есть документ [17], посвящённый защите пользователей от сборки цифровых отпечатков.

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

Иногда средства для чтения с экрана не передают семантику некоторых элементов

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

Например, такое происходит в WebKit. Если убрать маркеры списка [18], то, при использовании VoiceOver, теряется семантический смысл списка. Это — весьма распространённый паттерн, в частности — используемый для оформления навигационных элементов сайта. Джеймс Крэйг, который занимается в Apple стандартами доступности контента, говорит [19] о том, почему это — проблема юзабилити, цитируя [20] текст документа W3C «HTML Design Principles»:

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

Ещё один случай, когда семантические значения элементов могут быть утрачены, касается различных способов выделения текста. Возьмём, например, встроенные элементы, такие, как strong, em, mark, ins, del и data. Это — элементы, которые имеют семантическое значение, но они вряд ли будут озвучены [21] средствами для чтения с экрана, так как иначе пользователь услышит слишком много «шума». Это можно изменить, прибегнув к настройкам программ для чтения с экрана, но если есть действительно сильная необходимость машинного озвучивания таких элементов, к ним можно применить класс .visually-hidden, описываемый в этом [22] материале, можно прибегнуть к свойству content псевдо-элемента :before или :after.

Для того чтобы проиллюстрировать эту проблему — я подготовил небольшой пример [23], с помощью которого исследовал то, как NVDA в Firefox 89 и VoiceOver в Safari 14.6 озвучивают семантические элементы.

Вот видеозаписи моих экспериментов

NVDA в Firefox 89

VoiceOver в Safari 14.6

NVDA, в отличие от VoiceOver, озвучивает некоторые семантические элементы (del, ins и mark) и пытается обозначить факт выделения текста постепенным повышением громкости. При этом обе системы не испытывают трудностей при озвучивании содержимого псевдо-элементов :before и :after. VoiceOver, кроме того, озвучивает скобки тегов (произнося «greater than» и «less than»). Обе системы поддерживают настройку озвучивания знаков препинания.

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

Может оказаться, что веб-хранилище хранит данные лишь временно

Спецификация WHATWG, посвящённая веб-хранилищу [24], включает в себя раздел, посвящённый приватности, в котором рассматриваются возможные способы предотвращения использования хранилища в роли механизма отслеживания деятельности пользователей. Один из таких способов заключается в том, чтобы ограничить срок хранения данных. Именно поэтому Safari, что может показаться спорным решением, ограничивает [25] срок хранения данных, записанных в хранилище скриптами, периодом в 7 дней. Обратите внимание на то, что это не относится к «установленным» веб-сайтам, добавленным на домашний экран.

Итоги

Интересно — правда? Некоторые веб-технологии, от которых мы ожидаем определённого поведения, на самом деле, ведут себя не так. Это не значит, что такие технологии реализованы с ошибками, и что их нужно исправлять. Речь скорее идёт о том, что программистам во время работы нельзя терять бдительности.

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

Автор этого материала рассказал о веб-технологиях, которые работают не так, как он ожидал. А что вы внесли бы в собственный список таких технологий?

Автор:
ru_vds

Источник [26]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/css/367560

Ссылки в тексте:

[1] Image: https://habr.com/ru/company/ruvds/blog/575350/

[2] ленивой загрузки: https://css-tricks.com/a-native-lazy-load-for-the-web-platform/

[3] CSS History Leak: https://blog.mozilla.org/security/2010/03/31/plugging-the-css-history-leak/

[4] штрафы: https://www.ftc.gov/news-events/press-releases/2012/12/ftc-settlement-puts-end-history-sniffing-online-advertising

[5] Вот: https://codepen.io/faraixyz/pen/zYwBoMK

[6] способы: https://josephpetitti.com/blog/how-css-can-leak-your-browser-history

[7] способы стилизации :visited-ссылок: https://developer.mozilla.org/en-US/docs/Web/CSS/Privacy_and_the_:visited_selector#limits_to_visited_link_styles

[8] атаку по времени: https://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%B0%D0%BA%D0%B0_%D0%BF%D0%BE_%D0%B2%D1%80%D0%B5%D0%BC%D0%B5%D0%BD%D0%B8

[9] Джефф Кауфман: https://www.jefftk.com/p/shared-cache-is-going-away

[10] Spectre: https://meltdownattack.com

[11] leaky.page: https://leaky.page/

[12] Ленивая загрузка данных: https://css-tricks.com/the-complete-guide-to-lazy-loading-images/

[13] IntersectionObserver: https://css-tricks.com/an-explanation-of-how-the-intersection-observer-watches/

[14] нельзя реализовать с помощью полифилла: https://www.smashingmagazine.com/2019/05/hybrid-lazy-loading-progressive-migration-native/

[15] WHATWG: https://html.spec.whatwg.org/#lazy-loading-attributes

[16] цифровых отпечатков: https://wiki.mozilla.org/Security/Fingerprinting

[17] документ: https://trac.webkit.org/wiki/Fingerprinting

[18] убрать маркеры списка: https://yatil.net/posts/2019/01/13/much-ado-about-no-lists/

[19] говорит: https://twitter.com/cookiecrook/status/1337226933822603270

[20] цитируя: https://www.w3.org/TR/html-design-principles/#priority-of-constituencies

[21] вряд ли будут озвучены: https://www.tempertemper.net/blog/bold-and-italics-arent-read-by-screen-readers

[22] этом: https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/

[23] пример: https://codepen.io/faraixyz/full/wvdWgpB

[24] веб-хранилищу: https://html.spec.whatwg.org/#webstorage

[25] ограничивает: https://webkit.org/tracking-prevention/

[26] Источник: https://habr.com/ru/post/575350/?utm_source=habrahabr&utm_medium=rss&utm_campaign=575350