add rel='noopener noreferrer' to all external links. prevents tab nabbing vuln.

This commit is contained in:
Alex Grintsvayg 2019-10-02 12:47:32 -04:00
parent 4840f17f05
commit b181563aba
No known key found for this signature in database
GPG key ID: AEB3F089F86A22B5
4 changed files with 133 additions and 4 deletions

View file

@ -12,6 +12,7 @@
"erusev/parsedown": "^1.6", "erusev/parsedown": "^1.6",
"erusev/parsedown-extra": "^0.7.1", "erusev/parsedown-extra": "^0.7.1",
"pelago/emogrifier": "^2.0", "pelago/emogrifier": "^2.0",
"mustangostang/spyc": "^0.6.2" "mustangostang/spyc": "^0.6.2",
"paquettg/php-html-parser": "^2.1"
} }
} }

98
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "4df89eeabf27a1fde648e75d29109826", "content-hash": "f15420a8ce3f7350d05c6ac92f3e54b0",
"packages": [ "packages": [
{ {
"name": "erusev/parsedown", "name": "erusev/parsedown",
@ -198,6 +198,102 @@
], ],
"time": "2017-02-24T16:06:33+00:00" "time": "2017-02-24T16:06:33+00:00"
}, },
{
"name": "paquettg/php-html-parser",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/paquettg/php-html-parser.git",
"reference": "d1000936350fed2cb6c54058890d2d19c5ccba4f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paquettg/php-html-parser/zipball/d1000936350fed2cb6c54058890d2d19c5ccba4f",
"reference": "d1000936350fed2cb6c54058890d2d19c5ccba4f",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"paquettg/string-encode": "~1.0.0",
"php": ">=7.1"
},
"require-dev": {
"mockery/mockery": "^1.2",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^7.5.1"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPHtmlParser\\": "src/PHPHtmlParser"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gilles Paquette",
"email": "paquettg@gmail.com",
"homepage": "http://gillespaquette.ca"
}
],
"description": "An HTML DOM parser. It allows you to manipulate HTML. Find tags on an HTML page with selectors just like jQuery.",
"homepage": "https://github.com/paquettg/php-html-parser",
"keywords": [
"dom",
"html",
"parser"
],
"time": "2019-08-18T18:27:45+00:00"
},
{
"name": "paquettg/string-encode",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/paquettg/string-encoder.git",
"reference": "a8708e9fac9d5ddfc8fc2aac6004e2cd05d80fee"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paquettg/string-encoder/zipball/a8708e9fac9d5ddfc8fc2aac6004e2cd05d80fee",
"reference": "a8708e9fac9d5ddfc8fc2aac6004e2cd05d80fee",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5.1"
},
"type": "library",
"autoload": {
"psr-0": {
"stringEncode": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gilles Paquette",
"email": "paquettg@gmail.com",
"homepage": "http://gillespaquette.ca"
}
],
"description": "Facilitating the process of altering string encoding in PHP.",
"homepage": "https://github.com/paquettg/string-encoder",
"keywords": [
"charset",
"encoding",
"string"
],
"time": "2018-12-21T02:25:09+00:00"
},
{ {
"name": "pelago/emogrifier", "name": "pelago/emogrifier",
"version": "v2.2.0", "version": "v2.2.0",

View file

@ -32,8 +32,11 @@ class Controller
unset($viewParameters[View::LAYOUT_PARAMS]); unset($viewParameters[View::LAYOUT_PARAMS]);
$content = View::render($viewTemplate, $viewParameters + ['fullPage' => true]); $content = View::render($viewTemplate, $viewParameters + ['fullPage' => true]);
if ($layout) {
Response::setContent($layout ? View::render('layout/basic', ['content' => $content] + $layoutParams) : $content); $content = View::render('layout/basic', ['content' => $content] + $layoutParams);
}
$content = View::safeExternalLinks($content, Request::getHost());
Response::setContent($content);
} }
Response::setDefaultSecurityHeaders(); Response::setDefaultSecurityHeaders();

View file

@ -176,4 +176,33 @@ class View
Response::setHeader(Response::HEADER_CONTENT_TYPE, 'application/json'); Response::setHeader(Response::HEADER_CONTENT_TYPE, 'application/json');
return ['internal/json', ['json' => $data, '_no_layout' => true]]; return ['internal/json', ['json' => $data, '_no_layout' => true]];
} }
public static function safeExternalLinks(string $html, string $domain): string
{
$dom = new PHPHtmlParser\Dom();
$dom->load($html, ['cleanupInput' => false, 'removeDoubleSpace' => false, 'removeSmartyScripts' => false]);
foreach ($dom->find('body a') as $link)
{
if (static::isLinkExternal($link->getAttribute('href'), $domain))
{
$link->setAttribute('rel', "noopener noreferrer");
}
}
return $dom->root->outerHtml();
}
public static function isLinkExternal(string $url, string $domain): bool
{
$components = parse_url(strtolower($url));
$domain = strtolower($domain);
return $url
&&
!empty($components['host']) // relative urls are not external
&&
$components['host'] !== $domain
&&
mb_substr($components['host'], -mb_strlen('.' . $domain)) !== '.' . $domain;
}
} }