Your ‘App’ Could Have Been a Webpage (so I fixed it for you…)

Why is this an “app”?

This summer, the kids’ performing arts school are singing and dancing in a show at Disneyland. We’re all very excited, but my excitement, at least, was muted a little when I was told to install the “Travelbound” app in order to get access to the itinerary, travel arrangements, and accommodation details.

Fuck that noise. This should have been a webpage. Why do you want me to install a(nother) shitty app just to tell me something that could have been a (smaller, faster, more universally-accessible) document?

Screenshot of an Android app showing a summary itinerary: times for a 'ferry crossing', 'check in at your hotel', 'coach driver accommodation', 'disney's hotel cheyenne', and 'welcome gala', each with an attractive but generic photo.
I’m not remotely exaggerating. This app is literally text, images, and links to PDF files, delivered via the Web.

There only seem to be two things that this “app” does, that a webpage might not have, and they’re both anti-features:

  1. It reports tracking data associated with your Google Account back to the developers.
  2. It shows you advertisements (which they call “inspirations”) for other trips organised by the same agency.

Fuck. Everything. About. That.

A webpage would have been so much better. Unlike this app, a webpage can be…1

  • Copy-pastable
  • Printable
  • Saveable
  • Bookmarkable
  • Searchable
  • Usable on virtually any device
  • (Potentially) more-accessible

I’m annoyed enough… that I’m going to “fix” this app. Hold my beer.

Intercepting app traffic

It’s been a while since the last time I reverse-engineered an Android app from its network traffic, so I had to brush-up on the best way. Here’s what I ended up doing.

  1. Created a new virtual device in Android Studio’s Virtual Device Manager.
  2. Tested adb shell was working and used rootAVD to root it: ./rootAVD.sh system-images/android-33/google_apis_playstore/x86_64/ramdisk.img.2
  3. Performed a cold boot, ran Magisk, and tweaked its settings to automatically grant su access to any app that asked.3
Screenshot showing the HTTP Toolkit application running on both MacOS and an emulated Android mobile, with User Trust and System Trust enabled.
All your traffic are belong to me. At this point, it’s just like running Wireshark or TCPdump.
  1. Ran HTTP Toolkit and told it to intercept AVD traffic. It installed a (fake) VPN provider, routing the phone’s traffic through the proxy.4
  2. Installed the Travelbound app from the Play Store.
  3. Configured HTTP Toolkit to proxy only the Travelbound app (more signal, less noise).

With only a couple of minutes experimentation I discovered that the app works by concatenating the username and password5 and using it in a URL of the form:

https://travelbound.api.vamoos.com/api/itineraries/{username}-{password}

This returns a pile of JSON which, with a little interpretation, can be seen to represent all of the content the app “shows”. E.g., there’s:

  • an array containing each leg of the itinerary,
  • an array containing all of the “inspirations” advertisements to show you,
  • a cross-referenced array containing all of the files (images etc.) that are referenced by the other sections, etc.
Firefox showing a JSON document, focussing on a section about 'Your Ferry Crossing' with some accompanying HTML.
They’re clearly producing HTML code anyway… so again, I ask: why isn’t this a webpage?

A little experimentation showed me that the S3 image URLs were being delivered with moderately-short expiration times, so the JSON needs re-fetching periodically even if the content hasn’t been changed.6

Turning it into something better

Now I had everything I needed to make something… better. I wrote a Ruby script that runs on a Cron schedule to pull the latest JSON and use it to build a HTML page.

I chose to have it completely skip over the “inspirations” (“overlayRows” in the data schema) and just list:

  1. the items from the itinerary and
  2. all of the files not referenced by the inspirations nor itinerary, (a lazy way to collate the PDF download links).

Then I hosted the page, protected by a password: the same one my tour group were given in the first place. I included the raw JSON it used in <details> elements so it can be checked if e.g. there are bits of the schema I didn’t see but that might appear later.

Screenshot of a simply-styled web page showing the same information about the ferry, along with a photo from its deck.
My web page isn’t as “pretty” as the app from which it “borrows” its information. But it’s a fraction of the size and gets all of the Web’s standard features for free.

Some people like an “app”, and that’s… fine, I guess. But some apps could have been a webpage. And especially where, like this one, the content they deliver is already written in HTML and delivered over HTTP… they should be a webpage, right?

I can’t understand how we got to this place with “app culture”! Software companies are happy to make their lives harder (and more expensive: deploying to the big app stores isn’t free!), in order to deliver HTML content to fewer people and with fewer features7 than if they just published directly to the Web in the first place!

There are (some) tasks for which an “app” is absolutely the right choice of medium. Travelbound is not one of them.

But at least I (and the rest of our group, whom I’ve shared it with) now get the choice about how we access this content. Either a 43MB app (ballooning to 124MB when it’s finished downloading extra content) with tracking and advertisements… or a 0.05MB web page (with an optional extra 35MB of images) that provides more features and works on more devices. I know which one I’ll be using!

Footnotes

1 And these are just the features that everybody can get behind. The webpage I ultimately ended up making to replace the app also has some user-friendly/developer-hostile features, like the fact that it removes the tracking code and doesn’t show advertisements.

2 You need to root the device in order to force applications that use Certificate Pinning to trust your man-in-the-middle proxy server. Without this, some applications – including the one I wanted to reverse-engineer – will recognise your self-signed TLS certificate as invalid and refuse to communicate.

3 Without changing this setting in Magisk, I found that HTTP Toolkit would request su access but not wait for the response, and go on to run in unprivileged mode before I had a chance to grant it!

4 Owing to Android security considerations I needed to manually install the root CA certificate it installed for me, but the instructions “just worked”.

5 The username and password is shared by an entire tour group. I’m guessing they don’t have a plan for if some credentials get leaked? Or possibly they consider all of the data they hold to be low-sensitivity enough that it doesn’t matter if it does… in which case I return to my original point: why the hell wasn’t it just a webpage in the first place?

6 Or else the images need caching locally, which seems to be what the app does, in the bloatiest possible way.

7 And, often, with worse accessibility. I’ve not audited the accessibility of this app, but there are things about it that suggest that it’d be harder to use using accessibility technologies than my plain, simple Web version.

× × × ×

A Random List of Silly Things I Hate

So apparently now this is a thing, so here I go:

  1. Websites that are just blank pages if the JavaScript doesn’t load from the CDN.1
  2. The misunderstanding that LLMs can somehow be a route to AGI.
  3. Computer systems that say my name is too short or my password is too long.2
  4. People being unwilling to discuss their wild claims later using the lack of discussion as evidence of widespread acceptance.
  5. When people balance the new toilet roll one atop the old one’s tube.3
A nearly-full roll of toilet paper perched atop an empty toilet roll tube on an open-ended spindle.
Come on! It would have been so easy!
  1. Shellfish. Why would you eat that!?
  2. People assuming my interest in computers and technology means I want to talk to them about cryptocurrencies.4
  3. Websites that nag you to install their shitty app. (I know you have an app. I’m choosing to use your website. Stop with the banners!)
  4. People who seem to only be able to drive at one speed.5
  5. The assumption that the fact I’m “sharing” my partner is some kind of compromise on my part; a concession; something that I’d “wish away” if I could. (It’s very much not.)
  6. Brexit.

Wow, that was strangely cathartic.

Footnotes

1 I have a special pet hate for websites that require JavaScript to render their images. Like… we’d had the <img> tag since 1993! Why are you throwing it away and replacing it with something objectively slower, more-brittle, and less-accessible?

2 Or, worse yet, claiming that my long, random password is insecure because it contains my surname. I get that composition-based password rules, while terrible (even when they’re correctly implemented, which they’re often not), are a moderately useful model for people to whom you’d otherwise struggle to explain password complexity. I get that a password composed entirely of personal information about the owner is a bad idea too. But there’s a correct way to do this, and it’s not “ban passwords with forbidden words in them”. Here’s what you should do: first, strip any forbidden words from the password: you might need to make multiple passes. Second, validate the resulting password against your composition rules. If it fails, then yes: the password isn’t good enough. If it passes, then it doesn’t matter that forbidden words were in it: a properly-stored and used password is never made less-secure by the addition of extra information into it!

3 This is the worst of the toilet paper crimes, but there’s a lesser but more-common offence.

4 Also: I’m uninterested in whatever multiplayer shooter game you’re playing, and no I won’t fix your printer.

5 “You were doing 35mph in the 60mph limit, then you were doing 35mph in the 40mph limit, now you’re doing 35mph in the 20mph limit. Argh!”

×

Is it possible to allow sideloading *and* keep users safe?

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

Terence Eden raises some valid points:

I’ve tried to be pragmatic, but there’s something of a dilemma here.

  1. Users should be free to run whatever code they like.
  2. Vulnerable members of society should be protected from scams.

Do we accept that a megacorporation should keep everyone safe at the expense of a few pesky nerds wanting to run some janky code?

Do we say that the right to run free software is more important than granny being protected from scammers?

Do we pour billions into educating users not to click “yes” to every prompt they see?

Do we try and build a super-secure Operating System which, somehow, gives users complete freedom without exposing them to risk?

Do we hope that Google won’t suddenly start extorting developers, users, and society as a whole?

Do we chase down and punish everyone who releases a scam app?

Do we stick an AI on every phone to detect scam apps and refuse to run them if they’re dodgy?

I don’t know the answers to any of these questions and – if I’m honest – I don’t like asking them.

Google’s gradual locking-down of Android bothers me, too. I’ve rooted many of my phones in order to unlock features that I benefit from (as a developer… and as a nerd!), and it’s bugged me on the occasions where I’ve been unable to run had to use complicated workarounds to trick e.g. a bank’s app. Having gone to the effort to root a phone – which remains outside of the reach of most regular users – I’d be happy to accept an appropriate share of the liability if my mistake, y’know, let a scammer steal all of my money.

That’s the risk you take with any device on which you have root, and it’s why we make it hard to the point of being discouraging. Because you can’t just put up a warning and hope that users will read and understand it, because they won’t. They’ll just click whatever button looks like it’ll get them to the next step without even glancing at the danger signs1.

I’m glad to have been increasingly decoupling myself from Google’s ecosystem, because I’ve been burned by it too. Like Terence, I’ve been hit by “real name” policies that discriminate against people with unusual names or who might be at risk of impersonation2. But I’m not convinced that there’s a good alternative for me to running Android on my mobile devices, at the moment: I really enjoyed Maemo back in the day; what’s the status of Sailfish nowadays?

I get that we need to protect people from dangerous scammy apps. But I’d like to think there’s a middle-ground somewhere between Doctrowian “it’s your device, you’re responsible for what runs on it” and the growing Apple/Google thinking of “if we don’t have the targetting coordinates of the developer that wrote the code, our OS won’t let you run it”. I’m ready to concede that user education alone hasn’t worked, but there’s got to be a better solution than this, Google.

Footnotes

1 Incidentally, I don’t blame users for this behaviour. Users have absolutely been conditioned, and continue to be conditioned, to click-without-reading. Cookie and privacy banners with dark patterns, EULAs and legal small print are notoriously (and often unnecessarily) long and convoluted, and companies routinely try to blur the line between “serious thing you should really read but we want you not to” and “trivial thing that you don’t need to read; it’s just a formality that we have to say it”.

2 Right now, my biggest fight with Google has come from the fact that lately, it seems like every time I upload a Three Rings demo video to YouTube it gets deleted under their harassment policy for doxxing people… people like “Alan Fakename” from Somewhereville, “Betty Notaperson” from Otherplace, and their friend “Chris McMadeup” who lives at 123 Imaginary Street. The appeals process turns out to be that you click a button to appeal, but don’t get to provide any further information (e.g. to explain that these are clearly-fake people who won’t mind being doxxed on account of the fact that they don’t exist), and then a few hours later you get an email to say “nah, we’re keeping it deleted”. I almost expect the YouTube version of my recent video demonstrating FreeDeedPoll.org.uk will be next to be targetted by this policy for showing me scribbling the purported signature Sam McRealName, formerly known as Jo Genuine-Person.

Better In Our App

What they say: “This site works better in our app.”

What they mean (optimistic): “We couldn’t be bothered to make a good website.”

What they mean (realistic): “We can track and monetise you better if we can coerce you into installing this.”