Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce whitelist test for link protocols. #13798

Merged
merged 7 commits into from Dec 10, 2018
Merged

Introduce whitelist test for link protocols. #13798

merged 7 commits into from Dec 10, 2018

Conversation

diosmosis
Copy link
Member

No description provided.

@diosmosis diosmosis added the Needs Review PRs that need a code review label Dec 4, 2018
@diosmosis diosmosis added this to the 3.8.0 milestone Dec 4, 2018
private static $validLinkProtocols = [
'http',
'https',
'tel',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also asume callto as safe, as it is still used by some old system for initiating Skype calls.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When in doubt, I'm against it. callto is no official standard, so I can't promise that there is no way to exploit people clicking on the link accidentally and maybe automatically call a paid number or something like this, because some old badly written software doesn't want to make a confirm screen.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can also happen with tel if you (or some malware) changed the default program.

@@ -11,7 +11,7 @@
url|trim|lower starts with 'data:' %}
{{ url }}
{% else %}
<a href="{{ url }}" rel="noreferrer noopener" target="_blank"
<a href="{{ url|e('url') }}" rel="noreferrer noopener" target="_blank"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haven't tested but think the URL escape should be usually used only on components, not on the whole URL? maybe double check those links still work when they include protocol etc

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from the docs

url: escapes a string for the URI or parameter contexts. This should not be used to escape an entire URI; only a subcomponent being inserted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for the other usages of this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@diosmosis can you change the escaping?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the PR, added a whole_url filter that can be used to whitelist the url. Has to be used in conjunction w/ html_attr or |raw otherwise twig will automatically escape w/ html.

core/Twig.php Outdated
@@ -65,7 +65,9 @@ function piwik_fix_lbrace($string)

function piwik_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) {

$string = twig_escape_filter($env, $string, $strategy, $charset, $autoescape);
if ($strategy != 'whole_url') {
$string = twig_escape_filter($env, $string, $strategy, $charset, $autoescape);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason this is not done for whole_url? wouldn't it be the same as simple adding like a filter whole_url that does below logic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When twig sees an escape like |e('html_attr') it doesn't apply the default |e('html') escape to a variable. But when twig sees |e('whole_url') it applies the default escape. So if we call twig_escape_filter above, it double encodes the URL. Only way I could come up w/ to not do that, is to make sure whole_url doesn't escape anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My message was bit confusing, not sure if understood what I meant. Just to be sure... what I meant was we could add whole_url as a regular twig filter like this maybe:

    private function addFilter_wholeUrl()
    {
        $filter = new \Twig_SimpleFilter('wholeUrl', function ($value) {
            if (!UrlHelper::isLookLikeSafeUrl($string)) { ... } return $value
         });
        $this->twig->addFilter($filter);
    }

and then use it like {{ link|wholeUrl|e('html_attr')}}.
Then we wouldn't need the above if... as whole_url is not really escaping but more like filtering/changing the value? And using |e('whole_url') could give a developer the impression the value will be escaped there (and think another escape html_attr is not needed) which in reality it doesn't...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, yes, makes sense.

core/Twig.php Outdated
@@ -74,6 +76,11 @@ function piwik_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
case 'url':
$encoded = rawurlencode('{');
return str_replace('{{', $encoded . $encoded, $string);
case 'whole_url':
if (!UrlHelper::isLookLikeSafeUrl($string)) { // if this could be a dangerous link, replace it w/ an empty url
return 'javascript:;';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would this need to be javascript:void(0)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it could be? I'm not sure if it would change anything (assuming it's for preventDefault()?).

@diosmosis
Copy link
Member Author

@tsteur looks like there was already a safelink filter, modified & used that.

;
}

public static function isLookLikeSafeUrl($url)
{
if (strpos($url, ':') === false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to merge but maybe we should here also execute self:: isLookLikeUrl ($url) and only return true if it actually is a url?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tel:555... and sms:... links do not pass isLookLikeUrl, since they don't have //. I didn't want to regress anything since isLookLikeUrl is used in tracking and probably everywhere else, so didn't modify it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I did try and make the // optional, but then everything passes isLookLikeUrl

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense 👍

}

$protocol = explode(':', $url, 2)[0];
return preg_match('/^' . implode('|', self::$validLinkProtocols) . '/i', $protocol);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small tweak maybe... should the protocol also check for ends with this? as in '/^' . implode('|', $validLinkProtocols) . '$/i' otherwise smstest might be considered a valid protocol?

@diosmosis
Copy link
Member Author

Tweaked the regex.

@tsteur
Copy link
Member

tsteur commented Dec 10, 2018

feel free to merge when tests pass

@diosmosis diosmosis merged commit 43b6159 into 3.x-dev Dec 10, 2018
@diosmosis diosmosis deleted the url-escaping branch December 10, 2018 19:29
@mattab mattab added the c: Security For issues that make Matomo more secure. Please report issues through HackerOne and not in Github. label Dec 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: Security For issues that make Matomo more secure. Please report issues through HackerOne and not in Github. Needs Review PRs that need a code review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants