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

open-uri: application URI handlers #1313

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

andyholmes
Copy link
Contributor

@andyholmes andyholmes commented Mar 22, 2024

Add support for applications to handle URIs more specifically than just by scheme.

If an application invokes OpenUri() with URI it claims to handle, the portal will omit it from the possible handlers and fallback to the browser if there are none.

This is being coordinated with the proposed intent-apps-spec, and an initial implementation in GLib:

@andyholmes andyholmes force-pushed the andyholmes/uri-handling branch 4 times, most recently from c44ea99 to 3be7a58 Compare April 9, 2024 09:17
@andyholmes andyholmes force-pushed the andyholmes/uri-handling branch 3 times, most recently from 4078d00 to 71d59ad Compare April 17, 2024 01:56
@andyholmes andyholmes changed the title open-uri: Basic globbing for detailed URI handlers open-uri: application URI handlers Apr 17, 2024
@andyholmes andyholmes force-pushed the andyholmes/uri-handling branch 2 times, most recently from 86a74a4 to 4806ad1 Compare April 19, 2024 17:12
Pre-empt the default URI scheme handlers, so that applications may
specify scheme, host, port and path patterns.

URIs are checked incrementally by part.
When matching URI handlers, prevent sending a link back to the source
application. This allows an application to define broad patterns and
reject unwanted URIs by explicitly calling `OpenUri()`.
This adds support for deserializing URI handlers from `.desktop`
overrides in the user data directory, with the format:

```ini
[URI Handler example.com]
Scheme=https;
Host=example.com;*.example.com;
Port=443;
Path=/resource/*
```
@razzeee
Copy link
Contributor

razzeee commented May 10, 2024

This is a good start.

But I think we also need an additional handler for something requesting a specific app to open a deep link and if that app is not installed, prompt to install it.

There recently was a security "incident" in Germany, where an app was attacked, due to it only asking for a schema and somebody placed a malicious app, that basically got priority (as oder is not defined by the ios implementation it seems).

So the correct way to handle that would have been to use apples universal links https://developer.apple.com/ios/universal-links/

Here's a post about the vulnerability https://ctrlalt.medium.com/space-attack-spoofing-eids-password-authenticated-connection-establishment-11561e5657b1

@vimpostor
Copy link

I think it would also be good to support proper Regex in addition to the simple patterns that g_pattern_match_simple() supports (supported URLs can become complicated, e.g. a Spotify desktop application would only be able to support a very specific set of URLs like ^https://open.spotify.com/(artist|album|track)/[[:alnum:]]+\??.*$).

See for example android:pathPattern vs android:pathAdvancedPattern as prior art from the Android scheme matching implementation.

@andyholmes
Copy link
Contributor Author

@razzeee

But I think we also need an additional handler for something requesting a specific app to open a deep link and if that app is not installed, prompt to install it.

How would a user implicitly request a specific app with the action of opening a URI? Would that make e.g. Freetube unable to open YouTube links if there were an official YouTube app?

Apple uses an apple-app-site-association file on the application's website, but that approach seems like a fairly big constraint for FOSS projects. This sort of metadata seems like it might be a good fit for AppStream, but then we'd have to rely on that being available and up to date.

@andyholmes
Copy link
Contributor Author

@vimpostor

I think it would also be good to support proper Regex in addition to the simple patterns that g_pattern_match_simple() supports

I'm not sure that's a good idea, to be honest. Something like Android's pathAdvancedPattern might be possible, but Regex is far too vulnerable to DoS attacks.

Would https://open.spotify.com/*/* not work for Spotify, or are their paths under open.spotify.com/ that aren't for "opening"? I'm interested in examples of URIs that can't be matched with simple patterns, but we'd like to keep this relatively simple and safe, too.

@vimpostor
Copy link

but Regex is far too vulnerable to DoS attacks.

That sounds like a far fetched attack scenario. Regex is cheap enough that an attacker would need to busy-spin matching URLs anyway, so I don't think there is a difference between matching patterns and matching Regex in your scenario.

Would https://open.spotify.com/*/* not work for Spotify, or are their paths under open.spotify.com/ that aren't for "opening"?

I am not entirely sure, but I know that the Spotify APK doesn't accept all paths under that domain, but rather has a few hardcoded prefixes. Note that it uses neither pathPattern nor pathAdvancedPattern, but a conjunction of pathPrefix.

In any case such a Regex feature could be still useful for a user who wants to configure what exactly opens up in the app, I don't think you need to be terribly creative to find usecases there.

That being said, I don't want to block this very useful PR with my remark, just wanted to put some eyes on the prior art that happened in Android land.

@andyholmes
Copy link
Contributor Author

That sounds like a far fetched attack scenario. Regex is cheap enough that an attacker would need to busy-spin matching URLs anyway, so I don't think there is a difference between matching patterns and matching Regex in your scenario.

ReDoS attacks are relatively common, and likely why Android's pathPatternAdvanced is not Regex and explicitly doesn't support backtracking.

Note that it uses neither pathPattern nor pathAdvancedPattern, but a conjunction of pathPrefix.

Sorry, I'm not sure what you mean by conjunction here.

That being said, I don't want to block this very useful PR with my remark, just wanted to put some eyes on the prior art that happened in Android land.

Thanks! We've been looking at examples from other platforms, and it's most useful to have real-world examples of the URIs. I suspect most applications try to keep it simple, but more data points are always helpful.

Something not immediately obvious is that applications can "reject" URIs by invoking OpenUri(). To prevent re-entrancy a URI can't be directed to the application that opened it, so this may be an effective way to handle over-eager matching patterns like open.spotify.com/*.

@razzeee
Copy link
Contributor

razzeee commented May 21, 2024

@razzeee

But I think we also need an additional handler for something requesting a specific app to open a deep link and if that app is not installed, prompt to install it.

How would a user implicitly request a specific app with the action of opening a URI? Would that make e.g. Freetube unable to open YouTube links if there were an official YouTube app?

I think the idea would not be a user to do that, but an app shelling out to another app. Like a government app (a), trying to authenticate you via another specific app (b). So app a would somehow hard code the calls to app b, and it should never allow for app c (an attackers app) to handle that call.

App b could be something along the lines of this for e.g. https://flathub.org/apps/de.bund.ausweisapp.ausweisapp2

And yes, if the youtube app used "the wrong api" as in what I proposed here, it would lock it into the youtube app and freetube would be unable to open these.

Apple uses an apple-app-site-association file on the application's website, but that approach seems like a fairly big constraint for FOSS projects. This sort of metadata seems like it might be a good fit for AppStream, but then we'd have to rely on that being available and up to date.

I actually haven't looked that deep, but it seemed pretty important to me, to cover both bases.

@swick
Copy link
Contributor

swick commented May 22, 2024

I think we can reasonably ignore regex for now. If the simple matching turns out to be insufficient we can revisit that part and make it more powerful. We just have to make sure the portal allows this forwards compatibility.

@swick
Copy link
Contributor

swick commented May 22, 2024

mime-types and URI handlers have the same security issue described by @razzeee. Android and iOS usually solve this kind of issue by putting up a file at a well-known path for URIs but we can't rely on that. We need a solution that works by putting some metadata in the flatpak manifest or desktop file and rely on reviews to make sure the permissions granted there are reasonable.

This MR already gets the "permissions" from the desktop file which should be reviewed by actual people.

I'm not sure if we need some mechanism to lock specific URIs to specific apps. If there is an alternative ausweisapp for example, it should still work. If an app which has no business handling this still exposes the handler, it should not pass review.

@razzeee
Copy link
Contributor

razzeee commented May 22, 2024

If there is an alternative ausweisapp for example, it should still work. If an app which has no business handling this still exposes the handler, it should not pass review.

I'm not convinced this would work in the real world. I guess we're saying, that only apps from flathub are safe then, as only those might be reviewed.

It would obviously be nice for an alternative ausweisapp to work, but it could also just be an app attacking/sniffing on the other app. And this would be possible, as soon as the user has some other repo added I think? As we're not forcing apps from flathub to only open apps from flathub for e.g.

@swick
Copy link
Contributor

swick commented May 22, 2024

We will have the same problem with webauthn/fido where an app should not be able to access any credentials but only ones scoped to itself or specific remotes (hosts basically). Again, iOS and Android solve this by having a file at a well-known location on the host but we won't be able to get e.g. google to add random gnome apps to that file. We'll again have to use the flatpak manifest or the desktop file to make the association and let reviewers check if it is reasonable.

I'm also not sure how "pinning" a specific app would be useful. Why can't an "evil" app not pin itself?

@razzeee
Copy link
Contributor

razzeee commented May 22, 2024

I'm also not sure how "pinning" a specific app would be useful. Why can't an "evil" app not pin itself?

Because it's pinned in the other app calling it. Without control of the other app, the "evil" app can't change that. (at least that's what I hope and what iOS seems to rely upon)

@swick
Copy link
Contributor

swick commented May 22, 2024

Okay, but how does the calling app know which is not the evil app? The calling app must understand both the URL and that one specific app that should then be called. Could as well just call the app directly instead of going through portals (not right now because dbus limitations, but it should become possible).

@razzeee
Copy link
Contributor

razzeee commented May 22, 2024

Yeah, it probably needs both to lock it in, so rdns and repo/origin.

I have no idea about the implementation details when it comes to dbus, I just feel strongly that we need both for a versatile / safe platform.

Add support for reading URI handlers from a proposed addendum to
the intents-spec in the form:

```ini
[org.freedesktop.UriHandler]
Patterns=*.openstreetmap/node/*;*.openstreetmap/way/*;
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Needs Triage
Development

Successfully merging this pull request may close these issues.

4 participants