Почему нельзя установить размер шрифта у посещенной ссылки

Привет, Хабр! Представляю Вашему вниманию перевод статьи «Why can’t I set the font size of a visited link?» автора Jim Fisher.



Посещенные ссылки отображаются фиолетовым; не посещенные — голубым. Это различие пришло к нам с времен появления веба. Но CSS позволяет нам перенастроить эти свойства с помощью псевдо-селектора :visited! Скажем, вы хотите сделать посещенные ссылки серыми и уменьшить их размер, для того чтобы показать пользователю что эта ссылка уже была посещена.

a:visited {
  color: gray;
  font-size: 6px;
}

Этот стиль применен к текущей странице:


Окрашивание ссылки в серый цвет, как и предполагалось, уведомляет нас о том что она уже была посещена, но размер шрифта остался прежним! Так происходит, потому что изменение размера шрифта может быть причиной уязвимости! Если CSS сможет изменить размер шрифта, я(Jim) могу сказать посещали ли Вы pornhub.com. Но как?

Веб страницы доступны для инспектирования отображенных элементов на странице. Наиболее очевидный способ — использовать window.getComputedStyle(). Он рассказывает о свойствах, примененных к вышеупомянутой посещенной ссылке; как сообщает браузер: font-size: 18px; color: rgb(0, 0, 238).

Если getComputedStyle сообщит об изменении размера шрифта с 18px до 6px для посещенных ссылок, я(Jim) пойму, что на странице создана ссылка на pornhub.com, затем я проверю размер шрифта, для того чтобы определить историю посещений вашего браузера. Я могу использовать эти данные для таргетированной рекламы, могу продать ваши данные или шантажировать вас, и так далее… Дыра в безопасности была закрыта путем запрета на изменение font-size для a:visited.

Но информация, полученная с помощью getComputedStyle, о цвете посещенной ссылки будет: rgb(0, 0, 238) — то есть голубое. Это ложь — ссылка же серая! Для свойства color, браузеры закрывают дыру в уязвимости другим способом: вместо запрета на изменение, они заставляют getComputedStyle лгать о реальном значении.

Почему же используется два подхода? Почему мы не можем заставить getComputedStyle лгать также и о параметре font-size? Причина в том, что можно исследовать отображенные элементы веб страницы и другими методам, кроме getComputedStyle. Позицию элемента на странице можно проверить так же с помощью .pageXOffset или .pageYOffset. Изменение font-size у посещенной ссылки будет влиять на смещение у других элементов, из-за чего можно косвенно проверить какие ссылки были посещены. Отключение font-size для a:visited — это жесткое, но надежное решение.

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

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

P. S.:
Отдельное спасибо за помощь в корректировке перевода Переверзевой Ольге.

Спасибо Олегу Яценко(Samber), в комментариях он обратил внимание на то что решение проблемы с подобной утечкой данных было впервые реализовано в Mozilla — автор решения David Baron.
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 28

    –39
    Неужели в CSS нет более интересных и часто встречаемых проблем, о которых можно написать статью? Grid? Flex?

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

    Если очень нужно, JS как всегда приходит на помощь в подобных ситуациях:

    <a 
      href="https://habr.com/" 
      target="_blank" 
      onclick="this.classList.add('visited-link')"
    >link text
    </a>


    .visited-link {
      font-size: 35px;
    }
      +7
      В вашем примере размер шрифта изменится при клике по ссылке, а это не означает, что она была посещена. При обновлении страницы стили не сохранятся. При переходе по этой ссылке с другого источника также результата не будет.
        +18

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

          +12
          Вы, видимо, не поняли, тут рассказывается не о проблеме стилизации а о безопасности.
          «В каких ситуациях» — в ситуации когда злоумышленник хочет узнать какие сайты вы посещаете.
          Если бы эта уязвимость работала, злоумышленник мог бы разместить на своём сайте 100500 ссылок, которые хочет проверить, и мог бы увидеть какие ссылки у посетителей отмечались бы другим цветом или размером.
          JS вам в этом никак не поможет.
            +4
            Собственно, когда уязвимость существовала, так и делали.
            0
            Неужели в CSS нет более интересных и часто встречаемых проблем, о которых можно написать статью? Grid? Flex?

            В CSS, как кажется, порой проблем больше, чем решений. И уж про Grid, Flex, а также про много других подходов писали, и не раз, или я не прав? Но почему бы на Хабре (где вообще пишут обо всем, за уши притягивая тему к словам «ит» и «гик», как в том анекдоте про рыбу и блох) не описать такую вот тему, тем более что она, и правда, имеет очень интересную причину — я про откровенно, как кажется, нелогичное поведение браузера, идущее не от бага, а от логики безопасности? И лично мне, кстати, было очень интересно узнать об этом поведении, поскольку проблему, которую оно решает, я как раз считаю очень серьезной.
              +3
              Что-то я не понял, если скрипт может выполнить `getComputedStyle`, то он может и навесить `onclick` на все ссылки, в чем состоит защита в этом случае?

              PS: нашел ответ на reddit, без защиты сторонний скрипт мог бы нарисовать невидимый блок с тысячами ссылок и проверить были ли они посещены или нет.
                +10

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



                Я вижу это так:


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

                  0
                  У меня не подсветился habr.com, хотя я на habr.com прямо сейчас о_О
                  image
                    +1
                    в ссылке http
                +1
                А почему нельзя делать скрин ссылки, и проверять цвет по конкретной точке? Можно самому воткнуть стиль для посещенных ссылок, за футером помещать интересующие ссылки, и смотреть цвет пикселя
                  +6

                  Попробовал только что поиграться с html2canvas.js — браузер не отдает в читаемый канвас свойства псевдоэлемента :visited.

                    +3

                    html2canvas не делает скриншот, а рендерит страницу с нуля, используя данные из HTML и CSS кода. Поэтому на него действует защаита, встроенная в getComputedStyle.

                    +8
                    Потому-что нет встроенных средств сделать скриншоты по причине безопасности
                    +10
                    В Chrome есть дыра через которую можно узнать состояние :visited. Почти год прошел с тех пор как был открыт Issue но команда Chrome не торопится его закрывать.
                      0
                      А вот оно где. Я до этого не дошел, через час надоело. Можно еще порыться в направлении свойств дочерних/соседних элементов: а:visited [selector]
                        +1
                        Описанный способ так же работает и в последней версии ФФ — можно скачать attack.html из того issue, и попробовать.
                        –1
                        А разве нельзя на a:visited повешать еще что-то типа display:none, и проверять уже дальше, что элемент вообще видимый?
                          +1
                          Display не входит в список разрешённых для :visited свойств, поэтому браузер просто проигнорирует свойство
                            0
                            Allowable CSS properties are color, background-color, border-color, border-bottom-color, border-left-color, border-right-color, border-top-color, column-rule-color, and outline-color.
                          • UFO just landed and posted this here
                              0

                              Именно так, «изоляцией Ржевского в гусарской балладе», собираются решать проблему посещенных ссылок в будущем. Но пока уже много лет есть существующее решение, браузеры стремятся хотя бы его поддерживать единообразно. И в этом, имхо, правы.


                              А отправлять на сервер небезопасные данные через CSS, конечно, незачем, но не всегда вас об этом спрашивают..;)

                              • UFO just landed and posted this here
                                +4
                                плохая метафора как кот с дверью
                                +1
                                Какое же облегчение чувствуешь, читая такие строки. Даже на таком уровне используют костыли. Фух, я не один такой!
                                  +4
                                  Весь веб — это много много костылей, чтобы просто показать пользователям некое изображение (в виде текста, картинок или видео).
                                  0
                                  Для меня не менее интересным открытием стал запрет на использование CSS переменных внутри :visited в Хроме. А вот в FireFox это не запрещено.

                                  Only users with full accounts can post comments. Log in, please.