Let's take Discord as an example. When you are surfing the internet and you click a button to join a discord channel, automatically, your desktop Discord app opens. In other words, a webpage is opening an application of your system. How the **** could be this possible? Is this really secure?

General mechanisms

Actual browsers have different type of methods to handle the communication between Website to Applications. In this entry I will only focus in some of these mechanisms so, I highly encourage to read this serie of articles link by ericlaw. These articles provides a bigger picture and a detailed explainition of some of them. Also, he provides a really good discussion of the security risk inherited by this mechanisms. Following tables provides a summary list.

Mechanism DesktopOS independent Type
App protocol (websec://) Yes Fire-and-forget
File downloads Yes Fire-and-forget
File System Access API Yes Read-and-write
Extension (Native Message API) Yes (¿Safari?) Bi-directional communication
Drag&Drop Yes Fire-and-forget
System clipboard Yes Fire-and-forget
Local Web Server Yes Bi-directional communication
DirectInvoke/ClickOnce Windows only Fire-and-forget
getInstalledRelatedApps() Windows only (caniuse) Bi-directional communication
AppLinks Deprecated Fire-and-forget
Legacy Plugins/Active X architecture Deprecated Bi-directional communication

Mobile specific:

Mechanism MobileOS independent Type
Android Intents Android Fire-and-forget
Android Instant Apps Android Fire-and-forget

Browser dev specific:

  • Site-Locked Private APIs
  • Native Url Protocols

In the following sections I will focus in Application protocols and Local Web Servers.

App protocols

A simple explanation of application protocols would be to define a new ‘protocol’. So, instead of using “http” or “file”, you define a new special key (e.g., ‘websec://‘). Defining a new protocol allows to describe how this protocol must be handle by the browser. In technical words, the idea is to define a new URL scheme that will open a desktop application. As the reader might notice, improperly parsing URI schemes could produce critical vulnerabilities. One of the application I found that this mechanisms is used is Microsoft Teams (msteams://). In the following code snippet you can see how through the Chromium code comments developers could be also confused of URL parsing.


Uniform Resource Identifier (URI) scheme identifies a resource using a name, location or both. Uniform Resource Locator (URL) scheme identifies the location of a unique resource. So, URL scheme is a subset of URI schemes defined by the syntax of: [scheme]://[user]:[password]@[host]:[port]/[url-path]. More info. RFC.

Security Risk

These mechanisms inherits some security risks (e.g., Browser Sandbox Scape). As ericlaw describe in his blog, main security risk are:

Input Sanitization: App Protocols were not designed with the expectation that the app could be exposed to potentially dangerous data from the web at large.”

Context information: When the protocol is invoked by the browser, it simply bundles up the URL and passes it on the command line to the target application. The app doesn’t get any information about the caller (e.g. “What browser or app invoked this?“, “What origin invoked this?“, etc) and thus it must make any decisions solely on the basis of the received URL..”

Default schemes/protocol

Chromium based browser have a set of defined protocols in the code. It could be that this set of ‘default’ protocols may vary depending on the browser or the OS. In the case of Brave Browser, they have a small wiki on their github Wiki: Adding a protocol scheme to Brave. The standard schemes defined in HTML spec are:

  • http/https: Normal scheme used for HTTP/s protocol navigation.
  • file: Allows to retrieve/view local files (e.g., file:///home/websec/Downloads/Syllabus.pdf).
  • chromium or browser family (e.g., microsoft-edge, brave): Used for internal browser pages (e.g., chromium://settings).
  • mailto: It opens your default mail application to send an email to the person that follows the scheme (e.g., mailto:websec@abc.com).
  • tel: If you have your phone connected to your computer, it allows to make calls.
  • All: bitcoin ftp ftps geo im irc ircs magnet mailto matrix mms news nntp openpgp4fpr sftp sip sms smsto ssh tel urn webcal wtai xmpp
As Javier Fernandez describes in some entries in Igalia's blog, they changed the logic from `/chrome` to `/components`. Following this logic, the classes or code could change in the following years.

The following class diagram by jfernandez post at Igalia’s blog illustrates the multi-process architecture of the Custom Handler logic.


In order to have deeper look into how schemes are added, jfernandez provides us with another class diagram.


Despite jfernandez provides us with a class diagram, it is really hard to take a look the real code running inside the browser. However, I encourage you to go and see it for yourself (Chromium code).

Custom protocol handlers

Chromium based browsers when a custom, not blocked, protocol is used prompt a popup asking if the link should be open or not.


If you want to disable this behavior in chromium-based browsers, you need to go to Settings > Site settings > Additional permissions > Protocol handlers. In the case you want to check the behavior of your browser, I found this website. In chromium-based browser you can check the allowed/ignored custom protocol in the Preferences file inside the configuration of your browsers. In GNU/Linux, this file is located at ~/.config/BROWSER/Default/Preferences. In Firefox the file we need to found is handlers.json or opening the browser Settings > General > Applications. If you are using snap store, the browser config is inside this folder.


How to create a custom protocol/scheme handler

As I explained before, if the chromium-based browser doesn’t know how to handle a custom protocol, it will use the OS preferences of the user. In my case, this default application is xdg-open. In the following lines, I will create a new custom protocol “note://“. For Safari check the link. First, I created a simple script that will print the arguments received from the browser.


echo -ne "\nArguments: $@ \n"
while true
sleep 5;

After creating the script, we need to configure ‘xdg-open’ to use this script when a specific MimeType is matched. For this task, we need to create a ‘.desktop’ file like the following one:

[Desktop Entry]
Exec=/path/test.sh %u

Finally, we need to install this entry using the command ‘xdg-desktop-menu install path/to/file.desktop’. I used the previous named website to test the protocol and after accepting the prompt, our scripts receives the input:


There is a project called Scheme Flooding project which abuses this behavior in order to fingerprint the user depending on the applications installed on the device (e.g., skype://, spotify://...).

Local Web Server

Another mechanisms is to define a local web server. When the Desktop application is running, a local socket is opened waiting for a connection. Websites only have to create a WebSocket to the used port and the communication will take place. This mechanisms is used by Discord, explained more in depth in the following section. In order to find any of this local web server, you may run netstat -tlpn in Linux and netstat -ab in Windows. This command will print opened ports and the application that is listening. One of the key protection used by this method is ‘origin’. Origin Header indicates the scheme, hostname and port of the website creating the websocket. Origin is a restricted header that could not be change it.

Case Study: Discord

In this section I will use Discord (discord documentation) to take a look how this ‘Local Web Server’ mechanisms is implemented. As I mentioned before, we can see in the following screenshot that Discord Desktop App is listening in a specific port for the connection.


If we visit an invitation link on Discord website we would see how a WebSocket is created to “ws://” in which Discord Desktop App is listening.


In the following screenshot you could see all the messages exchanged between the Discord Website and Discord Desktop App.


In order to interact with the websocket created by the Discord Desktop App, I created the following snippet written in python. Notice origin header declaration on the script.

from websocket import create_connection

# Create connection
ws = create_connection("ws://", origin='https://discord.com/')

# cmd = 'DISPATCH'

# Opens the discord app with the a invitation channel

# Return channel info


Local Web Server Discovery

As the reader may be wondering, a website could create scanner of localhost to check for Local Web Servers. One example of this behavior was found in a bank, where the bank scanned computers to try to find any general Remote Access Trojans (RATs). Another example was found in Ebay, where the same behavior was observed.

File System Access API (a.k.a. Native File System API)

I recently discovered the FSA API (article), a powerful Web API that enables websites to interact with the file system and edit files. One notable example of this API in action is in Visual Studio, where users can read and modify files seamlessly. With FSA API, it’s possible to edit your project folder directly from the browser, making it easy to update your local code from the website. By using this API, users can grant access (accepting the popup) to specific files or folders, giving websites the ability to make local modifications to them. Take a look at this demo webpage. Although it’s not a standard API, it has been implemented in browsers like Chromium, Microsoft Edge and Chrome. Partially supported by Opera and Safari.