(”寝る人”さんのブログより、とてもわかりやすい画像を引用させていただきました)
脆弱性
target="_blank"
のaタグには脆弱性があります。
遷移先から遷移元のページを不正に操作したり、オブジェクトにアクセスしたりできてしまいます。
これによって、フッシング詐欺攻撃を行う余地を生んでしまいます。
サイトで rel="noopener" を使用して外部アンカーを開く(Google Developers)
監査が重要である理由
target="_blank" を使用して任意のページから別のページにリンクしている場合、リンク元のページとリンク先のページは同じプロセスで動作します。 そのため、リンク先のページで負荷の高い JavaScript が実行されていると、リンク元のページのパフォーマンスが低下するおそれがあります。
また、target="_blank" にはセキュリティ上の脆弱性もあります。リンク先のページでは window.opener を使用して親ウィンドウのオブジェクトにアクセスしたり、window.opener.location = newURL によって親ページの URL を変更したりできます。
対策
対策としては、target="_blank"
を指定しいるaタグのrel属性に"noopener noreferrer"
オプションを付与すれば良いです。
まずnoopener
ですが、このオプションを指定することで「別タブの遷移先から、window.openerを参照できなくなる(≒不正操作ができなくなる)」という効果があります。
そのため、基本的な対策は noopener
だけで問題ありません。
しかし、Edgeなどの一部のブラウザではnoopener
自体がサポートされていないこともあります。
その場合の対策としてnorefferer
を指定することで、同じような挙動を実現することが可能です。
norefferer
を指定することで、「遷移先のリソースからリファラーを送らないようにブラウザに指示を出す」ことが可能です。
そのため、noopener noreferrer
の両方をしていすることが望ましいわけです。
ちなみに、rel属性をはじめ各ブラウザのオプション対応については以下のWebサービスで確認をすることができます。
Can I use... Support tables for HTML5, CSS3, etc
背景
既存のWebページ内で、target="_blank"
オプションを指定したaタグが複数存在していました。
Reactであれば、eslintで危険なtarget="_blank"
オプションが弾かれたり、wordpressであれば自動で上記のrel属性が付与されるようになっているので、あまり気にすることはありません。
- React:yannickcr/eslint-plugin-react
- Wordpress:wordpress.org日本語 ワードプレス4.8と4.7.5
しかし、生のhtmlで記述されたLPでは上記のようにデフォルトでtarget="_blank"
を警戒することはできません。
ひとつひとつのaタグにrel属性を付与したりチームの規約にするのも大変なので、javascriptで自動的に属性を付与してあげるのが一番良さそうです。
スクリプト文
// aタグの中身を分解
var aTags = [].slice.call(document.getElementsByTagName('a'));
// 未対応であるIEを除外するため、ブラウザの種別を洗う
var userAgent = window.navigator.userAgent.toLowerCase();
var isIE = (~userAgent.indexOf('msie') || ~userAgent.indexOf('trident'));
function addRels() {
if (!isIE) { //IEは弾く
aTags.forEach(function (el) {
if (el.target === '_blank') {
var rels = el.rel.split(' ');
if (!~rels.indexOf('noopener')) {
rels.push('noopener');
el.setAttribute('rel', rels.join(' ').trim());
}
if (!~rels.indexOf('noreferrer')) {
rels.push('noreferrer');
el.setAttribute('rel', rels.join(' ').trim());
}
}
});
}
};
// 関数を実行
addRels();
少しだけ解説
!~rels.indexOf(文字列)
では、いわゆる「ビット反転演算子」を利用しています。
indexOf
は「文字列が見つかれば文字列が見つかった場所(0以上)」を、「文字列が見つからなければ-1」を返します。
この挙動を利用し、「-1をビット反転演算子にかけ、0を返す」→「if文で0が真と判定される」という文字列マッチングの仕組みを実現しています。