A while back a user suggested I setup a Mastodon account for Privacy Browser, but at the time I didn’t feel that I needed anything in addition to all the existing communication platforms I was using. But, as is typically the case, over time my perceptions have changed, and I thought it would be nice to have a platform where I could toot about Privacy Browser’s development as I work on features between releases. I expect I will use it similar to the blog posts I write after each release, but more focused on giving insights to each feature I am developing as things are shaping up for a new release.
The account is registered at https://fosstodon.org/@privacybrowser.
Privacy Browser 3.4.1 has been released. It contains an emergency fix for some Android 10 devices that couldn’t access the public directories even when the storage permission was granted. This caused
java.io.IOException:Permission denied errors when the storage permission was granted and the default download location was used on these devices.
Privacy Browser 3.4 has been released. It replaces the use of Android’s built-in download manager with a custom implementation. I didn’t start off trying to do this. Rather, I was building a feature to save a raw URL, and after I had done it I realized I could use it for all download purposes.
As I explain in the design guidelines, I attempt to reuse existing Android elements as much as possible. However, there are a couple of things about Android’s download manager that are unsatisfactory. First, it doesn’t provide a mechanism to specify a proxy. Second, it doesn’t work at all on Android 7.0 when a VPN is enabled. This was the reason that Privacy Browser added an option to download with an external program in version 2.14. With this release, that is no longer needed, so it has been removed.
There are a few things I really like about this implementation. First, the download URL is editable live. This is a feature that isn’t available on any other browser I have used, that, once I realized I could do it, tickled me pink. It isn’t something I would use often, but it doesn’t take any more space than displaying the URL, which is what I was originally going to do, and there are a few times I would like to modify a URL before download.
The file size is retrieved via an HTTP HEAD request whenever the URL is edited. This also indicates if a URL is invalid.
The file name is populated using the new download location option combined with a file name extracted from the URL.
File locations in Android are complex, so they deserve a bit of explanation. The first aspect is that the naming structure has changed over time. In older version of Android, user files were located in
/storage/sdcard. Beginning in Android Lollipop (version 5.0, API 21), the file location was changed to
0 indicates the first profile. If there are other profiles on the phone, they will have different numbers. Note that it is possible that OEM customizations to Android might change these locations.
In addition to the base location, beginning with Android Marshmallow (version 6.0, API 23) an apps ability to read and write files is restricted based on the storage permission. So, any app can read and write to its public directory, which is a subdirectory that includes the app’s ID. For the standard flavor of Privacy Browser it is
/storage/emulated/0/Android/data/com.stoutner.privacybrowser.standard. Any files saved in this directory are deleted by Android if the app is uninstalled. Reading and writing to other public directories requires the storage permission.
Selecting auto for the download location automatically chooses an appropriate directory based on the version of Android and the status of the storage permission. For example, on my phone it selects
/storage/emulated/0/Download if the storage permission has been granted and
/storage/emulated/0/Android/data/com.stoutner.privacybrowser.standard/files if it hasn’t.
If custom is selected, the user can use the separate Download Custom Location preference to specify a location. It should be a user-writable path that doesn’t end in a
/. If a user attempts to download a file to a public directory without enabling the storage permission a notice is displayed at the bottom of the save dialog. Before the file is saved, a dialog is presented requesting that the user grant the storage permission.
As can be seen in the screenshots above, the save dialog now warns if a download will overwrite an existing file. Not only does the save dialog appear when a file is downloaded, but it can also be launched from the context and options menus.
When files have finished there is an option to open them displayed in a snackbar. Except for APK files on Android Oreo (version 8.1, APK 26) or newer, where the REQUEST_INSTALL_PACKAGES permission is required to install an APK.
Tabs are now reloaded when the proxy changes. I added a Bookmarks entry to the options menu so that the bookmarks drawer can be opened on devices using Android 10’s gesture navigation. And the options menu has been reordered to prevent accidental creation of domain settings.
All the translations were updated with this release except for Turkish, which is currently looking for a translator. Under the hood, this release marks the beginning of the migration of the codebase from Java to Kotlin. The About View Source dialog was migrated in this release. Migrations should increase in future releases.
The next version of Privacy Browser will integrate with Android 10’s new day/night theme.
I received an email asking about implementing DNS over HTTPS (DoH) in Privacy Browser. As this is a fairly complex topic, and as there are likely to be other people with the same question, I thought it best to answer with this blog post.
First, a little background about the problem. When DNS (the Domain Name System) was originally designed, people hadn’t really thought through the security and privacy concerns that exist with the internet as we have it today. As such, the original protocol was designed to use UDP (on port 53). This has implications for DDoS amplification attacks, but more importantly for our purposes, it means that DNS traffic is not encrypted. Every time you make a request to view a website in a web browser, your device uses DNS to lookup the IP address of the server. Anyone who is able to listen to the packets leaving your system, like your ISP or the government of the country where you live, can read those DNS requests and, because they are not encrypted, can tell which websites you are visiting.
DoH is designed to address this problem by encrypting DNS traffic and sending it over the HTTPS protocol (which uses TCP instead of UDP and is encrypted). There has been a fair amount of discussion recently about DoH because both Firefox and Chrome have added experimental options to enable it in the browser. This sparked a bit of privacy theater in Europe, and has generally raised the consciousness of users about this topic. The purpose of this post is to explain what DoH is, why it isn’t the best solution to the problem, and why I don’t believe that DNS requests should be handled at the browser level.
- I am a big fan of encrypting everything that moves across the internet. I think running DNS over UDP is a poor idea. Most DNS servers now have the option of running a separate TCP interface, but it still often isn’t the default for either servers or clients. Moving to TCP, even unencrypted TCP (still on port 53), removes the problem of the DDoS amplification attacks, although at the cost of making DNS requests take a little more bandwidth and a little more time.
- But once you switch everything to use TCP for DNS, it is easy to encrypt that DNS traffic. This is known as DNS over TCP (DoT, which runs on port 853). This is the solution that everyone should be aiming for instead of DoH. While DoT is a little more bandwidth hungry than DNS over UDP, DoH is hugely more bandwidth hungry and much slower. This is because DoH must first setup a TCP connection, then it must setup a encrypted TCP session, then it must setup an entire HTTPS session (huge and bulky and overly complex) just to make a DNS request. So, the first part of the answer is that the solution we should be going for is DoT instead of DoH. Beginning with Android 9 Pie, the OS uses DoT by default as long as it is supported by the DNS server.
- Proponents of DoH point to scenarios where some type of ISP or government firewall blocks DoT traffic as a reason to use DoH (which runs on port 443 like normal HTTPS traffic). This is because DoH traffic looks a lot more like normal HTTPS traffic than DoT, so the argument is that it would be harder to block. While it is true that it would be marginally harder to block, it wouldn’t be that difficult to fingerprint it, because it doesn’t actually look that much like normal web browsing traffic. And the IP addresses DoH traffic is heading to are known DNS servers, so it would be fairly east to blacklist those IP addresses if one wanted to. However, the easiest solution to this problem is for all DoT servers to also run a DoH instance, and clients can downgrade from DoT to DoH instead of DNS over UDP if they cannot make a DoT connection.
- But this leads us to the next point, which is that even if all DNS traffic were encrypted with DoT or DoH, it would still be laughably easy for ISPs or governments to know exactly what websites you are visiting, so nothing has been gained from a privacy standpoint. This is why I call it privacy theater. Let me enumerate how they can do this in points 5-7 below.
- When your device makes an HTTPS request to a server for a website, it includes the server name in plaintext at the beginning of the encrypted packet. Let me slow down and say that again: every packet you send to a HTTPS server includes the unencrypted domain name of the server you want to speak with. This is called Server Name Indication (SNI). SNI exists to solve a very real problem: most servers host multiple domains on the same IP address. The server needs to know which certificate to use when it receives the first packet from your device. If it picks the wrong certificate, your device will show an SSL certificate error. So the packet contains the domain in plaintext to facilitate this. Anyone who could read your unencrypted DNS traffic can also read the SNI on your encrypted HTTPS traffic. So, until the SNI problem is fixed, encrypting DNS is meaningless from a privacy standpoint.
- There is some effort being made to encrypt SNI, which is included as an optional component of TLS 1.3. But implementing it is complicated and breaks things. I don’t see anything like this going mainstream until it is implemented as a core part of the TLS protocol and not as an optional component. Which means that we won’t see this until TLS 1.4 at the earliest. And it also means that it won’t work in the real world until the lowest protocol supported by either the server or the client is TLS 1.4, because otherwise the person who it attempting to sniff the domain name could just force a TLS downgrade to 1.3 to get the information. So, at a minimum I put that at 10 years.
- At face value the argument would then be that we should just get rid of SNI and have a 1:1 correlation between domain names and IP addresses. But this would make it even easier for ISPs and governments to track your web traffic, because there are companies who maintain lists of all the domain names on the internet and the IP addresses they are linked with. 95% of the websites you visit can be identified just by the list of IP addresses they use. Without SNI, that would be 100%. And the IP address, like the destination address on a letter, is one part of a packet that can’t be encrypted because otherwise the routers on the internet wouldn’t know where to send it.
- By now it should be apparent that DoT is the solution we are looking for to encrypt DNS, and that from a privacy standpoint it won’t mean anything until we also get encrypted SNI (and that even after both are encrypted it won’t mean very much). Which leads us to the question: what is Privacy Browser going to do about it. The answer is that Privacy Browser is going to do nothing. The reason I say that is not to be flippant, but because DNS is something I feel very strongly should not be dealt with at the browser level. It should be handled entirely by the OS. I have written a bit about this topic in relation to minimizing Privacy Browser’s attack surface. The idea is that, from a security perspective, the browser shouldn’t try to recreate core OS functions, like encryption or DNS. Not only does this introduce another attack surface that is unlikely to be as well audited as the functionality in the core OS, but it also overrides core OS networking settings at an app level, which leads to unexpected behavior, like not updating DNS servers to match the current network or VPN settings.
- This is the part of the conversation where we explain how DoH can actually decrease your privacy instead of merely doing nothing to increase it. During the normal course of the day, you probably connect to multiple DNS servers. When you wake up you probably use the DNS server provided by your home ISP (available over your home Wi-Fi). When you are commuting to work or school, you use your cell phone company’s DNS server. When you arrive at work or school, you connect to the DNS server provided through their Wi-Fi. Each of these DNS servers has a partial list of the the websites you visit throughout the day, but it would be hard to stitch them into a cohesive whole. However, if you use DoH as it is currently implemented in Chrome and Firefox, you would use one DNS server all the time. The two biggest servers that support DoH are run by Google and Cloudflare. Cloudflare promises they won’t do anything bad with that information. But you have no way of verifying that such is the case. And Google, they don’t even make any promises. There is a lot of additional information about this topic on a blog entitled Centralized DoH is bad for privacy, in 2019 and beyond.
- To round out this conversation, we should talk a little bit about DNSSEC (Domain Name System Security Extensions). The whole purpose of DoH is to encrypt traffic in transit while it is going between a DNS server and your device. The actual DNS data on the server is unencrypted, and, as a huge security problem, can be modified by that server before it is sent to you. DNSSEC digitally signs DNS information, so that only the owner of a DNS record can make changes. This verifies that the information DNS servers give you is what it is supposed to be. Because it is quite complex to implement, and because it dramatically increases the size of DNS responses, DNSSEC still hasn’t caught on as the default configuration in most cases. But we hopefully are a little closer than encrypted SNI.
Each of the points above represents almost a gross oversimplification of the complexities of these issues. And, because I am constantly learning more about these issues, the implication is that I do not yet know everything about them. As such, I am perfectly willing to change my mind about any of the above topics if I am presented with a persuasive argument to the contrary. I labeled each section above so that is it easy to reference them by number in the comments if you feel that there is anything I have missed.
- Google Play: $335.27
- Amazon: $136.63
- PayPal: $71.68
- Patreon: $29.61
Total Revenue: $573.19
Google Play revenue comes from selling the standard flavor on Google Play. Amazon revenue comes from selling the standard flavor on the Amazon Appstore. PayPal revenue comes from selling the standard flavor on XDA Labs and also from donations. Google AdMob ad revenue only pays out when the balance exceeds $100.00. Currently the balance is $84.71. CEX.IO, the company I currently use to transfer Bitcoin into USD, requires a minimum transfer amount of $20.00. Currently there is a balance of $14.56.
Google Play reports some fairly detailed statistics about installations. The other distribution methods (F-Droid, XDA-Labs, the Amazon Appstore, and direct downloads from Stoutner.com) either do not track this information or provide only vague information. The screenshot below shows the installs of the standard flavor on active devices, which is defined as the “Number of Android devices that have been active in the past 30 days with the application installed”.
Unsurprisingly, even though there are far more installs of the free flavor, there are fewer active devices. This is because most users either decide they don’t like Privacy Browser, or they switch to using the standard flavor.
Privacy Browser 3.3 has been released. This release took a lot longer than I expected due to a sudden increase in the business of my day job combined with the time constraints of having another child.
There was a major reworking of the way Privacy Browser handles proxies. The underlying code was switched from using an undocumented and unsupported method of tricking WebView to use a proxy to the newly minted AndroidX ProxyController. Not only does this mean that, going forward, we won’t have problems with updates to WebView breaking the proxy, but this also enables the use of SOCKS proxies, which is the preferred way of connecting to Orbot. This removes the problem with HTTP not proxying correctly through recent versions of Orbot.
In previous versions of Privacy Browser, the full name of the app was displayed in the launcher. Depending on the device and the font size, this could cause the displayed name to be truncated.
The launcher display name has now been shortened to one word. This only affects the launcher; the full name will be displayed in most other contexts.
Note that this is not a change I am fully comfortable with, and it might be further revised in the future. On devices where there is enough room for the full name, it is suboptimal to shorten it. There was some discussion about whether
Browser would be the better short name. And, when displayed in the app chooser, both names will be listed. This last problem can be worked around if intents from the launcher enter the app through a different activity than intents from other programs, but doing so would require refactoring the code and could introduce unintended consequences, so I am not sure I want to go that route.
Privacy Browser is now removed from the recent apps list when it is closed via the back button. This brings the back button in line with Clear and Exit from the navigation menu and closing the app with the tab close button.
A bug was fixed that caused on-the-fly settings (the quick settings available in the options menu) to be reset if navigating history (using the forward or back buttons) even if the domain didn’t change. The intended behavior is for these setting to reset only when the domain changes.
Previous version of Privacy Browser had a preset list of font sizes. This has now been changed to a free-form text box that allows any size.
Privacy Browser can now save webpages as MHT archives. A navigation entry to open files has also been added. Privacy Browser can handle MHT files as well as images, text, and anything else that can be displayed in a browser.
There are now separate context menu entries for opening links in the foreground and the background.
Long pressing a link in the bookmarks drawer now opens it in a new tab in the background. This allows users to quickly open multiple bookmarks. Editing of bookmarks is available in the bookmarks activity.
The screen timeout is now explicitly disabled when playing videos in full screen mode. Previously, some videos, like those from YouTube would disable the screen timeout, while others wouldn’t.
HSTS (HTTP Strict Transport Security) is now disabled. The purpose of HSTS is to keep a list of websites that the browser should only connect to via HTTPS instead of HTTP. This has no benefit in Privacy Browser because every website is loaded via HTTPS unless the user explicitly connects via HTTP. And it has the negative downside of creating a de facto list of all the websites a user has visited in the past.
The new Sec-Fetch headers are now displayed in View Source.
The target API was bumped to 29 (Android 10). There is a new French translation provided by Kévin LE FLOHIC. The German translation was updated by Bernhard G. Keller, the Italian translation was updated by Francesco Buratti, and the Spanish translation was updated by Jose A. León. The Russian translation was also updated. I am currently looking for a new Turkish translator.
During 2019, Stoutner received 0 requests from governments or organizations to insert backdoors into Privacy Browser.
During 2019, Stoutner inserted 0 backdoors into Privacy Browser.
During 2019, Stoutner received 0 requests from governments or organizations to weaken the privacy of Privacy Browser.
During 2019, Stoutner has made 0 changes to weaken the privacy of Privacy Browser.
Legal Requests for Information
During 2019, Stoutner receive 0 legal requests for information from government law enforcement organizations. These requests sought information about a total of 0 individuals.
During 2019, Stoutner provided information in response to 0 legal requests for information from government law enforcement organizations. These responses included information about a total of 0 individuals.
Privacy Browser 3.2 has been released. The default search engine and homepage have been changed to Startpage. This is a big enough change that I have written a separate post explaining it.
A bug was fixed that sometimes caused a blocklist to prevent the loading of the main URL. This would result in a blank page and the blocklists showing nothing was blocked.
The Facebook URL modification was updated to include
The close and add tab buttons now display visual feedback when they are touched. As part of this change, the horizontal touch sensitive area of the button was expanded to match the height of the tab. Previously, only the height of the actual icon was touch sensitive. This makes the buttons easier to tap.
New tabs opened from a context menu are now loaded in the background. This better matches the experience on a desktop browser, where new tabs opened from the right-click context menu are typically loaded in the background. It aligns with the workflow of scrolling through a webpage and opening a number of tabs via the context menu to be read at a later time.
It is now possible to save a webpage as an image.
The View Source request headers were updated to match the current format used by WebView. Note that WebView uses
Accept-Encoding:gzip, deflate while View Source must use
Accept-Encoding:gzip due to limitations in HttpUrlConnection.
An updated German translation was provided by Bernhard G. Keller, Italian by Francesco Buratti, and Spanish by Jose A. León. The Russian and Turkish translations were also updated.
Of all the search engines I have seen, I like the structure and philosophy of Searx the most. It is open source software released under the AGPLv3+ license. As far as I can tell it doesn’t perform any user tracking. And it is possible for users to run their own instances.
However, Searx gets its search results from other engines, and is often rate limited by them.
And the default settings of Searx.me do not use the safe search filters, which means they display large amounts of pornography and violence when doing image searches. The result is that, even though I like a lot of things about Searx, it currently doesn’t work well enough to be Privacy Browser’s default homepage and search engine.
As such, it is unlikely I will stay with Startpage in the long term. Perhaps I will contribute to Searx or create my own search engine. Finding a good search engine is a little like looking for a Defense Against the Dark Arts teacher.
The default Tor search engine and homepage is also being changed to DuckDuckGo’s https://3g2upl4pq6kufc4m.onion/.
All of these changes only affect the defaults for new installs. The settings for existing users will not change unless they are manually updated.