PHP и атрибут target="_blank": решения для веб-разработчика

Раздел: Веб-разработка на PHP -> HTML атрибуты в PHP

Работа с атрибутом target="_blank" в PHP

При генерации HTML-ссылок на PHP часто требуется открыть страницу в новой вкладке. Без дополнительных мер это ведёт к уязвимости tabnabbing (злонамеренный скрипт может изменить содержимое исходной страницы). Основное решение - при использовании target="_blank" всегда добавлять rel="noopener noreferrer".

Основное решение: универсальная функция для безопасной ссылки

Функция принимает URL, текст ссылки и опциональные атрибуты. Автоматически добавляет target="_blank" и rel="noopener noreferrer".


<?php
function safe_external_link($url, $text, $additional_attrs = []) {
    $escaped_url = htmlspecialchars($url, ENT_QUOTES, 'UTF-8');
    $escaped_text = htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    $attrs = 'target="_blank" rel="noopener noreferrer"';
    foreach ($additional_attrs as $key => $value) {
        $attrs .= ' ' . htmlspecialchars($key, ENT_QUOTES, 'UTF-8') . '="' . htmlspecialchars($value, ENT_QUOTES, 'UTF-8') . '"';
    }
    return '<a href="' . $escaped_url . '" ' . $attrs . '>' . $escaped_text . '</a>';
}

echo safe_external_link('https://example.com', 'Пример ссылки', ['class' => 'ext-link']);
?>
  
<a href="https://example.com" target="_blank" rel="noopener noreferrer" class="ext-link">Пример ссылки</a>
  

Возможная проблема:

Если не экранировать текст ссылки, возможна XSS-атака. Обязательно используйте htmlspecialchars(). В функции выше это учтено.

Как просто добавить target="_blank" в ссылку без дополнительной защиты?

Вариант 1: Прямое указание атрибута в строке


<?php
$url = 'https://example.com';
$text = 'Перейти';
echo '<a href="' . htmlspecialchars($url) . '" target="_blank">' . htmlspecialchars($text) . '</a>';
?>
  

Типичная ошибка:

Пропущен rel, что делает исходную страницу уязвимой. Не рекомендуется к применению без осознания риска.

Как защитить ссылку от tabnabbing с помощью rel?

Вариант 2: Добавление rel вручную


<?php
$url = 'https://example.com';
echo '<a href="' . htmlspecialchars($url) . '" target="_blank" rel="noopener noreferrer">Открыть в новой вкладке</a>';
?>
  

Этот способ прост и безопасен, но требует повторения кода.

Как реализовать target="_blank" в шаблонизаторе Twig?

Вариант 3: Использование Twig с автоматической вставкой rel


{# В шаблоне Twig #}
<a href="{{ url }}" target="_blank" rel="noopener noreferrer">{{ text }}</a>
  

Особенность:

Twig по умолчанию экранирует переменные, но не добавляет rel. Атрибут нужно прописывать явно.

Как добавить target="_blank" только для внешних ссылок?

Вариант 4: Определение внешних ссылок по домену


<?php
function is_external($url, $base_host) {
    $host = parse_url($url, PHP_URL_HOST);
    return $host !== null && $host !== $base_host;
}

$base_host = $_SERVER['HTTP_HOST'];
$links = [
    'https://example.com',
    '/internal/page',
    'https://mysite.com/relative'
];

foreach ($links as $link) {
    $target = is_external($link, $base_host) ? 'target="_blank" rel="noopener noreferrer"' : '';
    echo '<a href="' . htmlspecialchars($link) . '" ' . $target . '>ссылка</a><br>';
}
?>
  

Функция parse_url извлекает хост. Если хост отличается от текущего, ссылка считается внешней.

Проблема:

Не все URL содержат хост (относительные). Нужно проверить, что $host не равен null. В примере это учтено.

Расширенные примеры работы с target="_blank" в PHP

Пример 1: Обёртка для ссылок с использованием DOMDocument

Парсинг HTML-строки и добавление атрибутов ко всем ссылкам, ведущим на внешние ресурсы.

Пример

<?php
function add_target_blank_to_external($html, $base_url) {
    $dom = new DOMDocument();
    @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    $links = $dom->getElementsByTagName('a');
    $base_host = parse_url($base_url, PHP_URL_HOST);

    foreach ($links as $link) {
        $href = $link->getAttribute('href');
        if ($href === '') continue;
        $host = parse_url($href, PHP_URL_HOST);
        if ($host !== null && $host !== $base_host) {
            $link->setAttribute('target', '_blank');
            $link->setAttribute('rel', 'noopener noreferrer');
        }
    }
    return $dom->saveHTML();
}

$html = '<a href="https://evil.com">Плохая ссылка</a> <a href="/local">Локальная</a>';
echo add_target_blank_to_external($html, 'http://mysite.com');
?>
<a href="https://evil.com" target="_blank" rel="noopener noreferrer">Плохая ссылка</a> <a href="/local">Локальная</a>

Пример 2: Регулярное выражение для замены target в строке HTML

Быстрая замена, но менее надёжная (не учитывает вложенные кавычки).

Пример

<?php
$html = '<a href="http://example.com" target="_blank">link</a>';
// Добавляем rel, если его нет
$pattern = '/<a\s+([^>]*?)target="_blank"(?!.*?rel=)/i';
$replacement = '<a $1 target="_blank" rel="noopener noreferrer"';
$result = preg_replace($pattern, $replacement, $html);
echo $result;
?>
<a href="http://example.com" target="_blank" rel="noopener noreferrer">link</a>

Ограничение:

Регулярные выражения не подходят для сложной вложенности HTML (например, кавычки внутри атрибута). Лучше использовать DOM.

Пример 3: C использованием фильтрации URL через filter_var

Проверка, является ли URL абсолютным (схема присутствует).

Пример

<?php
function is_absolute_url($url) {
    return filter_var($url, FILTER_VALIDATE_URL) !== false;
}

$links = ['https://example.com', '/relative']; 
foreach ($links as $url) {
    if (is_absolute_url($url) && strpos($url, 'http') === 0) {
        echo '<a href="' . htmlspecialchars($url) . '" target="_blank" rel="noopener noreferrer">Ссылка</a><br>';
    } else {
        echo '<a href="' . htmlspecialchars($url) . '">Ссылка</a><br>';
    }
}
?>
<a href="https://example.com" target="_blank" rel="noopener noreferrer">Ссылка</a><br>
<a href="/relative">Ссылка</a><br>

Пример 4: Хелпер для фреймворков (Laravel-подобный стиль)

Пример

<?php
class LinkBuilder {
    public static function external($url, $text) {
        return sprintf(
            '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
            htmlspecialchars($url, ENT_QUOTES, 'UTF-8'),
            htmlspecialchars($text, ENT_QUOTES, 'UTF-8')
        );
    }
}

echo LinkBuilder::external('https://php.net', 'PHP');
?>
<a href="https://php.net" target="_blank" rel="noopener noreferrer">PHP</a>

PHP атрибут target blank - comments

En
Target blank php (php)