Как исправить ссылки target = ”_ blank”: проблема безопасности и производительности на веб-страницах
Почти на каждой веб-странице есть ссылки, которые открываются в новой вкладке, а другая веб-страница остается доступной. Например, информационные агентства будут писать твиты о последних событиях в Твиттере: твит содержит краткое описание статьи и ссылку для просмотра всей истории на их веб-странице.
По умолчанию ссылки в HTML открываются на вашей текущей вкладке, и это обычно полезно. В конце концов, как владелец веб-страницы, вы хотите, чтобы люди оставались на вашей веб-странице и не отвлекались на слишком много открытых вкладок или других веб-страниц. Внимание людей ограничено, поэтому вы хотите, чтобы они сосредоточились на вашем контенте. Кроме того, ссылки, которые открываются в новой вкладке, также плохо влияют на доступность, если вы не предоставляете подсказки и атрибуты ARIA.
Все еще есть случаи, когда вы хотите или должны ссылаться на внешние страницы. Вышеупомянутый пример информационного агентства является хорошим примером: информационные агентства хотят, чтобы люди читали новостные статьи на своих веб-страницах, а не на внешних веб-страницах, таких как Twitter, потому что вам нужны доходы от рекламы от посетителей. Вот почему Twitter позволяет своим пользователям использовать ссылки в своих твитах. Обычно открытие ссылок в новой вкладке используется, когда ссылка принадлежит не вам, а третьему лицу.
Как попасть прямо в ловушку
Итак, как сделать так, чтобы ссылка открывалась на новой вкладке в HTML? Это легко (может быть, слишком просто), нам даже не нужно писать код JavaScript. Все, что нам нужно сделать, это добавить атрибут target=”_blank”
к вашей привязке, и все готово.
‹a href="https://sedeo.net" target="_blank"›. Перейти к Sedeo ‹/a›
Теперь каждый раз, когда кто-то нажимает на эту ссылку, веб-страница (в данном случае Sedeo) будет открываться в новой вкладке.
Давайте представим, что веб-страница, на которую я ссылаюсь в примере, безопасна (я знаю, что это безопасно, потому что я ее создал). Но, возможно, внешняя веб-страница принадлежит третьей стороне (например, поставщику услуг или покупателю). Или, может быть, вы разрешите пользовательский контент на своей веб-странице (например, комментарии, в которых пользователи могут использовать ссылки на внешние веб-страницы). В этих случаях вы не контролируете эти связанные веб-страницы и не знаете, что там делают люди, стоящие за ними.
Если вам не повезло, вы могли попасть в ловушку и непреднамеренно создать проблему с безопасностью и производительностью.
Проблема безопасности: табнабинг.
По умолчанию, когда вы открываете веб-страницу в новой вкладке, щелкнув ссылку с target="_blank"
, эта страница теперь имеет ограниченный доступ к странице, на которую указывает ссылка. Самым важным аспектом безопасности, о котором я могу думать, является то, что вы можете изменить window.opener.location
страницы ссылок. Например: веб-страница сомнительных новостей использует Twitter для распространения некоторых историй. Пользователь Twitter видит твит, а затем щелкает, чтобы прочитать всю историю на теневой веб-странице новостей, которая открывается в новой вкладке. Между тем, страница теневых новостей может использовать этот встроенный скрипт на своей странице.
window.opener.location = «http://fake-twitter.com»
и теперь пользователь больше не в Твиттере, а на этой фальшивой веб-странице, похожей на Твиттер. Эта фальшивая страница выглядит точно так же, как Twitter, и требует входа в систему, потому что вы по какой-то причине вышли из системы. Пользователь, не особо задумываясь об этом, вводит свои учетные данные и отправляет форму.
Вы только что отправили свои личные учетные данные кому-то, кто теперь может войти в вашу учетную запись Twitter и сделать что-нибудь вроде быстрого изменения учетных данных.
Этот вид фишинга называется (обратная) табуляция. Напоминаем, что вот определение фишинга из Википедии:
Фишинг - это мошенническая попытка получить конфиденциальную информацию, такую как имена пользователей, пароли и данные кредитной карты (и деньги), часто по злонамеренным причинам, путем маскировки под надежную организацию в электронном сообщении.
Влияние на производительность просмотра
Интересно, что target="_blank"
также влияет на производительность вашего просмотра веб-страниц, что я наблюдал воочию. Каждый крупный современный браузер, такой как Chrome и Firefox, является многопроцессорным. Благодаря синхронному межоконному доступу, который DOM предоставляет нам через window.opener
, окна, запущенные через target="_blank"
, попадают в один и тот же процесс и поток. То же самое верно для iFrames и окон, открытых через window.open.
В качестве побочного проекта я разработал персональную «стартовую страницу», которая выглядит и ведет себя так же, как и Speed Dial в Opera. При нажатии на одну из плиток веб-страница открывается в новой вкладке (каждая ссылка имеет target=”_blank”
). Я заметил, что мой просмотр веб-страниц по какой-то причине был медленнее, и я подозревал, что это как-то связано с моей собственной страницей, потому что я не сталкивался с этой проблемой, когда не использовал свою собственную страницу. После того, как я узнал об этой проблеме с target=”_blank”
, я изменил свой код, и снижение производительности исчезло.
Какие популярные веб-страницы уязвимы?
Проблема хорошо известна, но в последние несколько лет она стала более известной. Вот несколько веб-страниц, на которых работает этот подход с использованием табуляции (на момент написания этой статьи). Я надеюсь, что эти веб-страницы, которыми пользуются миллионы людей, быстро решат эту проблему:
- Ebay.com (хотя влияние на безопасность должно быть уменьшено, поскольку большинство внешних ссылок работает в iFrame)
- Heroku.com
- Моя Нинтендо
Некоторые веб-страницы, которые успешно блокируют этот подход табуляции:
- GitHub.com
- Slack.com
- Twitter.com
- Facebook.com
- United-domains Webmailer (они исправили это, когда я их уведомил. В этом дух!)
- dev.to (они исправили это быстро, когда я их уведомил)
Простые решения
Есть два решения для устранения этих проблем. Первое решение - добавить атрибут rel="noopener noreferrer"
к каждой ссылке с target="_blank"
. noopener
- необходимое значение, чтобы гарантировать, что связанные страницы не имеют доступа к странице, на которую указывает ссылка. Используйте этот подход, когда у вас есть доступ к HTML-коду и когда не так много случаев, чтобы исправить это вручную. Некоторые CMS, такие как WordPress (≥ 4.7.4), делают это автоматически, поэтому вам не нужно предпринимать никаких действий. Приведенный выше (теперь безопасный) пример теперь будет выглядеть так:
<a href=”https://sedeo.net” target=”_blank” rel=”noopener noreferrer”>Go to sedeo</a>
Браузеры поддерживают noopener
довольно хорошо в современных браузерах, браузеры Microsoft, такие как Internet Explorer, как обычно, являются исключением. Однако Microsoft Edge предположительно не поддерживает window.opener для target="_blank"
ссылок.
Второй подход - использовать JavaScript для решения этой проблемы. Используйте это, когда вы управляете CMS или если существует слишком много вхождений, чтобы исправить все эти ссылки вручную.
var otherWindow = window.open(); otherWindow.opener = null;
Если вы разрешаете контент, создаваемый пользователями (например, в социальной сети), не забудьте очистить ссылки. Twitter автоматически добавляет rel="noopener"
к каждой ссылке в твите.
ОБНОВЛЕНИЕ: раннее выявление небезопасных статических / динамических ссылок
Вы можете определить небезопасные ссылки на раннем этапе, если добавите правило lint. Таким образом, вы сможете идентифицировать небезопасные ссылки в вашем HTML. Этот линтер будет. Вот реализация такого правила для популярного линтера HTMLHint:
module.exports = (HTMLHint) => { HTMLHint.addRule({ id: 'noopener-external-links', description: 'Links with target="_blank" must have a rel="noopener noreferrer" attribute to prevent reverse tabnabbing.', init: function (parser, reporter) { var self = this; parser.addListener('tagstart', function (event) { var tagName = event.tagName.toLowerCase(); var col = event.col + event.tagName.length + 1; var mapAttrs = parser.getMapAttrs(event.attrs); if (tagName === 'a' && mapAttrs.target && (!mapAttrs.rel || mapAttrs.rel.indexOf('noopener') === -1 || mapAttrs.rel.indexOf('noreferrer') === -1)) { reporter.warn('Prevent reverse tabnabbing of external links by providing rel="noopener noreferrer" attribute.', event.line, col, self, event.raw); } }); } }); };
Чтобы определить, небезопасно ли открытие страницы в новом окне с помощью JavaScript, вы можете написать ESLint / TSLint правило, которое запрещает прямое использование window.open
в пользу настраиваемой функции, которая сбрасывает открыватель.
Динамически созданные ссылки (например, через jQuery) не будут идентифицироваться линтерами. Один из способов идентифицировать их - многократно сканировать DOM на наличие небезопасных ссылок при локальной разработке. Вот простой способ сделать это на простом JavaScript:
if (!environment.production) { setInterval(() => { const links = Array.from(document.querySelectorAll('a[target]')); for (let link of links) { const target = link.getAttribute('target'); if (target && (!link.getAttribute('rel') || link.getAttribute('rel').indexOf('noopener') === -1)) { console.error(`Unsafe link ${link} is vulnerable to reverse tabnabbing.`); } } }, 5000); }
Заключение
Интересно, что Google не считает это серьезной проблемой безопасности.
На мой взгляд, нет серьезных недостатков в применении первого решения (добавление rel="noopener"
к каждой ссылке target="_blank"
). Этот выпуск показывает, насколько легко создать лазейки в безопасности вашей веб-страницы. Кроме того, влияние target=”_blank”
ссылок на производительность также может быть существенным для веб-приложений с высокой потребностью в производительности. Если вы веб-разработчик, я бы посоветовал вам проверить свой код, чтобы устранить эту проблему.
Спасибо, что прочитали мои мысли по этому поводу. Я хотел бы услышать ваши мысли в комментариях.
Источники: