<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>security &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/security/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Fri, 17 Apr 2026 06:45:45 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>security &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Responsible Disclosure: Chimoney Android App and KYCaid]]></title>
		<link>https://shkspr.mobi/blog/2026/01/responsible-disclosure-chimoney-android-app-and-kycaid/</link>
					<comments>https://shkspr.mobi/blog/2026/01/responsible-disclosure-chimoney-android-app-and-kycaid/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 14 Jan 2026 12:34:52 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[Responsible Disclosure]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[WebMonetization]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=64849</guid>

					<description><![CDATA[Chimoney is a new &#34;multi-currency wallet&#34; provider. Based out of Canada, it allows users to send money to and from a variety of currencies. It also supports the new Interledger protocol for WebMonetization.  It is, as far as I can tell, unregulated by any financial institution. Nevertheless, it performs a &#34;Know Your Customer&#34; (KYC) check on all new account in order to prevent fraud.  To do this,…]]></description>
										<content:encoded><![CDATA[<p><a href="https://chimoney.app/">Chimoney</a> is a new "multi-currency wallet" provider. Based out of Canada, it allows users to send money to and from a variety of currencies. It also supports the new Interledger protocol for <a href="https://shkspr.mobi/blog/2025/08/security-flaws-in-the-webmonetization-site/">WebMonetization</a>.</p>

<p>It is, as far as I can tell, unregulated by any financial institution. Nevertheless, it performs a "Know Your Customer" (KYC) check on all new account in order to prevent fraud.  To do this, it uses the Ukranian <a href="https://kycaid.com/">KYCaid</a> platform.</p>

<p>So far, so standard. But there's a small problem with how they both integrate.</p>

<p>I installed Chimoney's Android app and attempted to go through KYCaid's verification process. For some reason it hit me with this error message.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/11/error.webp" alt="Screenshot. An error occurred and an email address." width="504" class="aligncenter size-full wp-image-64856">

<p>Well, I'd better click that email and report the problem.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/11/email-protected.webp" alt="Screenshot. The email is protected, but clickable." width="504" height="240" class="aligncenter size-full wp-image-64855">

<p>Oh, that's odd. What happens if I click the protected link?</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/11/Cloudflare.webp" alt="Screenshot. Cloudflare's email protection screen." width="504" height="625" class="aligncenter size-full wp-image-64854">

<p>Huh! I guess I've been taken to Cloudflare's website. What happens if I click on the links on their page?</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/11/discord.webp" alt="Screenshot. Invitation to join Cloudflare's Discord." width="504" height="606" class="aligncenter size-full wp-image-64853">

<p>Looks like I can now visit any site on the web. If Cloudflare has a link to it, I can go there. For example, GitHub.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/11/github.webp" alt="Screenshot. GitHub page still within the Chimoney app." width="504" height="499" class="aligncenter size-full wp-image-64852">

<h2 id="why-is-this-a-problem"><a href="https://shkspr.mobi/blog/2026/01/responsible-disclosure-chimoney-android-app-and-kycaid/#why-is-this-a-problem">Why is this a problem?</a></h2>

<blockquote><p><a href="https://mas.owasp.org/MASTG/knowledge/android/MASVS-PLATFORM/MASTG-KNOW-0018/">MASTG-KNOW-0018: WebViews</a></p>

<p>One of the most important things to do when testing WebViews is to make sure that only trusted content can be loaded in it. Any newly loaded page could be potentially malicious, try to exploit any WebView bindings or try to phish the user. <strong>Unless you're developing a browser app, usually you'd like to restrict the pages being loaded to the domain of your app.</strong> A good practice is to prevent the user from even having the chance to input any URLs inside WebViews (which is the default on Android) nor navigate outside the trusted domains. Even when navigating on trusted domains there's still the risk that the user might encounter and click on other links to untrustworthy content</p>

<p><small>Emphasis added</small></p></blockquote>

<p>A company's app is its sacred space. It shouldn't let anyone penetrate its inner sanctum because it has no control over what that 3rd party shows its customers.</p>

<p>There's nothing stopping an external service displaying a message like "To continue, please transfer 0.1 Bitcon to …"</p>

<p>(Of course, if your KYC provider - or their CDN - decides to turn evil then you probably have bigger problems!)</p>

<p>There are some other problems. It has long been known that <a href="https://discussions.apple.com/thread/7918307?sortBy=rank">people can use in-app browsers to circumvent restrictions</a>.  Some in-app browsers have <a href="https://medium.com/%40youssefhussein212103168/exploiting-insecure-android-webview-with-setallowuniversalaccessfromfileurls-c7f4f7a8db9c">insecure configurations which can be used for exploits</a>.  These sorts of "accidentally open" browsers <a href="https://matan-h.com/google-has-a-secret-browser-hidden-inside-the-settings/">are often considered to be a security vulnerability</a>.</p>

<h2 id="the-fix"><a href="https://shkspr.mobi/blog/2026/01/responsible-disclosure-chimoney-android-app-and-kycaid/#the-fix">The Fix</a></h2>

<p>Ideally, an Android app like this wouldn't use a web view. It should use a KYC provider's API rather than giving them wholesale control of the user experience.</p>

<p>But, suppose you do need a webview. What's the recommendation?</p>

<p>Boring old <a href="https://blog.oversecured.com/Android-security-checklist-webview/#insufficient-url-validation">URl validation</a> using <a href="https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20android.webkit.WebResourceRequest)">Android's <code>shouldOverrideUrlLoading()</code> method</a>.</p>

<p>Essentially, your app restricts what can be seen in the webview and rejects anything else.</p>

<h2 id="risk"><a href="https://shkspr.mobi/blog/2026/01/responsible-disclosure-chimoney-android-app-and-kycaid/#risk">Risk</a></h2>

<p>Look, this is pretty low risk. A user would have to take several deliberate steps to find themselves in a place of danger.</p>

<p>Ultimately, it is "<a href="https://wiki.c2.com/?CodeSmell">Code Smell</a>" - part of the app is giving off a noxious whiff. That's something you cannot afford to have on a money transfer app. If this simple security fix wasn't implemented, what other horrors are lurking in the source code?</p>

<h2 id="contacting-the-company"><a href="https://shkspr.mobi/blog/2026/01/responsible-disclosure-chimoney-android-app-and-kycaid/#contacting-the-company">Contacting the company</a></h2>

<p>There was no <a href="https://securitytxt.org/">security.txt</a> contact - nor anything on their website about reporting security bugs. I reached out to the CEO by email, but didn't hear back.</p>

<p>In desperation, I went on to Discord and asked in their support channel for help.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/11/send-an-email.webp" alt="Screenshot. Someone advising me on who to email." width="504" class="aligncenter size-full wp-image-64857">

<p>Unfortunately, that email address didn't exist.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/11/email-chimoney.webp" alt="Bounce message." width="504" class="aligncenter size-full wp-image-64851">

<p>I also tried contacting KYCaid, but they seemed unable or unwilling to help - and redirected me back to Chimoney.</p>

<p>As it has been over two month since I sent them video of this bug, I'm performing a responsible disclosure to make people aware of the problem.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=64849&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/01/responsible-disclosure-chimoney-android-app-and-kycaid/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Your Password Algorithm Sucks]]></title>
		<link>https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/</link>
					<comments>https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 16 Jun 2025 11:34:07 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[passwords]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=61259</guid>

					<description><![CDATA[There are two sorts of people in the world; those who know they are stupid and those who think they are clever.  Stupid people use a password manager. They know they can&#039;t remember a hundred different passwords and so outsource the thinking to something reasonably secure. I&#039;m a stupid person and am very happy to have BitWarden generate and save fiendishly complex unique passwords which are then…]]></description>
										<content:encoded><![CDATA[<p>There are two sorts of people in the world; those who know they are stupid and those who think they are clever.</p>

<p>Stupid people use a password manager. They know they can't remember a hundred different passwords and so outsource the thinking to something reasonably secure. I'm a stupid person and am very happy to have BitWarden generate and save fiendishly complex unique passwords which are then protected by the app's MFA. Lovely!</p>

<p>But people who think they are clever decide to bypass that and use their own super-secret algorithm.</p>

<p>Every clever person's algorithm boils down to the same thing:</p>

<ol>
<li>Have a single strong main password.</li>
<li>Add to it some information related to the service.</li>
</ol>

<p>For example <code>P@ssw0rd!_facebook</code> and <code>P@ssw0rd!_linkedin</code>. On the surface, that's quite an attractive proposition. You remember one thing and you don't need to trust a password manager.</p>

<p>People who are <em>extra</em> clever use the same algorithm but wrap it in a command-line function which XORs both pieces of data, creates a SHA-512 hash, takes every prime numbered bit, converts to ASCII, and uses <em>that</em> to generate a password. <a href="https://www.youtube.com/watch?v=ls5BFzuxGw4">Smart!</a></p>

<p>Either way, these algorithms <strong>suck!</strong>  Let me explain why.</p>

<h2 id="password-leaking"><a href="https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/#password-leaking">Password Leaking</a></h2>

<p>One day, LinkedIn decides to <a href="https://www.linkedin.com/blog/member/trust-and-safety/protecting-our-members">LeakedOut its users' passwords</a>. Anyone who can see <code>P@ssw0rd!_linkedin</code> can make a pretty good guess at your password for Facebook, banking, dating, and shopping etc.  This means you now need to change <em>every</em> password that you have.</p>

<p>Even if you have used some amazing cryptographic powerhouse of an algorithm, there's still a chance you'll accidentally leak it or get so paranoid that you decide to invalidate it. Now you need to change your password on hundreds of sites.</p>

<h2 id="password-rotation"><a href="https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/#password-rotation">Password Rotation</a></h2>

<p>We all know that it is <a href="https://www.ncsc.gov.uk/collection/passwords/updating-your-approach">a bad idea to ask your users to regularly change their passwords</a> - yet sites often persist in doing so.</p>

<p>How does your algorithm cope with this?</p>

<p>Do you have to remember that it is <code>P@ssw0rd!_facebook_1</code> and <code>P@ssw0rd!_linkedin_23</code>?</p>

<p>Perhaps you'll write down all the suffixes and find a way to store them securely - like, say, a password manager?</p>

<h2 id="password-requirements"><a href="https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/#password-requirements">Password Requirements</a></h2>

<p>One site says "Your password <em>must</em> contain a special character and a number" another says "You can use any special character <em>except</em> % or ?" another refuses to let your password contain two consecutive identical characters, or it <em>must</em> start with a number, or it cannot be longer than 12 characters. Yes, I know password rules like this aren't sensible - but they <em>are</em> common.</p>

<p>How does your algorithm cope with that?</p>

<p>If you manually have to tweak a couple of dozen passwords generated by your algorithm, you are going to tie yourself in knots remembering the arcane requirements for each one.</p>

<h2 id="be-stupid-use-a-password-manager"><a href="https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/#be-stupid-use-a-password-manager">Be Stupid - Use A Password Manager</a></h2>

<p>Humans are stupid<sup id="fnref:not"><a href="https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/#fn:not" class="footnote-ref" title="Not you, of course. You're mummy's extra-special boy who never makes mistakes." role="doc-noteref">0</a></sup>. Humans get tired, forgetful, or sick. Our delicious meaty brains are not optimised to remember long strings of complex information or hundreds of rarely used combinations. Knowing that you know not is a super-power. It allows you to offload things that you don't understand to something more competent.</p>

<p>Pick a password manager. Secure it with a reasonably strong password and multi-factor authentication. Let it do the hard work of remembering.</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:not">
<p>Not you, of course. You're mummy's extra-special boy who never makes mistakes.&nbsp;<a href="https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/#fnref:not" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=61259&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/06/your-password-algorithm-sucks/feed/</wfw:commentRss>
			<slash:comments>18</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[What is a "Cyber Attack"?]]></title>
		<link>https://shkspr.mobi/blog/2025/06/what-is-a-cyber-attack/</link>
					<comments>https://shkspr.mobi/blog/2025/06/what-is-a-cyber-attack/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 05 Jun 2025 11:34:57 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=61135</guid>

					<description><![CDATA[Terminology is hard. Computer terminology is even harder. Humans are animals who just love to classify things. We have a fundamental need in our delicious meaty brains to put things into conceptual buckets.  This, I think, leads to some unfortunate consequences when our categories don&#039;t match up with other people&#039;s categories.  For example, take this news story and this journalist&#039;s response to…]]></description>
										<content:encoded><![CDATA[<p>Terminology is hard. <em>Computer</em> terminology is even harder. Humans are animals who just <em>love</em> to classify things. We have a fundamental need in our delicious meaty brains to put things into conceptual buckets.  This, I think, leads to some unfortunate consequences when our categories don't match up with other people's categories.</p>

<p>For example, take this news story and this journalist's response to it:</p>

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:4uyo4p7dnrovo2q2d4tya2vf/app.bsky.feed.post/3lqtp77uwyk2p" data-bluesky-cid="bafyreihxb766fu5it5ilrsjckvnpkakf5fcmw4kvvxbooiyb5vrzus47ay"><p lang="en">100,000 taxpayers will be told shortly that their @HMRCgovuk accounts have been hacked and £47m stolen by thieves claiming fake tax repayments bit.ly/4mSDrMs extraordinary admission to MPs from top official who claims it wasn’t a cyberattack!</p>— <a href="https://bsky.app/profile/did:plc:4uyo4p7dnrovo2q2d4tya2vf?ref_src=embed">Paul Lewis (@paullewismoney.bsky.social)</a> <a href="https://bsky.app/profile/did:plc:4uyo4p7dnrovo2q2d4tya2vf/post/3lqtp77uwyk2p?ref_src=embed">2025-06-05T06:33:24.098Z</a></blockquote>

<script async="" src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>

<p>I think it is pretty reasonable to say that having 100,000 accounts breached using a computer <em>is</em> a "cyberattack". So how do the UK tax authorities square that circle?  Angela MacDonald, the deputy chief executive of HMRC, said:</p>

<blockquote><p>MacDonald stressed that the breach was “not a cyberattack, we have not been hacked, we have not had data extracted from us”.</p>

<p>She later said: “The ability for somebody to breach your systems and to extract data, to hold you to ransomware and all of those things, that is a cyberattack. That is not what has happened here.”</p>

<p>…</p>

<p>“This was not a cyberattack — it involved criminals using personal information from phishing activity or data obtained elsewhere to try to claim money from HMRC. We’re writing to those customers affected to reassure them we’ve secured their accounts and that they haven’t lost any money.”</p>

<p><a href="https://www.thetimes.com/article/24aef47e-978e-400d-bf6c-b6932e737cbc?shareToken=9d7a9b9250df0ea4ba9b3426d24492f9">Criminals access 100,000 people’s tax records</a></p></blockquote>

<p>Ah. I think that's pretty reasonable. Well, up to a point.</p>

<p>If you set your HMRC password to be "password" and someone guesses that - it is <em>you</em> who has been attacked; not the online service.</p>

<p>Here's what has probably happened in this case.</p>

<ul>
<li>You signed up to an online service.</li>
<li>You used your regular email and password.</li>
<li>The service <a href="https://haveibeenpwned.com/Breach/LinkedIn">got hacked and leaks everyone's details</a>.</li>
<li>A criminal went <a href="https://owasp.org/www-community/attacks/Credential_stuffing">credential stuffing</a> and tried all the usernames and password on lots of sites.</li>
<li>One of those sites was HMRC and the criminal started filling their pockets.</li>
</ul>

<p>Who is being "cyberattacked" here?  HMRC say that no individual lost any money - although I suspect people will possibly feel various administrative repercussions. It is hard to feel that the individual is the victim.</p>

<p>HMRC didn't have any malware or ransomware installed. None of their computers were misused. Vast globs of data were not exfiltrated.</p>

<p>But were HMRC's digital defences breached? <em>Maybe…</em></p>

<p>Let's suppose that the cybercriminal who did this was an idiot. Here's what they <em>might</em> have done:</p>

<ul>
<li>Used a single IP address</li>
<li>From a "dangerous" country</li>
<li>Trying 1,000 passwords per second</li>
</ul>

<p>At which point, HMRC's systems should have started flashing red, sirens wailing, and countermeasures deployed. Any one or combination of the above should have been enough to trigger a "something fishy is going on here" alert. I think that scenario would be fair to describe it as <em>looking like</em> a cyberattack - although, depending on their risk tolerance it might be described as "<a href="https://knowyourmeme.com/memes/anatoly-dyatlov">not great, not terrible</a>".</p>

<p>But if the attacker was smart, they'd have rotated through thousands of UK-based IP addresses and kept their stuffing volume below the noise threshold.  Whereupon their attempts would likely have gone unnoticed.</p>

<p>Is a small and subtle attack still an attack? Yes.</p>

<h2 id="was-this-a-cyberattack"><a href="https://shkspr.mobi/blog/2025/06/what-is-a-cyber-attack/#was-this-a-cyberattack">Was this a cyberattack?</a></h2>

<p>I don't think it matters. Sorting things into predefined buckets is often just a way to bypass responsibility and accountability. Concentrating on the name of the thing rather than the thing itself doesn't help victims and doesn't prevent the incident from happening again.</p>

<p>Every counter-measure which HMRC could deploy will negatively affect legitimate users. Getting bombarded with emails saying "did you just try to log in?" is an annoyance, mandating 2FA excludes less technical users, banning suspicious IP addresses inevitably leads to false positives, rate-limits hit legitimate users. And, ultimately, (whisper it) users bear <em>some</em> of the blame for their poor password practices.</p>

<p>I'm sure HMRC will tighten up their monitoring, I'm sure some individuals will have better password hygiene, and I'm sure criminals will find a way to bypass both.</p>

<p>As ontology is difficult, I'll leave you with this instructional video.</p>

<iframe title="What Makes Soup, Soup? | Short Stuff | Comedy" width="620" height="349" src="https://www.youtube.com/embed/Y1HVTNxwt7w?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=61135&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/06/what-is-a-cyber-attack/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[That's Not How A SIM Swap Attack Works]]></title>
		<link>https://shkspr.mobi/blog/2025/04/thats-not-how-a-sim-swap-attack-works/</link>
					<comments>https://shkspr.mobi/blog/2025/04/thats-not-how-a-sim-swap-attack-works/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 17 Apr 2025 11:34:54 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[sim]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=59603</guid>

					<description><![CDATA[There&#039;s a disturbing article in The Guardian about a person who was on the receiving end of a successful cybersecurity attack.  EE texted to say they had processed my sim activation request, and the new sim would be active in 24 hours. I was told to contact them if I hadn’t requested this. I hadn’t, so I did so immediately. Twenty-four hours later, my mobile stopped working and money was wit…]]></description>
										<content:encoded><![CDATA[<p>There's <a href="https://www.theguardian.com/money/2025/apr/15/ee-was-unapologetic-after-i-tried-to-stop-a-sim-swap">a disturbing article in The Guardian</a> about a person who was on the receiving end of a successful cybersecurity attack.</p>

<blockquote><p>EE texted to say they had processed my sim activation request, and the new sim would be active in 24 hours. I was told to contact them if I hadn’t requested this. I hadn’t, so I did so immediately. Twenty-four hours later, my mobile stopped working and money was withdrawn from my bank account.
</p><p><strong>With their alien sim, the ­fraudster infiltrated my handset and stole details for every account I had.</strong> Passwords and logins had been changed for my finance, retail and some social media accounts. </p></blockquote>

<p>(Emphasis added.)</p>

<p>I realise it is in the consumer rights section of the newspaper, not the technology section, and I dare-say some editorialising has gone on, but that's <em>nonsense</em>.</p>

<p>Here's how a SIM swap works.</p>

<ol>
<li>Attacker convinces your phone company to reassign your telephone number to a new SIM.</li>
<li>Attacker goes to a website where you have an account, and initiates a password reset.</li>
<li>Website sends a verification code to your phone number, which is now in the hands of the attacker.</li>
<li>Attacker supplies verification code and gets into your account.</li>
</ol>

<p>Do you notice the missing step there?</p>

<p>At no point does the attacker "infiltrate" your handset. Your handset is still in your possession. The SIM is dead, but that doesn't give the attacker access to the phone itself. There is simply <strong>no way</strong> for someone to put a new SIM into their phone and automatically get access to your device.</p>

<p>Try it now. Take your SIM out of your phone and put it into a new one. Do all of your apps suddenly appear? Are your usernames and passwords visible to you? No.</p>

<p>There are ways to transfer your data from an <a href="https://support.apple.com/en-gb/HT210216">iPhone</a> or <a href="https://support.google.com/android/answer/13761358?hl=en">Android</a> - but they require a lot more work than swapping a SIM.</p>

<p>So how did the attacker know which websites to target and what username to use?</p>

<h2 id="what-probably-happened"><a href="https://shkspr.mobi/blog/2025/04/thats-not-how-a-sim-swap-attack-works/#what-probably-happened">What (Probably) Happened</a></h2>

<p>Let's assume the person in the article didn't have malware on their device and hadn't handed over all their details to a cold caller.</p>

<p>The most obvious answer is that the attacker <em>already</em> knew the victim's email address. Maybe the victim gave out their phone number and email to some dodgy site, or they're listed on their contact page, or something like that.</p>

<p>The attacker now has two routes.</p>

<p>First is "hit and hope". They try the email address on hundreds of popular sites' password reset page until they get a match. That's time-consuming given the vast volume of websites.</p>

<p>Second is targetting your email. If the attacker can get into your email, they can see which sites you use, who your bank is, and where you shop.  They can target those specific sites, perform a password reset, and get your details.</p>

<p>I strongly suspect it is the latter which has happened. The swapped SIM was used to reset the victim's email password. Once in the email, all the accounts were easily found. At no point was the handset broken into.</p>

<h2 id="what-can-i-do-to-protect-myself"><a href="https://shkspr.mobi/blog/2025/04/thats-not-how-a-sim-swap-attack-works/#what-can-i-do-to-protect-myself">What can I do to protect myself?</a></h2>

<p>It is important to realise that <a href="https://shkspr.mobi/blog/2024/03/theres-nothing-you-can-do-to-prevent-a-sim-swap-attack/">there's nothing you can do to prevent a SIM-swap attack</a>! Your phone company is probably incompetent and their staff can easily be bribed. You do not control your phone number. If you get hit by a SIM swap, it almost certainly isn't your fault.</p>

<p>So here are some practical steps anyone can take to reduce the likelihood and effectiveness of this class of attack:</p>

<ul>
<li>Remember that <a href="https://shkspr.mobi/blog/2020/03/its-ok-to-lie-to-wifi-providers/">it's OK to lie to WiFi providers</a> and other people who ask for your details. You don't need to give someone your email for a receipt. You don't need to hand over your real phone number on a survey. This is the most important thing you can do.</li>
<li>Try to hack yourself. How easy would it be for an attacker who had stolen your phone number to also steal your email address? Open up a private browser window and try to reset your email password. What do you notice? How could you secure yourself better?</li>
<li>Don't use SMS for two-factor authentication. If you are given a choice of 2FA methods, use a dedicated app. If the only option you're given is SMS - contact the company to complain, or leave for a different provider.</li>
<li>Don't rely on a <a href="https://bsky.app/profile/scientits.bsky.social/post/3lmz2zaxkf22k">setting a PIN for your SIM</a>. The PIN only protects the physical SIM from being moved to a new device; it does nothing to stop your number being ported to a new SIM.</li>
<li>Finally, realise that professional criminals only need to be lucky once but you need to be lucky all the time.</li>
</ul>

<p>Stay safe out there.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=59603&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/04/thats-not-how-a-sim-swap-attack-works/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[FobCam '25 - All my MFA tokens on one page]]></title>
		<link>https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/</link>
					<comments>https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 11 Apr 2025 11:34:34 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[Satire (Probably)]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=59334</guid>

					<description><![CDATA[Some ideas are timeless. Back in 2004, an anonymous genius set up &#34;FobCam&#34;. Tired of having to carry around an RSA SecurID token everywhere, our hero simply left the fob at home with an early webcam pointing at it. And then left the page open for all to see.    Security expert Bruce Schneier approved of this trade-off between security and usability - saying what we&#039;re all thinking:  Here’s a guy w…]]></description>
										<content:encoded><![CDATA[<p>Some ideas are timeless. Back in 2004, an anonymous genius set up "<a href="https://web.archive.org/web/20060215092922/http://fob.webhop.net/">FobCam</a>". Tired of having to carry around an RSA SecurID token everywhere, our hero simply left the fob at home with an early webcam pointing at it. And then left the page open for all to see.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/04/FobCam-fs8.png" alt="Website with a grainy webcam photo of a SecurID fob." width="512" class="aligncenter size-full wp-image-59341">

<p>Security expert Bruce Schneier approved<sup id="fnref:🫠"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:🫠" class="footnote-ref" title="🫠" role="doc-noteref">0</a></sup> of this trade-off between security and usability - saying what we're all thinking:</p>

<blockquote><p>Here’s a guy who has a webcam pointing at his SecurID token, so he doesn’t have to remember to carry it around. Here’s the strange thing: unless you know who the webpage belongs to, it’s still good security.
<a href="https://www.schneier.com/crypto-gram/archives/2004/0815.html#:~:text=webcam">Crypto-Gram - August 15, 2004</a></p></blockquote>

<p>Nowadays, we have to carry dozens of these tokens with us. Although, unlike the poor schmucks of 2004, we have an app for that. But I don't always have access to my phone. Sometimes I'm in a secure location where I can't access my electronics. Sometimes my phone gets stolen, and I need to log into Facebook to whinge about it. Sometimes I just can't be bothered to remember which fingerprint unlocks my phone<sup id="fnref:🖕"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:🖕" class="footnote-ref" title="🖕" role="doc-noteref">1</a></sup>.</p>

<p>Using the <a href="https://shkspr.mobi/blog/2025/03/using-the-web-crypto-api-to-generate-totp-codes-in-javascript-without-3rd-party-libraries/">Web Crypto API, it is easy to Generate TOTP Codes in JavaScript directly in the browser</a>.  So here are all my important MFA tokens. If I ever need to log in somewhere, I can just visit this page and grab the code I need<sup id="fnref:🙃"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:🙃" class="footnote-ref" title="🙃" role="doc-noteref">2</a></sup>.</p>

<h2 id="all-my-important-codes"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#all-my-important-codes">All My Important Codes</a></h2>

<table>
<tbody><tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/github.svg" width="100" title="Github"></td><td id="otp0"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/bitwarden.svg" width="100" title="BitWarden"></td><td id="otp1"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/apple.svg" width="100" title="Apple"></td><td id="otp2"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/ebay.svg" width="100" title="ebay"></td><td id="otp3"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/amazon.svg" width="100" title="Amazon"></td><td id="otp4"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/npm.svg" width="100" title="NPM"></td><td id="otp5"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/paypal.svg" width="100" title="PayPal"></td><td id="otp6"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/facebook.svg" width="100" title="Facebook"></td><td id="otp7"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/zoom.svg" width="100" title="Zoom"></td><td id="otp8"></td></tr>
<tr><td><img src="https://edent.github.io/SuperTinyIcons/images/svg/linkedin.svg" width="100" title="LinkedIn"></td><td id="otp9"></td></tr>
</tbody></table>

<h2 id="what-the-actual-fuck"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#what-the-actual-fuck">What The <em>Actual</em> Fuck?</a></h2>

<p>A 2007 paper called <a href="https://cups.cs.cmu.edu/soups/2007/proceedings/p64_bauer.pdf">Lessons learned from the deployment of a smartphone-based access-control system</a> looked at whether fobs met the needs of their users:</p>

<blockquote><p>However, we observed that end users tend to be most concerned about how convenient [fobs] are to use. There are many examples of end users of widely used access-control technologies readily sacrificing security for convenience. For example, it is well known that users often write their passwords on post-it notes and stick them to their computer monitors. Other users are more inventive: a good example is the user who pointed a webcam at his fob and published the image online so he would not have to carry the fob around.</p></blockquote>

<p>As for Schneier's suggestion that anonymity added protection, a contemporary report noted that <a href="https://www.schneier.com/crypto-gram/archives/2004/0915.html#:~:text=Fobcam">the owner of the FobCam site was trivial to identify</a><sup id="fnref:dox"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:dox" class="footnote-ref" title="The neologism &quot;doxing&quot; hadn't yet been invented." role="doc-noteref">3</a></sup>.</p>

<p>Every security system involves trade-offs. I have a password manager, but with over a thousand passwords in it, the process of navigating and maintaining becomes a burden. <a href="https://shkspr.mobi/blog/2020/08/i-have-4-2fa-coverage/">The number of 2FA tokens I have is also rising</a>. All of these security factors need backing up. Those back-ups need testing<sup id="fnref:back"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:back" class="footnote-ref" title="As was written by the prophets: &quot;Only wimps use tape backup: real men just upload their important stuff on ftp, and let the rest of the world mirror it&quot;" role="doc-noteref">4</a></sup>. It is an endless cycle of drudgery.</p>

<p>What's a rational user supposed to do<sup id="fnref:rat"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:rat" class="footnote-ref" title="I in no way imply that I am rational." role="doc-noteref">5</a></sup>? I suppose I could buy a couple of hardware keys, keep one in an off-site location, but somehow keep both in sync, and hope that a firmware-update doesn't brick them.</p>

<p>Should I just upload all of my passwords, tokens, secrets, recovery codes, passkeys, and biometrics<sup id="fnref:bro"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:bro" class="footnote-ref" title="Just one more factor, that'll fix security, just gotta add one more factor bro." role="doc-noteref">6</a></sup> into the cloud?</p>

<p>The cloud is just someone else's computer. This website is <em>my</em> computer. So I'm going to upload all my factors here. What's the worst that could happen<sup id="fnref:🤯"><a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fn:🤯" class="footnote-ref" title="This is left as an exercise for the reader." role="doc-noteref">7</a></sup>.</p>

<script>async function generateTOTP( 
    base32Secret = "QWERTY", 
    interval = 30, 
    length = 6, 
    algorithm = "SHA-1" ) {
    
    //  Decode the secret
    //  The Base32 Alphabet is specified at https://datatracker.ietf.org/doc/html/rfc4648#section-6
    const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
    let bits = "";
    
    //  Some secrets are padded with the `=` character. Remove padding.
    //  https://datatracker.ietf.org/doc/html/rfc3548#section-2.2
    base32Secret = base32Secret.replace( /=+$/, "" )

    //  Loop through the trimmed secret
    for ( let char of base32Secret ) {
        //  Ensure the secret's characters are upper case
        const value = alphabet.indexOf( char.toUpperCase() );

        //  If the character doesn't appear in the alphabet.
        if (value === -1) throw new Error( "Invalid Base32 character" );
        
        //  Binary representation of where the character is in the alphabet
        bits += value.toString( 2 ).padStart( 5, "0" );
    }

    //  Turn the bits into bytes
    let bytes = [];
    //  Loop through the bits, eight at a time
    for ( let i = 0; i < bits.length; i += 8 ) {
        if ( bits.length - i >= 8 ) {
                bytes.push( parseInt( bits.substring( i, i + 8 ), 2 ) );
        }
    }

    //  Turn those bytes into an array
    const decodedSecret = new Uint8Array( bytes );
    
    //  Number of seconds since Unix Epoch
    const timeStamp = Date.now() / 1000; 

    //  Number of intervals since Unix Epoch
    //  https://datatracker.ietf.org/doc/html/rfc6238#section-4.2
    const timeCounter = Math.floor( timeStamp / interval );

    //  Number of intervals in hexadecimal
    const timeHex = timeCounter.toString( 16 );

    //  Left-Pad with 0
    const paddedHex = timeHex.padStart( 16, "0" );

    //  Set up a buffer to hold the data
    const timeBuffer = new ArrayBuffer( 8 );
    const timeView   = new DataView( timeBuffer );
    
    //  Take the hex string, split it into 2-character chunks 
    const timeBytes = paddedHex.match( /.{1,2}/g ).map(
        //  Convert to bytes
        byte => parseInt( byte, 16 )
    );

    //  Write each byte into timeBuffer.
    for ( let i = 0; i < 8; i++ ) {
         timeView.setUint8(i, timeBytes[i]);
    }
    
    //  Use Web Crypto API to generate the HMAC key
    //  https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey
    const key = await crypto.subtle.importKey(
        "raw",
        decodedSecret,
        { 
            name: "HMAC", 
            hash: algorithm 
        },
        false,
        ["sign"]
    );

    //  Sign the timeBuffer with the generated HMAC key
    //  https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/sign
    const signature = await crypto.subtle.sign( "HMAC", key, timeBuffer );
    
    //  Get HMAC as bytes
    const hmac = new Uint8Array( signature );

    //  https://datatracker.ietf.org/doc/html/rfc4226#section-5.4
    //  Use the last byte to generate the offset
    const offset = hmac[ hmac.length - 1 ] & 0x0f;

    //  Bit Twiddling operations
    const binaryCode = 
        ( ( hmac[ offset     ] & 0x7f ) << 24 ) |
        ( ( hmac[ offset + 1 ] & 0xff ) << 16 ) |
        ( ( hmac[ offset + 2 ] & 0xff ) <<  8 ) |
        ( ( hmac[ offset + 3 ] & 0xff ) );

    //  Turn the binary code into a decimal string
    const stringOTP = binaryCode.toString();

    //  Count backwards from the last character for the length of the code
    let otp = stringOTP.slice( -length)

    //  Pad with 0 to full length
    otp = otp.padStart( length, "0" );

    //  All done!
    return otp;
}
//  Placeholder for OTPs
var otps = [];

//  Do you really think these are my genuine codes? At least one of them is. But which?
var otpData = 
    [
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "IPT5TRO7VFK66M6SHUJ7XZNM2U6IZZ4L"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "EXGKOX26KMDSTL6KM3BYMPXXDDKNQEYM"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "UGVGXRQFHY62OWI5SGSTZLIQUMXTTVME"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "Y4UHVLFIZZZK7ENDYZ4O3ZZI2QWUJI37"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "Z2KDRL4ELOCDALT3OSNUK65Z2KPOWGUL"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "OWRQKSCBLRUZXYXLXIDATUK6UTG3CPVV"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "XQLSEGNYPBMVK35ZMDTVN5GFOZB46WJJ"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "M3KVKGRB2WVWOZXN437EMF2MS36G75IR",
            "Comment"    : "This is genuinely my Twitter TOTP secret - although the period should be 30. But what's the password? There's a clue somewhere in this source code!",
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "3EMER2B6YXIFMMAY5XBYLNF4NSEGJXCU"
        },
        {
            "algorithm"  : "SHA1",
            "digits"     :  6,
            "period"     : 15,
            "secret"     : "ZML6O5K7QSVFE5QIWNFFT7BIZI7PBHNV"
        }
    ]


var i = 0;

otpData.forEach (
    item => {
        //  Add OTP
        otps[i] = item;
        i++;
    }
);

//  Generate TOTP codes
async function update() {
    for (var i = 0; i < otps.length; i++){
            //  Convert the algorithm
            //  The algorithm name is different for TOTP and Web Crypto(!)
            algorithm = "SHA-1";
            
            document.getElementById( "otp" + i).innerHTML = await generateTOTP( 
                otps[i]["secret"], 
                otps[i]["period"], 
                otps[i]["digits"], 
                algorithm
            );
    }
}
//  Update every second
setInterval(update, 1000);

</script>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:🫠">
<p>🫠&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:🫠" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:🖕">
<p>🖕&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:🖕" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:🙃">
<p>🙃&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:🙃" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:dox">
<p>The neologism "doxing" hadn't yet been invented.&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:dox" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:back">
<p>As was written by the prophets: "<a href="https://lkml.iu.edu/hypermail/linux/kernel/9607.2/0292.html">Only wimps use tape backup: <em>real</em> men just upload their important stuff on ftp, and let the rest of the world mirror it</a>"&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:back" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:rat">
<p>I in no way imply that I am rational.&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:rat" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:bro">
<p>Just one more factor, that'll fix security, just gotta add one more factor bro.&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:bro" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:🤯">
<p>This is left as an exercise for the reader.&nbsp;<a href="https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/#fnref:🤯" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=59334&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/04/fobcam-25-all-my-mfa-tokens-on-one-page/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[The least secure TOTP code possible]]></title>
		<link>https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/</link>
					<comments>https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 24 Feb 2025 12:34:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[rant]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[totp]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=58360</guid>

					<description><![CDATA[If you use Multi-Factor Authentication, you&#039;ll be well used to scanning in QR codes which allow you to share a secret code with a website. These are known as Time-based One Time Passwords (TOTP).  As I&#039;ve moaned about before, TOTP has never been properly standardised. It&#039;s a mish-mash of half-finished proposals with no active development, no test suite, and no-one looking after it. Which is…]]></description>
										<content:encoded><![CDATA[<p>If you use Multi-Factor Authentication, you'll be well used to scanning in QR codes which allow you to share a secret code with a website. These are known as Time-based One Time Passwords (TOTP<sup id="fnref:pop"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fn:pop" class="footnote-ref" title="Yes! Just like Top of The Pops! The famous British TV show! Wow! I bet you're the first person in history to make that joke! Have a biscuit." role="doc-noteref">0</a></sup>).</p>

<p>As I've moaned about before, <a href="https://shkspr.mobi/blog/2022/05/why-is-there-no-formal-specification-for-otpauth-urls/">TOTP has never been properly standardised</a>. It's a mish-mash of half-finished proposals with no active development, no test suite, and no-one looking after it. Which is <em>exactly</em> what you want from a security specification, right?!</p>

<p>So let's try to find some edge-cases and see where things break down.</p>

<h2 id="one-punch-man"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#one-punch-man">One Punch Man</a></h2>

<p>This is possibly the <em>least</em> secure TOTP code I could create. Scan it and see whether your app will accept it.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/02/ultra.png" alt="QR code." width="350" height="350" class="aligncenter size-full wp-image-58361">

<p>What makes it so crap?  There are three things which protect you when using TOTP.</p>

<ol>
<li>The shared secret. In this case, it is <code>abcdefghijklmno</code> - OK, that's not the easiest thing to guess, but it isn't exactly complex.</li>
<li>The amount time the code is valid for before changing. Most TOTP codes last 30 seconds, this lasts 120.</li>
<li>The length of the code. Most codes are 6 digits long. In theory, the spec allows 8 digits. This is 1. Yup. A single digit.</li>
</ol>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/02/totp-bitwarden-fs8.png" alt="BitWarden showing a single digit for 119 seconds." width="504" height="378" class="aligncenter size-full wp-image-58380">

<p>If you were thick enough to use this<sup id="fnref:noooooo"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fn:noooooo" class="footnote-ref" title="Please don't!" role="doc-noteref">1</a></sup>, an attacker would have a 1/10 chance of simply <em>guessing</em> your MFA code. If they saw you type it in, they'd have a couple of minutes in which to reuse it.</p>

<p>Can modern TOTP apps add this code? I <a href="https://mastodon.social/@Edent/114032994415288253">crowdsourced the answers</a>.</p>

<p>Surprisingly, a few apps accept it! Aegis, 1password, and BitWarden will happily store it and show you a 1 digit code for 120 seconds.</p>

<p>A few reject it. Authy, Google Authenticator, and OpenOTP claim the code is broken and won't add it.</p>

<p>But, weirdly, a few <em>interpret it incorrectly!</em> The native iOS app, Microsoft Authenticator, and KeepassXC store the code, but treat it as a 6 digit, 30 second code.</p>

<h2 id="do-the-right-thing"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#do-the-right-thing">Do The Right Thing</a></h2>

<p>What is the right thing to do in this case? The code is outside the (very loosely defined) specification. <a href="https://lawsofux.com/postels-law/">Postel's Law</a> tells us that we should try our best to interpret malformed data - which is what Aegis and BitWarden do.</p>

<p>But, in a security context, that could be dangerous. Perhaps rejecting a dodgy code makes more sense?</p>

<p>What is absolutely daft<sup id="fnref:stronger"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fn:stronger" class="footnote-ref" title="I wanted to use the words &quot;utterly fucking stupid&quot; but I felt it was unprofessional." role="doc-noteref">2</a></sup> is ignoring the bits of the code you don't like and substituting your own data! Luckily, in a normal TOTP enrolment, the user has to enter a code to prove they've saved it correctly. Entering in a 6 digit code where only 1 is expected is likely to fail.</p>

<h2 id="were-only-human"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#were-only-human">We're Only Human</a></h2>

<p>A one-digit code is ridiculous. But what about the other extreme? Would a 128-digit code be acceptable? For a human, no; it would be impossible to type in correctly. For a machine with a shared secret, it possibly makes sense.</p>

<p>On a high-latency connection or with users who may have mobility difficulties, a multi-minute timeframe could be sensible. For something of extremely high security, sub-30 seconds may be necessary.</p>

<p>But, again, the specification hasn't evolved to meet user needs. It is stagnant and decaying.</p>

<h2 id="whats-next"><a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#whats-next">What's Next?</a></h2>

<p>There's an <a href="https://www.ietf.org/archive/id/draft-linuxgemini-otpauth-uri-00.html">draft proposal to tighten up to TOTP spec</a> which has expired.</p>

<p>It would be nice if the major security players came together to work out a <em>formal</em> and <em>complete</em> specification for this vital piece of security architecture. But I bet it won't ever happen.</p>

<ul>
<li><a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format">Google have archived their work on authentication standards</a>.</li>
<li><a href="https://developer.apple.com/documentation/authenticationservices/securing-logins-with-icloud-keychain-verification-codes#3795996">Apple points to Google's outdated spec</a>.</li>
<li><a href="https://docs.yubico.com/yesdk/users-manual/application-oath/uri-string-format.html">YubiCo's incompatible spec hasn't been updated in 4 years</a>.</li>
<li>The <a href="https://www.iana.org/assignments/uri-schemes/prov/otpauth">otpauth registration</a> lists a consortium called <a href="https://openauthentication.org">https://openauthentication.org</a> who don't appear to published anything in a decade.</li>
<li>Microsoft don't have any documentation whatsoever.</li>
</ul>

<p>So there you have it. We're told to rely on TOTP for our MFA - yet the major apps all disagree on how the standard should be implemented. This is a recipe for an eventual security disaster.</p>

<p>How do we fix it?</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:pop">
<p>Yes! Just like Top of The Pops! The famous British TV show! Wow! I bet you're the first person in history to make that joke! Have a biscuit.&nbsp;<a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fnref:pop" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:noooooo">
<p>Please don't!&nbsp;<a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fnref:noooooo" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:stronger">
<p>I wanted to use the words "utterly fucking stupid" but I felt it was unprofessional.&nbsp;<a href="https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/#fnref:stronger" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=58360&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/02/the-least-secure-totp-code-possible/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[What's the best way to protect banking apps on Android?]]></title>
		<link>https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/</link>
					<comments>https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 29 Dec 2024 12:34:26 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[banking]]></category>
		<category><![CDATA[GrapheneOS]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=55137</guid>

					<description><![CDATA[Lots of people using banking apps on their Android phones.  They&#039;re a convenient way to check your balance, transfer money to people, and get alerts about fraudulent transactions. But, like anything related to money, they can be abused.  Nowadays, thieves are not only snatching phones, but forcing their owners to transfer money to the thieves. This is not an isolated incident.  How can you…]]></description>
										<content:encoded><![CDATA[<p>Lots of people using banking apps on their Android phones<sup id="fnref:smug"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fn:smug" class="footnote-ref" title="&quot;Not me,&quot; you say smugly. &quot;I am far superior to the sheeple. If I want to connect to my bank, I just SSH in to a bespoke firewalled box that runs a disposable Docker image which connect to TOR.&quot; You…" role="doc-noteref">0</a></sup>.  They're a convenient way to check your balance, transfer money to people, and get alerts about fraudulent transactions. But, like anything related to money, they can be abused.</p>

<p>Nowadays, thieves are not only snatching phones, but <a href="https://www.reddit.com/r/UKPersonalFinance/comments/12bl2rf/forced_to_transfer_money_to_muggers/">forcing their owners to transfer money to the thieves</a>. This is not an isolated incident<sup id="fnref:see"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fn:see" class="footnote-ref" title="See also Bank and phone lessons learned after a robbery and I was robbed and forced to transfer money from my banking app." role="doc-noteref">1</a></sup>.</p>

<p>How can you protect yourself from such a situation<sup id="fnref:state"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fn:state" class="footnote-ref" title="Here we're mostly concerned with street theft. If you are the target of state-sponsored violence, or the police are searching your phone, then you may have a different threat model. If you think that…" role="doc-noteref">2</a></sup>?</p>

<p>Broadly speaking, there are four ways to protect your sensitive apps.  Relying on the regular lockscreen, hiding the apps, using a Private Space, or placing the apps in different profile.  Let's look at the advantages and disadvantages of each approach.</p>

<h2 id="regular-lockscreen"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#regular-lockscreen">Regular Lockscreen</a></h2>

<p>Android's lockscreen controls are pretty good - <em>if</em> you turn them on.</p>

<p>Perhaps you have a super-long and complicated password. Maybe a 10 digit PIN that only you know. Biometrics like facial recognition and fingerprints are reasonably strong and fairly convenient.</p>

<p>But that relies on your phone being locked when it is snatched. If you're using your phone when it is taken from you, <a href="https://www.theverge.com/2024/5/15/24157068/android-15-ai-theft-detection-lock-privacy-security">the lockscreen <em>might</em> detect it and lock automatically</a>, but you need a modern device and to have specifically enabled the setting.</p>

<p>If a thief has shoulder-surfed your 4 digit PIN, that will be enough to let them enter your phone.</p>

<p>But here we are concerned with someone threatening you. Basically, if someone has a knife pointed at you, you're probably going to unlock the phone for them<sup id="fnref:palm"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fn:palm" class="footnote-ref" title="Yes, I know that your self-defence training is impressive, but handing over your unlocked phone is a lot preferable to getting punctured." role="doc-noteref">3</a></sup>. So, let's assume we want to protect our banking apps from someone who has access to your <em>unlocked</em> device.</p>

<h2 id="launcher-hiding"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#launcher-hiding">Launcher Hiding</a></h2>

<p>Some Android phones <a href="https://www.geeksforgeeks.org/how-to-hide-apps-on-an-android-phone/">let you hide apps</a>. When an attacker is scrolling through the list of installed apps, they won't be able to see any apps which are hidden.</p>

<p>This, I think, is a reasonable way to hide your banking apps. You can show the thug that there aren't any installed. That may or may not be enough to mollify them.  They might still nick your device, but you won't be forced to transfer your savings elsewhere.</p>

<p>This, of course, presents a problem for the regular user. How do <em>you</em> launch your apps if you can't find them?  Most launchers will let you type in the name of the app to find it - the app is merely hidden from the default list.</p>

<p>So an attacker would have to try typing "HSBC" or "Barclays" or "Chase" or a dozen different names until they find your app.  Will they be angry if you've lied to them?  Is that a risk you want to take?</p>

<p>Some launchers will let you change the name and icon of your sensitive apps. You can rename "Midland Bank" to "Calculator" and change its icon.  Not every launcher supports this sort of hiding though. It also places a cognitive load on you that you need to remember what you've hidden your apps as. Will you remember than Bank 1 is calendar and Bank 2 is Bumble?</p>

<h2 id="private-space"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#private-space">Private Space</a></h2>

<p>Android 15 has introduced the concept of a <a href="https://support.google.com/android/answer/15341885?hl=en">Private Space</a>. It is like a digital lock-box for your apps. If someone has your unlocked phone, they need to pass through authentication in order to use apps which are locked.</p>

<p>There are two main drawbacks with this approach.</p>

<p>Firstly, locked apps don't run in the background. That means you won't get alerts from them. If you rely on push notifications to tell you if someone is using your card fraudulently, this could be a problem.</p>

<p>Secondly, the Private Space shows up at the bottom of your app list like this:</p>

<p></p><div style="width: 230px;" class="wp-video"><video class="wp-video-shortcode" id="video-55137-2" width="230" height="512" preload="metadata" controls="controls"><source type="video/mp4" src="https://shkspr.mobi/blog/wp-content/uploads/2024/12/private-space.mp4?_=2"><a href="https://shkspr.mobi/blog/wp-content/uploads/2024/12/private-space.mp4">https://shkspr.mobi/blog/wp-content/uploads/2024/12/private-space.mp4</a></video></div><p></p>

<p>So an attacker can easily see it and demand that you open it up.  You can set the Private Space to be hidden. But then you're in the same position as above - typing in "private space" will show it in your launcher.</p>

<h2 id="work-profile"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#work-profile">Work Profile</a></h2>

<p>Android has the concept of "<a href="https://support.google.com/work/android/answer/6191949?hl=en">Work Profiles</a>". They're designed to segregate your work apps and your personal apps. Your work admin can wipe your work profile without touching your personal stuff, and you can't copy confidential emails to your personal area. Nifty!</p>

<p>If you don't have work apps on your phone, you can use an app like <a href="https://f-droid.org/packages/net.typeblog.shelter/">Shelter</a> to make your own Work Profile.</p>

<p>You can stick your banking apps in the Work Profile and have them locked away from prying eyes.</p>

<p>The Work Profile button is more subtle than the Private Space.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/12/workprofile-fs8.png" alt="Work Profile in the quick settings bar." width="485" height="524" class="aligncenter size-full wp-image-55141">

<p>But it still has the disadvantage that, once locked, the apps are suspended and won't receive any alerts.</p>

<h2 id="secondary-profile"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#secondary-profile">Secondary Profile</a></h2>

<p>Finally, modern versions of Android support <a href="https://support.google.com/android/answer/2865483?hl=en">multiple profiles</a>. They're generally designed so that multiple people can use your device - but there's nothing stopping you from putting your banking apps in there.</p>

<p>The immediate advantages of multi-user profiles are:</p>

<ul>
<li>The profile can be protected by a separate password.</li>
<li>The profile switcher is generally more subtle than the Work Profile switcher or Private Space toggle.</li>
<li>Apps can run in the background while in a separate profile.</li>
</ul>

<p>The disadvantage is that, because it is a completely separate profile, you'll need to sign in again using your Google account in order to install apps from the Play store. If you use a password manager and MFA app, you may need to install them in both your main and secondary profile.</p>

<p>Because the apps can run in the background, there may be some (minor) impact on battery life - you're effectively running Google's Notifications Service twice.</p>

<p>If you are being held at knifepoint and a notification from your bank comes through - you may find it socially awkward to explain.</p>

<h2 id="which-is-right-for-me"><a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#which-is-right-for-me">Which is right for me?</a></h2>

<p>It is complicated. I think I can distil it down to the following:</p>

<ul>
<li>If you need alerts from your banking apps - put them in a secondary profile.</li>
<li>There are <a href="https://www.reddit.com/r/GooglePixel/comments/1g5v3b8/private_space_is_kinda_useless_what_do_you_use_it/lsmhz34/">some reports of banking apps not working in secondary profiles</a> - if yours don't work in a profile then hiding apps is your best defence.</li>
<li>If you're not using Work Mode and don't need alerts - put them in Work Mode.</li>
<li>If you're using Work Mode and don't need alerts - put them in a Private Space and set the space to be hidden.</li>
</ul>

<p>Remember, you can't fling <a href="https://utcc.utoronto.ca/~cks/space/blog/tech/SocialProblemsAndTechnicalDecisions">technical solutions at social problems</a> and expect them to solve everything.  In general, <a href="https://www.ons.gov.uk/peoplepopulationandcommunity/crimeandjustice/bulletins/crimeinenglandandwales/yearendingjune2024">crime in England and Wales is at its lowest level</a> but certain crimes, like phone theft, are on the rise. Despite all the technology thrown at the problem, people are still walking around holding machines worth hundreds of pounds. Each of those machines is a gateway to potentially thousands of pounds. Phones and banking apps are incredibly lucrative targets.</p>

<p>The aim of this exercise isn't to solve the problem of crime. It isn't even to make you a less attractive target. It is to allow you to hand over your phone safe in the knowledge that your banking apps are <em>somewhat</em> protected from miscreants while still being useful to you.</p>

<p>If you have any tips on how to keep banking apps hidden, please leave a comment.</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:smug">
<p>"Not me," you say smugly. "I am far superior to the sheeple. If I want to connect to my bank, I just SSH in to a bespoke firewalled box that runs a disposable Docker image which connect to TOR."  You continue, indifferent to the exasperated sighs of the waitress  "Of course, I only use GNU/Linux on my phones, have you heard of it? I don't even trust password managers! I have my own algorithm for generating passwords using dice. I have some nifty D20s if you wanna see them? Sure beats having a <strong>CR</strong>app on my phone! If I want to transfer someone money I generate a new seed phrase for my Bitcoin wallet and then… say, do you take crypto here?"  The waitress contemplates stabbing you with a fish-knife but, instead, politely replies "If you don't want to leave a tip, sir, that's OK." She makes the mistake of smiling, which you misinterpret as a flirtatious gesture. You torrented a whole bunch of books about social interactions with girls and yet, somehow, failed to understand any of them. You try negging her. That's bound to work. "Of course, you're probably the sort of girl who uses an iPhone or as I call them…" before you can chuckle about normies running iDrones the waitress has turned and walked away. Bitch. Still, at least you don't have any banking apps on your phone. That makes you better than most people.&nbsp;<a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fnref:smug" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:see">
<p>See also <a href="https://www.reddit.com/r/UKPersonalFinance/comments/11nmyyz/bank_and_phone_lessons_learned_after_a_robbery/">Bank and phone lessons learned after a robbery</a> and <a href="https://www.reddit.com/r/UKPersonalFinance/comments/1clqlxd/i_was_robbed_and_forced_to_transfer_money_from_my/">I was robbed and forced to transfer money from my banking app</a>.&nbsp;<a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fnref:see" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:state">
<p>Here we're mostly concerned with street theft. If you are the target of state-sponsored violence, or the police are searching your phone, then you may have a different threat model. If you think that your snarky posts on your three-subscriber Substack about "lamestream media" and "Micro$oft" make you a target for the CIA, please go outside and run around in the fresh air for a bit.&nbsp;<a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fnref:state" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:palm">
<p>Yes, I know that <a href="https://forums.somethingawful.com/showthread.php?threadid=3742916">your self-defence training is impressive</a>, but handing over your unlocked phone is a lot preferable to getting punctured.&nbsp;<a href="https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/#fnref:palm" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=55137&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/12/whats-the-best-way-to-protect-banking-apps-on-android/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		<enclosure url="https://shkspr.mobi/blog/wp-content/uploads/2024/12/private-space.mp4" length="169206" type="video/mp4" />

			</item>
		<item>
		<title><![CDATA[A decade later, has my mobile security advice changed?]]></title>
		<link>https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/</link>
					<comments>https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 30 Sep 2024 11:34:10 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[podcast]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=52973</guid>

					<description><![CDATA[A decade ago, I appeared on the 361 Podcast to give my advice about mobile security.  This was the era of the iPhone 5 and Android KitKat. BlackBerry was trying to have (yet another) resurgence and Nokia was desperately trying to keep Windows Phone alive. What advice did I give then, and is it still relevant?    Stay Sceptical  In at number five is just stay sceptical. I mean, quite often, lots…]]></description>
										<content:encoded><![CDATA[<p>A decade ago, I appeared on the <a href="https://www.361podcast.com/s07e04">361 Podcast to give my advice about mobile security</a>.</p>

<p>This was the era of the iPhone 5 and Android KitKat. BlackBerry was trying to have (yet another) resurgence and Nokia was desperately trying to keep Windows Phone alive. What advice did I give then, and is it still relevant?</p>

<iframe src="https://player.fireside.fm/v2/NPvXvAWN+FiLUiDxm?theme=dark" width="740" height="200" frameborder="0" scrolling="no"></iframe>

<h2 id="stay-sceptical"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#stay-sceptical">Stay Sceptical</a></h2>

<blockquote><p>In at number five is just stay sceptical. I mean, quite often, lots of mobile viruses and mobile scams spread by text message, by email, by Twitter. And these are all things that we get on our phone. But for some reason, because they seem to come from people we trust, all of our savvy just goes out the window. When you see a message from someone which purports to be your friend, just think, does this sound like them? Is what they're asking me to do a rational thing to do? And when you go and click on a link that someone has sent, check to see if it's actually taking you to where you expect to go.</p></blockquote>

<p>Still entirely relevant, sadly. You've probably received an SMS saying "Mum, I've dropped my phone. This is my temporary number." Or accidentally clicked on an advert which prompts you to hand over your credentials.</p>

<p>Scams are everywhere. One of the best things you can do to protect yourself and your data is to be less trusting.</p>

<p>Browsers are getting better at sharing real-time blocklists. So clicking on an extremely dangerous website is likely to generate a scary warning. But these technologies aren't perfect.</p>

<h2 id="dont-just-change-your-password"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#dont-just-change-your-password">Don't <em>just</em> change your password</a></h2>

<blockquote><p>So what can I do if I have ended up putting my password into a fake site?</p>

<p>The most important thing you have to do is, if it's something like Twitter, go to settings and you'll see all the applications which have authenticated against that. You just need to go and delete all of those and then change your password.</p></blockquote>

<p>This is something that a lot of sites <em>still</em> get wrong. If a baddie has got your password, they can use OAuth to connect your account to their service. Changing your password <em>doesn't sever the link!</em></p>

<h2 id="2fa"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#2fa">2FA</a></h2>

<blockquote><p>If you want to be really security conscious, you can turn on something called two-factor authentication. This means you give your mobile number to the social network. When you try to log in, what will happen is you type in your username and your password and then Twitter will send you a text message and it says your one-time password is 12345. You type that code in and you're logged in. That way, if someone does manage to get your username and password, it doesn't matter because they don't have your phone as well.</p></blockquote>

<p>I encouraged people to use SMS as their preferred way of enabling Multi-Factor Authentication. Back then, that was pretty much the only choice for normal people. <a href="https://www.facebook.com/notes/10157814548431886/">Facebook wouldn't introduce non-SMS MFA until 2018</a>.</p>

<p>Nowadays, I'd probably recommend using an authenticator app which generates TOTP codes.  SMS is basically fine for normal people - yes, it can be spoofed or hacked at a network level, but that's unlikely unless you're specifically targetted.</p>

<h2 id="official-app-channels"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#official-app-channels">Official App Channels</a></h2>

<blockquote><p>Don't download apps outside of the official app store. Now, the app stores aren't perfect. You can get dodgy apps in there, but there is some safety in numbers.</p></blockquote>

<p>Reluctantly, I think I still agree with this. Back in the day, it was <em>too</em> easy to install dodgy apps. Drive-by downloads were common, and Android had a particularly poor model of security.</p>

<p>I value independent app stores like F-Droid and Aurora - but there's no doubt that they are generally for the more advanced users.  And, yeah, app stores aren't <em>perfect</em>, but they're still less likely to completely infect your phone and send premium rate messages.</p>

<h2 id="virus-checkers"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#virus-checkers">Virus Checkers</a></h2>

<blockquote><p>you can download apps which are virus checkers of a sort. I'm quite keen on Lookout, which is a great Android app. Whenever you install something, it will check it and it will look through the list of permissions, alert you, but it will also look at the app and see whether it's been reported that it's a scam or a virus.</p></blockquote>

<p>These days, I think virus checking apps are less useful. The permissions model of Android and iOS are much improved, and it's harder for apps to do bad things in the background.</p>

<p>If you have a corporate device (or personal device with work mode) an app scanner is usually mandatory as part of your employer's Mobile Device Management policies. Again, I'm not totally convinced they're a brilliant idea. They can be useful for peace of mind, or to prevent certain classes of attacks.</p>

<h2 id="password-managers"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#password-managers">Password Managers</a></h2>

<blockquote><p>Lots of people use really short passwords.  Why? Because they're easy to remember and they want to be able to type them into their mobile phone. And this means that people quite often have the same password for Twitter as they've got for Facebook, as they've got for email, as they've got for everything else. This is a real security nightmare because it means that if your Twitter password gets hacked, those hackers have access to everything, all your accounts. So my top tip is use a password manager.</p></blockquote>

<p>Yup! No notes. Get a password manager. I like <a href="https://bitwarden.com/">BitWarden</a> - but pick whichever meets your needs.</p>

<h2 id="physical-security"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#physical-security">Physical Security</a></h2>

<blockquote><p>This is my number one tip for mobile security. Buy a wrist strap.</p>

<p>On your phone, you probably have a case. It's got a little hole and you can buy a lanyard, a bit of string or a bit of leather that you clip onto your phone and you wrap around your wrist while you're using it.</p>

<p>Recent reports said 10,000 phones are stolen in London every month, 120,000 a year. That's just in London. Across the UK, it's hundreds of thousands. If you're wearing a wrist strap, it is much more unlikely that someone will be able to yank the phone away from you, because if they do yank the phone away from you, chances are it's unlocked because you are making your call, you're looking at Google Maps and hey presto, they've got access to all of your email, all of your documents. They can start making premium rate phone calls straight away.</p></blockquote>

<p>I still stand by this. In London, <a href="https://www.bbc.co.uk/news/uk-england-london-65105199">a phone is stolen every 6 minutes</a> - only 2% of them are ever recovered.</p>

<p>Strap your phone to you. Don't leave it on the table when you're in a pub. If you need to check directions, turn away from the street and hold it in both hands.</p>

<p>Phones are now worth <em>thousands</em> of pounds. They are a high-value target. You probably have a banking app on your phone - or contactless payment set up. Treat your phone as though you were carrying a big wodge of notes.</p>

<p>Another part of physical security is:</p>

<blockquote><p>The other thing that you need to do is set a PIN or a password on your phone. You can use facial recognition or a thumbprint scanner.  Anything to stop a casual thief being able to get into your phone is of paramount importance.</p></blockquote>

<p>If your phone is stolen while it is unlocked, you're shit out of luck. If it's locked, it is much harder for all but the most determined attacker to get in to it. Make sure all your banking apps have passwords on them.</p>

<p>Similarly, a SIM lock is essential. You don't want someone ejecting your SIM card and making expensive calls on it.</p>

<p>I also suggested:</p>

<blockquote><p>Set up Find My Phone. If you're on Android, Android Device Manager, this means that if your phone is stolen, you can find out where it is. But much more importantly, you can click a button and have your phone be completely wiped</p></blockquote>

<p>Again, solid advice. Perhaps all that will happen is you'll <a href="https://www.bbc.co.uk/news/articles/c3rdy132q3lo">see your phone visit China</a>. But at least you'll be able to prevent the thieves getting into your data.</p>

<h2 id="vpns"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#vpns">VPNs</a></h2>

<p>One of the hosts made this comment:</p>

<blockquote><p>If you're not familiar, a VPN is when you make a secured connection back to a server and all the data through that pipe is encrypted.
And the reason for doing that is that when I'm in a coffee shop or I'm on a public Wi-Fi network or something like that, it keeps my data secure because it doesn't matter whether the app does a good job of securing it or not. It gets it all encrypted as it goes over the network.</p></blockquote>

<p>Nowadays a VPN is less useful than it was. Let's Encrypt launched later that year and with it brought a dramatic increase in the number of Internet services which used HTTP<strong>S</strong>. The popularity of <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security">HSTS</a> increased, which means that most apps refuse to connect to non-secure versions of their site.</p>

<p>VPNs do protect you if unencrypted data is flowing through your device - but that is becoming rarer. I lean slightly towards the opinion that a VPN is <em>usually</em> a bad idea. They are, effectively, an untrusted connection between you and your destination. A malicious VPN - or one ordered to behave in such a way - is worse than no VPN.</p>

<h2 id="what-would-i-add-to-the-list"><a href="https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/#what-would-i-add-to-the-list">What would I add to the list?</a></h2>

<p>I think there are a few sensible additions.</p>

<ol start="0">
<li>Back up your data. Accept that, at some point, your phone will be compromised or stolen. Ensure you have safe backups of all your stuff.

<ul>
<li>Actually <em>test</em> your backups. For most people, that means regularly look inside them to make sure all your photos are still there.</li>
</ul></li>
<li>Activate your phone's emergency features. Learn which buttons to press to automatically lock and/or disable your phone.

<ul>
<li>Practice using them. You may need to use them in an emergency.</li>
</ul></li>
<li>Make sure your Password Manager and MFA tokens can be accessed from another device.

<ul>
<li>Once your phone has gone, you will still need to get into accounts to lock them down.</li>
</ul></li>
<li>Install an ad-blocker. Not only will it protect your sanity; you're less likely to see dodgy content.

<ul>
<li>Do it on mobile <em>and</em> larger devices.</li>
</ul></li>
</ol>

<p>Stay safe out there!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=52973&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/09/a-decade-later-has-my-mobile-security-advice-changed/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Bank scammers using genuine push notifications to trick their victims]]></title>
		<link>https://shkspr.mobi/blog/2024/05/bank-scammers-using-genuine-push-notifications-to-trick-their-victims/</link>
					<comments>https://shkspr.mobi/blog/2024/05/bank-scammers-using-genuine-push-notifications-to-trick-their-victims/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 03 May 2024 11:34:16 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[bank]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[phishing]]></category>
		<category><![CDATA[scam]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=50470</guid>

					<description><![CDATA[You receive a call on your phone. The polite call centre worker on the line asks for you by name, and gives the name of your bank. They say they&#039;re calling from your bank&#039;s fraud department.  &#34;Yeah, right!&#34; You think. Obvious scam, isn&#039;t it? You tell the caller to do unmentionable things to a goat.  They sigh.  &#34;I can assure you I&#039;m calling from Chase bank. I understand you&#039;re sceptical. I&#039;ll…]]></description>
										<content:encoded><![CDATA[<p>You receive a call on your phone. The polite call centre worker on the line asks for you by name, and gives the name of your bank. They say they're calling from your bank's fraud department.</p>

<p>"Yeah, right!" You think. Obvious scam, isn't it? You tell the caller to do unmentionable things to a goat.  They sigh.</p>

<p>"I can assure you I'm calling from Chase bank. I understand you're sceptical. I'll send a push notification through the app so you can see this is a genuine call."</p>

<p>Your phone buzzes. You tap the notification and this pops up on screen:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/05/chase-fs8.png" alt="`In app popup. &quot;Are you on the phone with Chase? We need to check it's you on the phone to us. Let us know it's you and enter your passcode on the next screen. @ Not you? Your details are safe. Just tap 'No, it's not me' and we'll end the call.&quot;`" width="512" class="aligncenter size-full wp-image-50471">

<p>This is <em>obviously</em> a genuine caller! This is a genuine pop-up, from the genuine app, which is protected by your genuine fingerprint. You tap the "Yes" button.</p>

<p>Why wouldn't you? The caller knows your name and bank <em>and</em> they have sent you an in-app notification.  Surely that can only be done by the bank. Right?</p>

<p>Right!</p>

<p>This is a genuine notification. It <em>was</em> sent by the bank.</p>

<p>You proceed to do as the fraud department asks. You give them more details. You move your money into a safe account. You're told you'll hear from them in the morning.</p>

<p>Congratulations. You just got played. <a href="https://www.reddit.com/r/UKPersonalFinance/comments/1cih3kd/been_scammed_over_18000_through_my_chase_account/">Scammers have stolen your life savings</a>.</p>

<h2 id="how-the-scam-works"><a href="https://shkspr.mobi/blog/2024/05/bank-scammers-using-genuine-push-notifications-to-trick-their-victims/#how-the-scam-works">How the scam works</a></h2>

<p>This is reasonably sophisticated, and it is easy to see why people fall for it.</p>

<ol>
<li>The scammer calls you up. They keep you on the phone while...</li>
<li>The scammer's accomplice calls your bank. They pretend to be you. So...</li>
<li>The bank sends you an in-app alert.</li>
<li>You confirm the alert.</li>
<li>The scammer on the phone to your bank now has control of your account.</li>
</ol>

<p>Look closer at what that pop is <em>actually</em> asking you to confirm.</p>

<blockquote><p>We need to check it is <em>you</em> on the phone to <em>us</em>.</p></blockquote>

<p>It isn't saying "This is <em>us</em> calling <em>you</em> - it is quite the opposite!</p>

<p>This pop-up is a security disaster. It should say something like:</p>

<blockquote><p>Did you call us?</p>

<p>If someone has called you claiming to be from us <strong>hang up now</strong></p>

<p><kbd>Yes, I am calling Chase</kbd> - <kbd>No, someone called me</kbd></p></blockquote>

<p>I dare say most people would fall for this. Oh, not you! You're <em>far</em> too clever and sceptical. You'd hang up and call the number on your card. You'd spend a terrifying 30 minute wait on hold to the fraud department, while hoping fraudsters haven't already drained your account.</p>

<p>But even if you were constantly packet sniffing the Internet connection on your phone, you'd see that this was a genuine pop-up from your genuine app. Would that bypass your defences? I reckon so.</p>

<p>Criminals are getting increasingly good at this. Banks are letting down customers by having vaguely worded security pop-up which they know their customers don't read properly.</p>

<p>And, yes, customers can sometimes be a little gullible. But it is hard to be constantly on the defensive.</p>

<h2 id="further-reading"><a href="https://shkspr.mobi/blog/2024/05/bank-scammers-using-genuine-push-notifications-to-trick-their-victims/#further-reading">Further reading</a></h2>

<p>You can <a href="https://web.archive.org/web/20240502195209/https://www.reddit.com/r/UKPersonalFinance/comments/1cih3kd/been_scammed_over_18000_through_my_chase_account/">read the original story from the victim</a> on Reddit.  See more <a href="https://mastodon.social/@Edent/112372412442888807">comments on Mastodon</a>.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=50470&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/05/bank-scammers-using-genuine-push-notifications-to-trick-their-victims/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[I can't use my number pad for 2FA codes]]></title>
		<link>https://shkspr.mobi/blog/2024/04/i-cant-use-my-number-pad-for-2fa-codes/</link>
					<comments>https://shkspr.mobi/blog/2024/04/i-cant-use-my-number-pad-for-2fa-codes/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 17 Apr 2024 11:34:20 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[ui]]></category>
		<category><![CDATA[ux]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=50119</guid>

					<description><![CDATA[This has to be the most infuriating bug report I&#039;ve ever submitted.  I went to type in my 2FA code on a website - but no numbers appeared on screen. Obviously, I was an idiot and had forgotten to press the NumLock button. D&#039;oh! I toggled it on and typed again. No numbers appeared. I switched to another tab, my numbers appeared when I typed them. So I was reasonably confident that my keyboard was…]]></description>
										<content:encoded><![CDATA[<p>This has to be the most infuriating bug report I've ever submitted.</p>

<p>I went to type in my 2FA code on a website - but no numbers appeared on screen. Obviously, I was an idiot and had forgotten to press the NumLock button. D'oh! I toggled it on and typed again. No numbers appeared. I switched to another tab, my numbers appeared when I typed them. So I was reasonably confident that my keyboard was working.</p>

<p>I swapped back to the 2FA entry and tried again. Still nothing.  Then I tried typing the numbers using the number row on my keyboard. My 2FA code appeared.</p>

<p>WHAT IN THE SAINTED NAME OF ALPHONSE CHAPANIS IS GOING ON?!?!?</p>

<p>Developers often use JavaScript to "improve" the standard features of HTML.  For example, using <code>&lt;input type="number"&gt;</code> has some <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number#accessibility">accessibility concerns</a> and using <a href="https://css-tricks.com/everything-you-ever-wanted-to-know-about-inputmode/#aa-numeric"><code>inputmode="numeric"</code></a> is great for showing a number key board on mobile, but not much else.</p>

<p>So a developer wants a reliable way to make sure a user can <em>only</em> type numbers. Fair enough.</p>

<p>There are two ways to do this - a right way and a wrong way - using <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent"><code>KeyboardEvent</code></a>.</p>

<p>One way is to <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key">listen for the character being sent from the keyboard</a> - known as the <code>key</code>.</p>

<p>The other is to <a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code">listen for the <em>button</em> being pressed on the keyboard</a> - known as the <code>code</code>.</p>

<p>A good demo of this is at <a href="https://keyjs.dev/">keyjs.dev</a> - play around with it to see what keyboard buttons your browser can detect.</p>

<p>When I press 7 on the top row of my keyboard, the key is 7 and the code is <strong><code>Digit7</code></strong>.</p>

<p>But when I press 7 on my number pad, the key is 7 but the code is <strong><code>Numpad7</code></strong>.</p>

<p>The JavaScript on the website was rejecting any key code which wasn't a "Digit"!</p>

<p>Perhaps I am a weirdo for insisting on both having and using my numpad?  Perhaps developers need to test on something other than MacBooks? Perhaps JavaScript was a mistake and the Web would be better without it?</p>

<p>Either way, don't be like that website. Let users type in using whatever keys they like.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=50119&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/04/i-cant-use-my-number-pad-for-2fa-codes/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[What the UK Government gets wrong about QR codes]]></title>
		<link>https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/</link>
					<comments>https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 20 Mar 2024 12:34:04 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[domains]]></category>
		<category><![CDATA[gov.uk]]></category>
		<category><![CDATA[privacy]]></category>
		<category><![CDATA[QR Codes]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49986</guid>

					<description><![CDATA[One of my most memorable experiences in the Civil Service was discussing link shortening services with a very friendly person from the Foreign and Commonwealth Office.  I was trying to explain why link shortners like bit.ly and ow.ly weren&#039;t sensible for Government use. They didn&#039;t seem to particularly care about the privacy implications or the risk of phishing.  I needed to take a different…]]></description>
										<content:encoded><![CDATA[<p>One of my most memorable experiences in the Civil Service<sup id="fnref:cs"><a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#fn:cs" class="footnote-ref" title="I am no longer a Civil Servant. The Government's views are not my own. And vice-versa." role="doc-noteref">0</a></sup> was discussing link shortening services with a very friendly<sup id="fnref:friend"><a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#fn:friend" class="footnote-ref" title="But not so friendly that they'd tell me their surname..." role="doc-noteref">1</a></sup> person from the Foreign and Commonwealth Office.</p>

<p>I was trying to explain why link shortners like bit.ly and ow.ly weren't sensible for Government use. They didn't seem to particularly care about <a href="https://shkspr.mobi/blog/2020/02/bitly-finally-starts-taking-privacy-seriously/">the privacy implications</a> or the risk of phishing.  I needed to take a different tack.</p>

<p>"So, you know how .uk is the UK and .de is Germany, right?"<br>
"Yes."<br>
"What country do you think .ly is for?"</p>

<p>There was some consulting of <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#LY">ISO 3166-1 alpha-2</a> whereupon the blood drained from their face and they stepped outside to make a phone call.</p>

<p>A little while later, the <a href="https://webarchive.nationalarchives.gov.uk/ukgwa/20220301154404/https://www.ncsc.gov.uk/blog-post/long-and-short-it">National Cyber Security Centre published an explainer about why they weren't using bit.ly any more</a>.</p>

<p>Throughout my time in the Civil Service I advocated for the use of .gov.uk URls everywhere. They're a trusted destination for users, they're under Government control so are less likely to be hijacked, and they don't require users to give their data to third parties.</p>

<p>I helped the Government Communication Service write "<a href="https://gcs.civilservice.gov.uk/blog/link-shorteners-the-long-and-short-of-why-you-shouldnt-use-them/">Link shorteners: the long and short of why you shouldn’t use them</a>."</p>

<p>Today, in the post, I received <strong>six</strong> QR codes for Government services.  Let's take a look at them.</p>

<h2 id="the-good"><a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#the-good">The Good</a></h2>

<p>Policing Surrey have a QR code which points to <code>surrey-pcc.gov.uk/...</code></p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/03/PCC.jpg" alt="A leaflet for Surrey Police." width="504" height="512" class="aligncenter size-full wp-image-49992">

<p>Excellent! 10/10! No notes.</p>

<p>Woking Council send out this code which use <code>qr.woking.gov.uk</code></p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/03/Woking.jpg" alt="A letter about council tax." width="504" height="512" class="aligncenter size-full wp-image-49989">

<p>Brilliant! The use of the <code>qr.</code> subdomain means they can easily track how many people follow the link from the code.</p>

<h2 id="the-bad"><a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#the-bad">The Bad</a></h2>

<p>Childcare Choices is a leaflet which is, I assume, shoved through everyone's letterbox.  All the URls in the leaflet say <code>gov.uk</code><sup id="fnref:brand"><a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#fn:brand" class="footnote-ref" title="When I was there, the &quot;Brand Police&quot; were insistent that it should be referred to as GOV.UK in all-caps. The leaflet exclusively uses the lower-case version. Sorry Neil!" role="doc-noteref">2</a></sup> - but what happens when you scan?</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/03/ChildCare-QR.jpg" alt="A leaflet for Childcare with a prominent QR code." width="504" height="256" class="aligncenter size-full wp-image-49993">

<p>Our old <del>friend</del> enemy Bitly. A user scanning this has no idea where that code will take them. They cannot access the content without giving their data away to Bitly.</p>

<p>Surrey also sent me a leaflet with <strong>two</strong> different QR codes.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/03/Surrey2.jpg" alt="A leaflet for Surrey - the QR code points to scnv.io." width="504" height="256" class="aligncenter size-full wp-image-49990">

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/03/Surrey1.jpg" alt="A leaflet for Surrey - the QR code points to scnv.io." width="504" height="256" class="aligncenter size-full wp-image-49991">

<p>There <a href="https://www.beep.blog/io/">are many reasons not to use .io</a>. Of particular interest is the <a href="https://scnv.io/">scnv.io privacy policy</a> which, if you click that link, you will see is missing from their website! What does this company do with the data of people who scan that code? No one knows!</p>

<h2 id="the-ugly"><a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#the-ugly">The Ugly</a></h2>

<p>Surrey police started <em>so</em> well, but the back of their leaflet is a major disappointment.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/03/PCC2.jpg" alt="A police leaflet. The QR code is almost invisible." width="504" height="512" class="aligncenter size-full wp-image-49988">

<p>Aside from using an unintelligible Bitly link, the QR code is inverted. The QR standard is very clear that the codes should be black-on-white. Some scanners will have difficulty scanning these white-on-dark codes. They may look æsthetically pleasing, but it's a pretty rubbish experience if you can't scan them.</p>

<h2 id="now-what"><a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#now-what">Now What?</a></h2>

<p><a href="https://shkspr.mobi/blog/2007/12/qr-codes/">I've been writing about QR codes for <em>17 years!</em></a> I'm thrilled that they've finally caught on. But, like any piece of technology, they need to be used sensibly. The <a href="https://shkspr.mobi/blog/2011/05/you-are-too-stupid-to-use-qr-codes-correctly/">rules are pretty straightforward</a> - mostly boiling down to testing your codes and keeping them simple.</p>

<p>Is there a risk <a href="https://shkspr.mobi/blog/2011/12/how-to-prevent-qr-hijacking/">risk of QR hijacking</a>? Possibly. The best defence is to train users to look for a trusted URl.</p>

<p>In this case, using link shorteners is training users to be phished. If they are used to official Government QR codes going to weird locations, they won't notice when a scammer tries to send them to a dodgy site.</p>

<p>Please practice safe QR generation!</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:cs">
<p>I am no longer a Civil Servant. The Government's views are not my own. And vice-versa.&nbsp;<a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#fnref:cs" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:friend">
<p>But not so friendly that they'd tell me their surname...&nbsp;<a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#fnref:friend" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:brand">
<p>When I was there, the "Brand Police" were insistent that it should be referred to as GOV.UK in all-caps. The leaflet exclusively uses the lower-case version. Sorry Neil!&nbsp;<a href="https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/#fnref:brand" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49986&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/03/what-the-uk-government-gets-wrong-about-qr-codes/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[I made a mistake in verifying HTTP Message Signatures]]></title>
		<link>https://shkspr.mobi/blog/2024/03/i-made-a-mistake-in-verifying-http-message-signatures/</link>
					<comments>https://shkspr.mobi/blog/2024/03/i-made-a-mistake-in-verifying-http-message-signatures/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 14 Mar 2024 12:34:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49885</guid>

					<description><![CDATA[It&#039;s never great to find out you&#039;re wrong, but that&#039;s how learning and personal growth happens.  HTTP Message Signatures are hard. There are lots of complex parts and getting any aspect wrong means certain death.  In a previous post, I wrote A simple(ish) guide to verifying HTTP Message Signatures in PHP. It turns out that it was too simple. And far too trusting.  An HTTP Message Signature is a…]]></description>
										<content:encoded><![CDATA[<p>It's never great to find out you're wrong, but that's how learning and personal growth happens.</p>

<p>HTTP Message Signatures are hard<sup id="fnref:hard"><a href="https://shkspr.mobi/blog/2024/03/i-made-a-mistake-in-verifying-http-message-signatures/#fn:hard" class="footnote-ref" title="You might think the Entscheidungsproblem is hard, but that's just peanuts compared to etc. etc." role="doc-noteref">0</a></sup>. There are lots of complex parts and getting any aspect wrong means certain death<sup id="fnref:death"><a href="https://shkspr.mobi/blog/2024/03/i-made-a-mistake-in-verifying-http-message-signatures/#fn:death" class="footnote-ref" title="Or cake." role="doc-noteref">1</a></sup>.</p>

<p>In a previous post, I wrote <a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/">A simple(ish) guide to verifying HTTP Message Signatures in PHP</a>. It turns out that it was <em>too</em> simple. And far too trusting.</p>

<p>An HTTP Message Signature is a header which is separate to the message it signs.  You might receive a JSON message like this:</p>

<pre><code class="language-json">{
   "actor":   "https://example.com/user/Alice",
   "message": "We strike at dawn!"
}
</code></pre>

<p>How do you know that <em>really</em> came from Alice? You look at the header of the message. It will be something like:</p>

<pre><code class="language-_">Signature: 
   keyId="https://example.org/user/Alice#main-key",
   algorithm="rsa-sha256",
   headers="(request-target) host date digest",
   signature="/AJ4Dv/wSL3XE1dLjFHCYVc7AF4f3+Q10G/r8+6cPsooiUh2K3YX3z++Nclo4qKHYr61yu+T4OMqUry1T6ZHmZqmNkg1RpVg=="
</code></pre>

<p>We want to check that Alice signed this message with her <em>private</em> key. So we grab her <em>public</em> key given by the <code>keyId</code>.
From there, we do some fancy maths using RSA-SHA256 and conclude that, when you put together the <code>(request-target) host date digest content-type</code> and compare them to the public key, they can only have be signed by the private key. Hurrah!</p>

<p>Did you spot the mistake I made? It wasn't in the maths, or the complex ordering of the data, or the algorithm choice, or some weird Unicode problem.</p>

<p>I made an error in <em>trust</em>.</p>

<p>Take a look at the Signature again.</p>

<p>The <code>keyId</code> is from example.<strong>org</strong>. But the actor is from example.<strong>com</strong>.</p>

<p>This message <em>is</em> signed correctly. It <em>is</em> cryptographically valid. <strong>But it wasn't signed by the actor in the message!</strong></p>

<p>In this case, the fix is simple.  Get the public key from <code>keyId</code>. Then <em>independently</em> get the named actor's public key.  If they match, all is well. If not, skulduggery is afoot.</p>

<p>I'm <em>almost</em> tempted to say that you should <em>ignore</em> the provided <code>keyId</code> entirely; the source of truth is the actor's key - and the best way to get that is directly from the actor's profile.</p>

<p>Please explain why I'm wrong in the comments.</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:hard">
<p>You might think the <i lang="de">Entscheidungsproblem</i> is hard, but that's just <em>peanuts</em> compared to etc. etc.&nbsp;<a href="https://shkspr.mobi/blog/2024/03/i-made-a-mistake-in-verifying-http-message-signatures/#fnref:hard" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:death">
<p>Or cake.&nbsp;<a href="https://shkspr.mobi/blog/2024/03/i-made-a-mistake-in-verifying-http-message-signatures/#fnref:death" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49885&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/03/i-made-a-mistake-in-verifying-http-message-signatures/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[O2 UK's Weird MSISDN Lookup API]]></title>
		<link>https://shkspr.mobi/blog/2024/03/o2-uks-weird-msisdn-lookup-api/</link>
					<comments>https://shkspr.mobi/blog/2024/03/o2-uks-weird-msisdn-lookup-api/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 04 Mar 2024 12:34:15 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[gdpr]]></category>
		<category><![CDATA[o2]]></category>
		<category><![CDATA[privacy]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49819</guid>

					<description><![CDATA[It&#039;s always fun keeping your network inspector tab open. While looking around the O2 UK website, I found this page all about eSIMs.  For some reason, it wants to know the user&#039;s phone number.  I put in a random number, and it refused to let me in.    Putting in a genuine O2 number let me through.  So what is it doing to validate numbers?  It is making an API call to this URl: …]]></description>
										<content:encoded><![CDATA[<p>It's always fun keeping your network inspector tab open. While looking around the O2 UK website, I found <a href="https://www.o2.co.uk/o/customer/mods/lookup/447700900000">this page all about eSIMs</a>.  For some reason, it wants to know the user's phone number.</p>

<p>I put in a random number, and it refused to let me in.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/03/o2esim-fs8.png" alt="Sorry, we don’t recognise this number. Please try again." width="972" height="391" class="aligncenter size-full wp-image-49821">

<p>Putting in a genuine O2 number let me through.  So what is it doing to validate numbers?</p>

<p>It is making an API call to this URl:</p>

<pre>https://www.o2.co.uk/o/customer/mods/lookup/447700900123</pre>

<p>After a bit of testing, this is how I <em>think</em> it works.</p>

<p>If you give it an O2 phone number, it replies with:</p>

<pre><code class="language-json">{&amp;quot;type&amp;quot;:&amp;quot;ONE&amp;quot;}
</code></pre>

<p>If you give it a number which <em>isn't</em> on O2, it gives:</p>

<pre><code class="language-json">{&amp;quot;type&amp;quot;:&amp;quot;ZERO&amp;quot;}
</code></pre>

<p>A number it doesn't recognise gives:</p>

<pre><code class="language-json">{&amp;quot;message&amp;quot;:&amp;quot;Unable to find the requested resource.&amp;quot;}
</code></pre>

<p>A malformed or incomplete phone number gives:</p>

<pre><code class="language-json">{&amp;quot;message&amp;quot;:&amp;quot;Something&amp;#039;s wrong. Please try again later.&amp;quot;}
</code></pre>

<h2 id="responsible-disclosure"><a href="https://shkspr.mobi/blog/2024/03/o2-uks-weird-msisdn-lookup-api/#responsible-disclosure">Responsible Disclosure?</a></h2>

<p>As far as I can tell, O2 no longer have a Bug Bounty or Responsible Disclosure offering. So I'm publishing it here to let people know.</p>

<p>It is possible that someone could use this API to disclose a (minor) piece of personal information about you - namely whether your phone number is on O2 or not.  I don't think that's particularly sensitive, but it is probably worth knowing.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49819&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/03/o2-uks-weird-msisdn-lookup-api/feed/</wfw:commentRss>
			<slash:comments>7</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[A simple(ish) guide to verifying HTTP Message Signatures in PHP]]></title>
		<link>https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/</link>
					<comments>https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 27 Feb 2024 12:34:04 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[cryptography]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49733</guid>

					<description><![CDATA[Mastodon makes heavy use of HTTP Message Signatures. They&#039;re a newish almost-standard which allows a server to verify that a request made to it came from the person who sent it.  This is a quick example to show how to verify these signatures using PHP. I don&#039;t claim that it covers every use-case, and it is no-doubt missing some weird edge cases. But it successfully verifies messages sent by…]]></description>
										<content:encoded><![CDATA[<p>Mastodon makes heavy use of <a href="https://datatracker.ietf.org/doc/rfc9421/">HTTP Message Signatures</a>. They're a newish almost-standard which allows a server to verify that a request made to it came from the person who sent it.</p>

<p>This is a quick example to show how to verify these signatures using PHP. I don't claim that it covers every use-case, and it is no-doubt missing some weird edge cases. But it successfully verifies messages sent by multiple Fediverse servers.</p>

<p>Let's step through it with an example of a message sent from Mastodon to my server.</p>

<h2 id="headers"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#headers">Headers</a></h2>

<p>The HTTP request starts with these headers:</p>

<pre><code class="language-_">User-Agent:  http.rb/5.1.1 (Mastodon/4.3.0-nightly.2024-02-23; +https://mastodon.social/)
Host:  example.com
Date:  Sun, 25 Feb 2024 10:48:22 GMT
Accept-Encoding:  gzip
Digest:  SHA-256=Hqu/6MR2imi8DTzbNp5PNEAFSyk0poN7+x5F+Z4vZMg=
Content-Type:  application/activity+json
Signature:  keyId="https://mastodon.social/users/Edent#main-key",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="P07V5I2zflR8FRsDMHshHmhgOwSkjWevujEbOyKMwjycrdVXjTD0ACiLuc5lTqDEXZ/...4eg=="
Connection:  Keep-Alive
Content-Length:  2857
</code></pre>

<p>Some of those you may be familiar with, some not.  The first thing we'll do is a sanity check; was this message sent recently? Because clocks drift in and out of synchronisation, we'll check if the message was within ±30 seconds.</p>

<pre><code class="language-php">$headers = getallheaders();
if ( !isset( $headers["Date"] ) ) { return null; }  //  No date set
$dateHeader = $headers["Date"];
$headerDatetime  = DateTime::createFromFormat('D, d M Y H:i:s T', $dateHeader);
$currentDatetime = new DateTime();

// Calculate the time difference in seconds
$timeDifference = abs( $currentDatetime-&gt;getTimestamp() - $headerDatetime-&gt;getTimestamp() );
return ( $timeDifference &lt; 30 );
</code></pre>

<p>That was easy! On to the next bit.</p>

<h2 id="digest"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#digest">Digest</a></h2>

<p>A message posted to the server usually has a body. In this case it is a long string of JSON data.  To ensure the message hasn't been altered in transit, one of the headers is:</p>

<pre><code class="language-_">Digest:  SHA-256=Hqu/6MR2imi8DTzbNp5PNEAFSyk0poN7+x5F+Z4vZMg=
</code></pre>

<p>That says, if you do a SHA-256 hash of the JSON you received, and convert that hash to Base64, it should match the digest in the header.</p>

<pre><code class="language-php">$digestString = $headers["Digest"];
//  Usually in the form `SHA-256=Hqu/6MR2imi8DTzbNp5PNEAFSyk0poN7+x5F+Z4vZMg=`
//  The Base64 encoding may have multiple `=` at the end. So split this at the first `=`
$digestData = explode( "=", $digestString, 2 );
$digestAlgorithm = $digestData[0];
$digestHash = $digestData[1];

//  There might be many different hashing algorithms
//  TODO: Find a way to transform these automatically
if ( "SHA-256" == $digestAlgorithm ) {
    $digestAlgorithm = "sha256";
} else if ( "SHA-512" == $digestAlgorithm ) {
    $digestAlgorithm = "sha512";
}

$json = file_get_contents( "php://input" );

//  Manually calculate the digest based on the data sent
$digestCalculated = base64_encode( hash( $digestAlgorithm, $json, true ) );

return ( $digestCalculated == $digestHash );
</code></pre>

<p>But, of course, if someone has manipulated the JSON, they may also have manipulated the digest. So it is time to look at the signature.</p>

<h2 id="the-signature"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#the-signature">The Signature</a></h2>

<p>Let's take a look at the signature header:</p>

<pre><code class="language-_">Signature:
  keyId="https://mastodon.social/users/Edent#main-key",
  algorithm="rsa-sha256",
  headers="(request-target) host date digest content-type",
  signature="P07V5I2zflR8FRsDMHshHmhgOwSkjWevujEbOyKMwjycrdVXjTD0ACiLuc5lTqDEXZ/...4eg=="
</code></pre>

<p>This contains 4 pieces of information.</p>

<ol>
<li><code>keyID</code> - a link to the user's public key.</li>
<li><code>algorithm</code> - the algorithm used by this signature.</li>
<li><code>headers</code> - the headers which make up the string to be signed.</li>
<li><code>signature</code> - the signature string.</li>
</ol>

<p>Let's split them up so they can be used:</p>

<pre><code class="language-php">//  Examine the signature
$signatureHeader = $headers["Signature"];

// Extract key information from the Signature header
$signatureParts = [];
//  Converts 'a=b,c=d e f' into ["a"=&gt;"b", "c"=&gt;"d e f"]
               // word="text"
preg_match_all('/(\w+)="([^"]+)"/', $signatureHeader, $matches);
foreach ($matches[1] as $index =&gt; $key) {
    $signatureParts[$key] = $matches[2][$index];
}
</code></pre>

<p>Let's tackle each part in order.</p>

<h3 id="get-the-users-public-key"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#get-the-users-public-key">Get the user's public key</a></h3>

<p>You might think you can just get <code>https://mastodon.social/users/Edent#main-key</code> - but you would be wrong.</p>

<p>Firstly, you need to tell the key server that you want the JSON representation of the URl - otherwise you'll end up with HTML.</p>

<pre><code class="language-php">$publicKeyURL = $signatureParts["keyId"];
$context   = stream_context_create(
    [ "http" =&gt; [ "header" =&gt; "Accept: application/activity+json" ] ] 
);
$userJSON  = file_get_contents( $publicKeyURL, false, $context );
</code></pre>

<p>That gets you the JSON representation of the user.  On Mastodon, the key can be found at:
<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/02/jsonkey.pnh-fs8.png" alt="Screenshot of JSON. As described in text." width="938" height="355" class="aligncenter size-full wp-image-49734"></p>

<p>I don't know how to automatically find the key, so I've hard-coded its location.</p>

<pre><code class="language-php">$userData  = json_decode( $userJSON, true );
$publicKey = $userData["publicKey"]["publicKeyPem"];
</code></pre>

<h3 id="get-the-algorithm"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#get-the-algorithm">Get the algorithm</a></h3>

<p>This is rather straightforward. It's just the text in the signature header:</p>

<pre><code class="language-php">$algorithm = $signatureParts["algorithm"];
</code></pre>

<h3 id="reconstruct-the-signing-header"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#reconstruct-the-signing-header">Reconstruct the signing header</a></h3>

<p>Let's take a look at the third piece of the puzzle:</p>

<p><code>headers="(request-target) host date digest content-type"</code></p>

<p>This says "The signature is based on the following parts in order". So we only care about the headers which make up the request, the host, the date, the digest, and the content type. Other servers may require different parts of the headers.</p>

<p>Again, let's tackle them in order.</p>

<h4 id="request-target"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#request-target"><code>(request-target)</code></a></h4>

<p>This means the method of the request and the target it was sent to.  In our example, this is a <code>POST</code> sent to the path <code>/inbox</code>.</p>

<h4 id="host"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#host"><code>host</code></a></h4>

<p>This is the HTTP host the message was sent to.  This should be retrieved from the server, not taken from the sent headers.</p>

<h4 id="date-digest-content-type"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#date-digest-content-type"><code>date digest content-type</code></a></h4>

<p>These are the values from the headers which were sent with the request.</p>

<h4 id="putting-it-all-together"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#putting-it-all-together">Putting it all together</a></h4>

<p>Annoyingly, the HTTP headers are written in Title-Case whereas the headers in the Signature are in lower-case. So some conversion is necessary:</p>

<pre><code class="language-php">//  Manually reconstruct the header string
$signatureHeaders = explode(" ", $signatureParts["headers"] );
$signatureString = "";
foreach ($signatureHeaders as $signatureHeader) {
    if ( "(request-target)" == $signatureHeader ) {
        $method = strtolower( $_SERVER["REQUEST_METHOD"] );
        $target = strtolower( $_SERVER["REQUEST_URI"] );
        $signatureString .= "(request-target): {$method} {$target}\n";
    } else if ( "host" == $signatureHeader ) {
        $host = strtolower( $_SERVER["HTTP_HOST"] );    
        $signatureString .= "host: {$host}\n";
    } else {
        //  In the HTTP header, the keys use Title Case
        $signatureString .= "{$signatureHeader}: " . $headers[ ucwords( $signatureHeader, "-" ) ] . "\n";
    }
}

//  Remove trailing newline
$signatureString = trim( $signatureString );
</code></pre>

<p>This results in a string like this:</p>

<pre><code class="language-_">(request-target): post /inbox
host: example.com
date: Sun, 25 Feb 2024 10:48:22 GMT
digest: SHA-256=Hqu/6MR2imi8DTzbNp5PNEAFSyk0poN7+x5F+Z4vZMg=
content-type: application/activity+json
</code></pre>

<h2 id="get-the-signature"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#get-the-signature">Get the signature</a></h2>

<p>The signature that we are sent is in Base64.</p>

<p><code>signature="P07V5I2zflR8FRsDMHshHmhgOwSkjWevujEbOyKMwjycrdVXjTD0ACiLuc5lTqDEXZ/...4eg=="</code></p>

<p>It needs to be decoded before we can use it.</p>

<pre><code class="language-php">$signature = base64_decode( $signatureParts["signature"] );
</code></pre>

<h2 id="verify-the-signature"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#verify-the-signature">Verify the signature</a></h2>

<p>We're nearly there!  Luckily, we don't have to do any crazy cryptography by hand. We use PHP's <a href="https://www.php.net/manual/en/function.openssl-verify"><code>openssl_verify()</code></a>:</p>

<pre><code class="language-php">//  Finally! Calculate whether the signature is valid
$verified = openssl_verify(
    $signatureString, 
    $signature, 
    $publicKey, 
    $algorithm
);
</code></pre>

<p>That takes the reconstructed string based on the headers, the signature which was sent, the public key we retrieved, and the algorithm.</p>

<p>If it all matches, it will return <code>true</code>.  If not... time for some debugging!</p>

<h2 id="but-what-about"><a href="https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/#but-what-about">But what about...?</a></h2>

<p>This is <em>not</em> a complete solution. My code almost certainly contains bugs, unforeseen edge-cases, memory leaks, black holes, and poisonous frogs.  This is intended to step you through the practical process of verifying an HTTP Message Signature.</p>

<p>Then you should get a properly tested and validated library and use that instead.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49733&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/02/a-simpleish-guide-to-verifying-http-message-signatures-in-php/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[It's a process; not a product]]></title>
		<link>https://shkspr.mobi/blog/2024/02/its-a-process-not-a-product/</link>
					<comments>https://shkspr.mobi/blog/2024/02/its-a-process-not-a-product/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 21 Feb 2024 12:34:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[accessibility]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[usability]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49679</guid>

					<description><![CDATA[Sometimes a client asks me a question and I&#039;m a little stunned by their mental model of the world.  A few weeks ago, we were discussing the need for better cybersecurity in their architecture. We spoke about several aspects of security, then they asked an outstanding question.  &#34;What should I buy to be secure?&#34;  It took a few moments to tease out exactly what they thought they were asking. In…]]></description>
										<content:encoded><![CDATA[<p>Sometimes a client asks me a question and I'm a little stunned by their mental model of the world.</p>

<p>A few weeks ago, we were discussing the need for better cybersecurity in their architecture. We spoke about several aspects of security, then they asked an <em>outstanding</em> question.</p>

<p>"What should I buy to be secure?"</p>

<p>It took a few moments to tease out exactly what they thought they were asking. In their mental model they could just buy a box which did what they needed. Want to print from any workstation? Buy a big HP network printer. Want to get WiFi in the office? Buy a bunch of access points. Want a website? Buy a WordPress. Want security? Buy a [<em>fill in the blank</em>]?</p>

<p>Their notion is that most things are products.  This is a common belief. I've had clients ask "What do I buy to make this accessible?" or "What can I buy to improve usability?"</p>

<p>In all these cases there <em>are</em> unscrupulous people who will sell you a magic cure-all - but the real answer is that these things are a <em>process</em>; not a product.</p>

<p>Yes, you can buy tools which will help <em>improve</em> your security / accessibility / usability etc. But unless you put processes in place to get people to use them effectively, the tools are useless.</p>

<p>People need to understand <em>why</em> something is important. They need processes which <em>support</em> best practices. The business needs a holistic understanding of <em>how</em> these processes improve the business.  And that is all underpinned by tools which make it possible.</p>

<p>There's no magic box which can both protect you from your CEO accidentally CCing confidential data to a competitor <em>and</em> stop DDoS attacks. An accessibility overlay won't help you if your staff refuse to incorporate alt text into their workflow. Automated code testing can't stop you building things without testing them with users.</p>

<p>Security is a <em>verb</em> - it is a <em>doing</em> word.
Accessibility is a <em>verb</em> - it is a <em>doing</em> word.
Usability is a <em>verb</em> - it is a <em>doing</em> word.</p>

<p>Buy nouns which support your verbs.</p>

<iframe title="Massive Attack - Teardrop (Official Video)" width="620" height="465" src="https://www.youtube.com/embed/u7K72X4eo_s?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49679&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/02/its-a-process-not-a-product/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[A quick look inside the HSTS file]]></title>
		<link>https://shkspr.mobi/blog/2024/01/a-quick-look-inside-the-hsts-file/</link>
					<comments>https://shkspr.mobi/blog/2024/01/a-quick-look-inside-the-hsts-file/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 03 Jan 2024 12:34:36 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[dns]]></category>
		<category><![CDATA[domains]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49041</guid>

					<description><![CDATA[You type in to your browser&#039;s address bar example.com and it automatically redirects you to the https:// version. How does your browser know that it needed to request the more secure version of a website?  The answer is... A big list.  The HTTP Strict Transport Security (HSTS) list is a list of domain names which have told Google that they always want their website served over https.  If the user …]]></description>
										<content:encoded><![CDATA[<p>You type in to your browser's address bar <code>example.com</code> and it automatically redirects you to the https:// version. How does your browser know that it needed to request the more secure version of a website?</p>

<p>The answer is... A <em>big</em> list.  The <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security">HTTP Strict Transport Security</a> (HSTS) list is a list of domain names which have told Google that they <em>always</em> want their website served over https.  If the user tries to manually request the insecure version, the browser won't let them. This means that a user's connection to, for example, their bank cannot be hijacked.  A dodgy WiFi network cannot force the user to visit an insecure and fraudulent version of a site.</p>

<p>After about a decade of use, the list is now 14MB in size, with around 130,000 entries in it.  You can <a href="https://source.chromium.org/chromium/chromium/src/+/main:net/http/transport_security_state_static.json">view the list online</a> or <a href="https://chromium.googlesource.com/chromium/src/net/+/refs/heads/main/http">download it</a>.</p>

<p>The format is relatively straightforward:</p>

<pre><code class="language-json">{
 "name": "example.com",
 "policy": "bulk-1-year",
 "mode": "force-https",
 "include_subdomains": true 
},
</code></pre>

<p>When the list is updated, <a href="https://source.chromium.org/chromium/chromium/src/+/main:net/tools/transport_security_state_generator/README.md?q=transport_security_state_static.json&amp;ss=chromium%2Fchromium%2Fsrc&amp;start=11">Chrome creates a trie with Huffman coding compression</a> - so it doesn't have to parse that monster file each time.</p>

<h2 id="a-rummage-inside"><a href="https://shkspr.mobi/blog/2024/01/a-quick-look-inside-the-hsts-file/#a-rummage-inside">A rummage inside</a></h2>

<p>The most popular (over 1,000 entries) TLDs / Public Suffixes are:</p>

<table>
<thead>
<tr>
  <th align="right">Rank</th>
  <th align="center">TLD</th>
  <th align="right">Entries</th>
</tr>
</thead>
<tbody>
<tr>
  <td align="right">1</td>
  <td align="center">com</td>
  <td align="right">43,236</td>
</tr>
<tr>
  <td align="right">2</td>
  <td align="center">tk</td>
  <td align="right">19,022</td>
</tr>
<tr>
  <td align="right">3</td>
  <td align="center">de</td>
  <td align="right">5,216</td>
</tr>
<tr>
  <td align="right">4</td>
  <td align="center">org</td>
  <td align="right">4,731</td>
</tr>
<tr>
  <td align="right">5</td>
  <td align="center">gov</td>
  <td align="right">4,507</td>
</tr>
<tr>
  <td align="right">6</td>
  <td align="center">net</td>
  <td align="right">4,410</td>
</tr>
<tr>
  <td align="right">7</td>
  <td align="center">ga</td>
  <td align="right">4,326</td>
</tr>
<tr>
  <td align="right">8</td>
  <td align="center">nl</td>
  <td align="right">2,671</td>
</tr>
<tr>
  <td align="right">9</td>
  <td align="center">cf</td>
  <td align="right">2,458</td>
</tr>
<tr>
  <td align="right">10</td>
  <td align="center">ml</td>
  <td align="right">2,271</td>
</tr>
<tr>
  <td align="right">11</td>
  <td align="center">co.uk</td>
  <td align="right">2,139</td>
</tr>
<tr>
  <td align="right">12</td>
  <td align="center">fr</td>
  <td align="right">1,714</td>
</tr>
<tr>
  <td align="right">13</td>
  <td align="center">ru</td>
  <td align="right">1,516</td>
</tr>
<tr>
  <td align="right">14</td>
  <td align="center">eu</td>
  <td align="right">1,283</td>
</tr>
<tr>
  <td align="right">15</td>
  <td align="center">com.br</td>
  <td align="right">1,226</td>
</tr>
<tr>
  <td align="right">16</td>
  <td align="center">gq</td>
  <td align="right">1,225</td>
</tr>
<tr>
  <td align="right">17</td>
  <td align="center">io</td>
  <td align="right">1,215</td>
</tr>
<tr>
  <td align="right">18</td>
  <td align="center">com.au</td>
  <td align="right">1,202</td>
</tr>
<tr>
  <td align="right">19</td>
  <td align="center">it</td>
  <td align="right">1,103</td>
</tr>
<tr>
  <td align="right">20</td>
  <td align="center">cz</td>
  <td align="right">1,004</td>
</tr>
</tbody>
</table>

<p>After <code>.com</code>, the free <code>.tk</code> domain names absolutely dominate. I wonder how many of them are fraudulent?</p>

<p>There are 2,676 <code>.uk</code> domain names - only 537 of which aren't on <code>.co.uk</code>.</p>

<p>Going a bit further, there are 418 IDNs (which start with <code>xn--</code>).</p>

<p>And about 187 have "porn" in the domain.</p>

<p>You can't really extrapolate <em>much</em> from this as a data set. Lots of the domains seem to have expired or otherwise no longer work. Reading around <a href="https://hstspreload.org"></a><a href="https://hstspreload.org">https://hstspreload.org</a> it notes that because this list is <em>hard-coded</em> into Chrome it can take months before a site is added. Similarly, removal can take a long time as well.</p>

<p>I can't help feeling that there should be a better way to manage all this though.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49041&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/01/a-quick-look-inside-the-hsts-file/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[An open(ish) redirect on Mastodon]]></title>
		<link>https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/</link>
					<comments>https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 30 Oct 2023 12:34:51 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[Responsible Disclosure]]></category>
		<category><![CDATA[security]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=47287</guid>

					<description><![CDATA[I&#039;ve responsibly disclosed a small security issue with Mastodon (GHSA-8982-p7pm-7mqw). It allows a sufficiently determined attacker to use any Mastodon instance to redirect unwary users to a malicious site.  What do you think happens if you visit: https://mastodon.social/@PasswordReset/111285045683598517/admin?  If you aren&#039;t logged in to that instance, it will redirect you to a 3rd party site.…]]></description>
										<content:encoded><![CDATA[<p>I've responsibly disclosed a small security issue with Mastodon (<a href="https://github.com/mastodon/mastodon/security/advisories/GHSA-8982-p7pm-7mqw">GHSA-8982-p7pm-7mqw</a>). It allows a sufficiently determined attacker to use <em>any</em> Mastodon instance to redirect unwary users to a malicious site.</p>

<p>What do you think happens if you visit:
<a href="https://mastodon.social/@PasswordReset/111285045683598517/admin"></a><a href="https://mastodon.social/@PasswordReset/111285045683598517/admin">https://mastodon.social/@PasswordReset/111285045683598517/admin</a>?</p>

<p>If you aren't logged in to that instance, it will redirect you to a 3rd party site. Try opening it in a private browser window.</p>

<p>Here's another, less convincing, demo:</p>

<p><a href="https://mastodon.social/@mastodonopenredirect.wordpress.com@mastodonopenredirect.wordpress.com"></a><a href="https://mastodon.social/@mastodonopenredirect.wordpress.com@mastodonopenredirect.wordpress.com">https://mastodon.social/@mastodonopenredirect.wordpress.com@mastodonopenredirect.wordpress.com</a>
(You will need to not be logged in to Mastodon.Social for this to work.</p>

<p>It is possible to craft a URl which will redirect any visitor who isn't logged in. Attackers can use this as an open redirect for phishing, spam, and other attacks.</p>

<h2 id="remediation"><a href="https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/#remediation">Remediation</a></h2>

<p>This will likely be fixed by <a href="https://github.com/mastodon/mastodon/pull/26917">#26917</a>. But, in the meantime, administrators of Mastodon instances should be aware that their site could be used as an open redirect.</p>

<p>If you do spot any accounts which appear to be dodgy, admins can either block the account or the entire domain.</p>

<h2 id="background"><a href="https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/#background">Background</a></h2>

<p>Here's how it works - which involves some necessary background detail.</p>

<p>I am user <code>@edent</code> on Mastodon.social.  I can send you a URl of <a href="https://Mastodon.Social/@edent"></a><a href="https://Mastodon.Social/@edent">https://Mastodon.Social/@edent</a> and you will see my profile.  Nice!</p>

<p>But there are lots of Fediverse servers out there.  For example, I run a little bot called <code>@colours</code> on the BotsIn.Space instance.  Its URl is <a href="https://BotsIn.Space/@colours"></a><a href="https://BotsIn.Space/@colours">https://BotsIn.Space/@colours</a> - simple.</p>

<p>But what happens if I am viewing the Colours bot while on Mastodon.Social?</p>

<p>The interface shows <a href="https://Mastodon.Social/@colours@BotsIn.Space"></a><a href="https://Mastodon.Social/@colours@BotsIn.Space">https://Mastodon.Social/@colours@BotsIn.Space</a> - if you are logged in to Mastodon.Social, you will see the colours account, you can follow it, reply to it, and interact with it as though it were a user on your home instance.</p>

<p>But what if you're <em>not</em> logged in?</p>

<p>If you visit <a href="https://Mastodon.Social/@colours@BotsIn.Space"></a><a href="https://Mastodon.Social/@colours@BotsIn.Space">https://Mastodon.Social/@colours@BotsIn.Space</a> you will be <em>immediately</em> redirected to <a href="https://BotsIn.Space/@colours"></a><a href="https://BotsIn.Space/@colours">https://BotsIn.Space/@colours</a></p>

<p>In theory, this is a good thing! You get taken to their home server and you can see their latest updates etc.</p>

<p>Unfortunately, this can be abused.</p>

<p>Try and visit <a href="https://botsin.space/@blog@shkspr.mobi"></a><a href="https://botsin.space/@blog@shkspr.mobi">https://botsin.space/@blog@shkspr.mobi</a> - if you are not logged in to BotsIn.Space, you will be automatically redirected to my blog.</p>

<p>In addition, Mastodon <em>ignores</em> the <code>@username</code> when it sees a local status ID which references an external status. For example, both of these URls will go to the same place:</p>

<ul>
<li><a href="https://mastodon.social/@colours@botsin.space/111323978746693908"></a><a href="https://mastodon.social/@colours@botsin.space/111323978746693908">https://mastodon.social/@colours@botsin.space/111323978746693908</a></li>
<li><a href="https://mastodon.social/@RandomLettersAnd1234/111323978746693908"></a><a href="https://mastodon.social/@RandomLettersAnd1234/111323978746693908">https://mastodon.social/@RandomLettersAnd1234/111323978746693908</a></li>
</ul>

<h2 id="impact"><a href="https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/#impact">Impact</a></h2>

<p>A malicious user could do a few things.</p>

<p>The first is spam evasion. Email out a link to <code>mastodon.social/@user@buy_illegal_puppies.com</code> and it might skip spam filters, or confuse the user about the true destination.</p>

<p>The second is phishing. Is a user going to notice that they've been silently redirected to <code>nnast0d0n.social</code>? Stick up a convincing "Please log in again" page and you can steal their credentials.</p>

<h2 id="why-this-works"><a href="https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/#why-this-works">Why This Works</a></h2>

<p>ActivityPub uses the Well-Known / WebFinger specification.  Mastodon will use this to find data on anything which looks like a username.</p>

<p>For example, here's what my blog's account looks like in WebFinger:
<a href="https://shkspr.mobi/blog/.well-known/webfinger?resource=acct:blog@shkspr.mobi"></a><a href="https://shkspr.mobi/blog/.well-known/webfinger?resource=acct:blog@shkspr.mobi">https://shkspr.mobi/blog/.well-known/webfinger?resource=acct:blog@shkspr.mobi</a>:</p>

<pre><code class="language-json">{
  "subject": "acct:blog@shkspr.mobi",
  "aliases": [
    "https://shkspr.mobi/blog/@blog"
  ],
  "links": [
    {
      "rel": "self",
      "type": "application/activity+json",
      "href": "https://shkspr.mobi/blog/@blog"
    },
    {
      "rel": "http://webfinger.net/rel/profile-page",
      "type": "text/html",
      "href": "https://shkspr.mobi/blog/@blog"
    }
  ]
}
</code></pre>

<p>Mastodon will check that account exists, and then redirect a non-logged-in user to the "profile-page" of an account that it finds.</p>

<p>So a malicious user can create a WebFinger at <code>evil.com</code>, then send out links to <code>mastodon.example/@SexyFunTimes@evil.com</code>, and have users instantly redirected to their site.</p>

<p>Most ActivityPub instances won't do this unless they've <em>already</em> seen the user being referenced. This can be achieved by sending a private message to a user on that server which mentions the redirection account.</p>

<h2 id="remediation"><a href="https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/#remediation">Remediation</a></h2>

<p>Given that it is sensible to redirect users to an account's home instance, I think there's really only one way to solve this. An annoying interstitial.</p>

<blockquote><p>You are leaving XYZ.social. We do not control the page Illegal_Ivory_Smuggling.com. If you are sure you want to proceed, click here. Do not share your username and password with 3rd party sites etc etc etc.</p></blockquote>

<p>I reported this to Mastodon on 2023-09-20. Apparently a number of other people have also reported it. While they work on how to fix the problem, I thought it was sensible to let people know that this attack was possible.</p>

<h2 id="timeline"><a href="https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/#timeline">Timeline</a></h2>

<ul>
<li>2023-09-20 Disclosed on GitHub</li>
<li>2023-10-22 Added more details and sought agreement to publish</li>
<li>2023-10-29 Checked with various independent Mastodon server admins to see if they were aware of this behaviour - most were not</li>
<li>2023-10-30 Published</li>
</ul>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=47287&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/10/an-openish-redirect-on-mastodon/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Using disposable phone numbers for better security]]></title>
		<link>https://shkspr.mobi/blog/2023/09/using-disposable-phone-numbers-for-better-security/</link>
					<comments>https://shkspr.mobi/blog/2023/09/using-disposable-phone-numbers-for-better-security/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 07 Sep 2023 11:34:10 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[sim]]></category>
		<category><![CDATA[virtual sim]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=46712</guid>

					<description><![CDATA[Last night I received a call from my bank. They&#039;d detected an unusual transaction and wanted to make sure that it was legitimate. Had I recently purchased £10,000 worth of crypto in the Maldives? What?!!? No! ARGH!  I started to panic. All my apes money gone!  No. Wait. The other thing. I knew it was a scam from the moment &#34;James from your bank&#039;s fraud team&#34; started his patter.  You see, I have …]]></description>
										<content:encoded><![CDATA[<p>Last night I received a call from my bank. They'd detected an unusual transaction and wanted to make sure that it was legitimate. Had I recently purchased £10,000 worth of crypto in the Maldives? What?!!? No! ARGH!</p>

<p>I started to panic. All my <del>apes</del> money gone!</p>

<p>No. Wait. The other thing. I <em>knew</em> it was a scam from the moment "James from your bank's fraud team" started his patter.</p>

<p>You see, I have <em>multiple</em> phone numbers. And "James" called me on a number which isn't tied to my bank. So I strung him along for half an hour or so pretending to move my money into a safe account, taking ages to wait for an SMS confirmation code, accidentally reading out the wrong one, before cursing him and his lineage even unto the seventh generation. He, in return, introduced me to a whole new range of swearwords from his native tongue and hung up on me. Charming!</p>

<p>Here's how it works, and how you can use this trick.</p>

<p>The easiest way is to get a dual SIM phone. I have my main SIM which holds my primary phone number. That's the number used by my family, my banks, and anyone vaguely trustworthy. My second SIM contains <a href="https://shkspr.mobi/blog/2021/06/data-is-getting-too-cheap-to-meter/">a free disposable PAYG SIM</a>.</p>

<p>That phone number gets given out to retailers, couriers, Gumtree &amp; Freecycle users, pizza delivery, and anyone else who doesn't need my number <em>permanently</em>.</p>

<p>My Android phone tells me which line is being called. And once I start getting too much spam to that number I throw the SIM away and get a new one with a new number.</p>

<p>If you have an eSIM, you can do the same thing. Find a cheap eSIM provider, sign up to a PAYG or monthly plan, then ditch the number whenever you like.</p>

<p>Or, you can <a href="https://shkspr.mobi/blog/2020/07/adding-sip-calls-to-android-for-free/">set up a SIP calling plan</a>. Install an app and have calls automatically routed to you.</p>

<p>About a dozen years ago, I worked with a UK mobile network to develop "disposable" phone numbers. The idea was that we would partner with, say, a dating app and generate a new phone number for you. Your date could call and text you without you having to reveal your real number. If they turned out to be a jerk, you could revoke the phone number immediately.</p>

<p>The same tech could work for hiring a plumber, getting a takeaway, or a hundred different use cases.  The plan was to have an app which displayed a push-notification telling you which number was being called - so you knew if it was from your temporary lover or the person picking up your old sofa.</p>

<p>Sadly, the demo never went anywhere. It's a pity. I'd love to have a SIM with multiple disposable numbers.</p>

<p>But, for now, give out your temporary number to people who don't need a way to permanently contact you. Better safe than sorry!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=46712&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/09/using-disposable-phone-numbers-for-better-security/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Please don't give away your Twitter API keys to Cloudinary]]></title>
		<link>https://shkspr.mobi/blog/2023/07/please-dont-give-away-your-twitter-api-keys-to-cloudinary/</link>
					<comments>https://shkspr.mobi/blog/2023/07/please-dont-give-away-your-twitter-api-keys-to-cloudinary/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 23 Jul 2023 11:34:03 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[twitter]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=46362</guid>

					<description><![CDATA[My CDN just asked me for all my Twitter API keys...    WTF? This would give them complete access to my app&#039;s Twitter account, the ability to send and receive messages, and anything else that my API key allows.  Giving them - or anyone - the entire set of credentials would be a very bad idea.  What&#039;s going on?  Twitter&#039;s slow-motion collapse and hostility to developers is causing a whole bunch of…]]></description>
										<content:encoded><![CDATA[<p>My CDN just asked me for <em>all</em> my Twitter API keys...</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/07/a0e21f3132883ee0.png" alt="Hi Terence, We don't have a way for customers to configure this on their own currently. Our team will handle the configurations for you. Here are the details needed for us to do the required changes: API Key and Secret. Access Token and Secret.Best Regards" width="1000" height="264" class="aligncenter size-full wp-image-46363">

<p>WTF? This would give them <em>complete</em> access to my app's Twitter account, the ability to send and receive messages, and anything else that my API key allows.</p>

<p>Giving them - or anyone - the <em>entire</em> set of credentials would be a <u><em><strong>very</strong></em></u> bad idea.</p>

<p>What's going on?</p>

<p>Twitter's slow-motion collapse and hostility to developers is causing a whole bunch of second-order effects.</p>

<p>Lots of services let people log in to them using Twitter. It is (was?!) a quick way to do identity management without having to bother the user with a separate username and password.  Once someone has logged in, it's nice to be able to show their user avatar.</p>

<p>Annoyingly, Twitter never had a simple solution for that. You couldn't take my username - <code>edent</code> - and then grab <code>twitter.com/edent/avatar.jpg</code>. Instead, you had to perform an API call to get the image.</p>

<p>So a whole bunch of services started up which would retrieve Twitter avatars based on username. And they also did the same for Facebook, GitHub, Google, and lots of other OAuth providers.</p>

<p>I was using <a href="https://cloudinary.com/documentation/social_media_profile_pictures">Cloudinary's Social Media Profile Pictures feature</a>. But with Twitter's complete inability to serve API users, that functionality is going away.</p>

<p>Last week Cloudinary said that they could keep the functionality going <em>if</em> I was willing to provide my API keys. I (somewhat impolitely) complained to Cloudinary that them asking for all the API keys was a security nightmare.  They responded (politely) to my points:</p>

<blockquote><p>I completely understand your concerns. Twitter's recent limitations to their API have made it so that we are unable to continue to use our own API credentials to allow customers to fetch Twitter assets, and so we must implement customer credentials instead. We were working on a long-term resolution, but they cut off our API access without warning, and so the temporary solution to minimise disruption was to request your credentials so our backend team can add in a rule to run your Twitter API requests with your own credentials. As such, we require both the API key and secret, along with the access token and secret.</p>

<p>...</p>

<p>As mentioned, this is just a temporary measure in order to ensure continued delivery of your assets. If you prefer to wait until we have a customer-facing portal to enter your Twitter account credentials, then you are certainly welcome to do so. Unfortunately I don't have an ETA on when such a solution might be available, however we will do our best to keep you updated.</p></blockquote>

<p>I understand that they don't want users to have a degraded experience. And I understand that Twitter have screwed them over. And I'm sure that they're a thoroughly trustworthy company who will never get hacked. But asking customers to fatally compromise their own security like that is <em>not</em> acceptable.</p>

<h2 id="cant-you-just"><a href="https://shkspr.mobi/blog/2023/07/please-dont-give-away-your-twitter-api-keys-to-cloudinary/#cant-you-just">Can't you just...?</a></h2>

<p>Twitter doesn't offer a <strong>stable</strong> avatar service. Users can change their profile picture at any time, and the URl to the old image stops working. So caching the profile picture URl often leads to a broken image.  Caching an old image can mean showing something outdated.</p>

<p>The <a href="https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-by-username-username">API rate limits</a> are pretty small for any service with heavy traffic.</p>

<p>Not showing a user image - or just the Twitter icon - <em>could</em> work. But it makes for a pretty crappy experience.</p>

<p>Telling everyone to leave Twitter and join Mastodon would be nice.</p>

<p>Creating a bespoke read-only API key could work - but Twitter now limits the number of apps a develop can have unless they pay stupid money.</p>

<p>It is entirely understandable that people would panic and hand over the keys to their (digital) kingdom. Fear makes people do dangerous things.</p>

<p>Anyway, this Mastodon post sums it up the best:</p>

<blockquote class="mastodon-embed" data-embed-url="https://beige.party/@RickiTarr/110755339125618925/embed" style="background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;"> <a href="https://beige.party/@RickiTarr/110755339125618925" target="_blank" style="align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 79 75"><path d="M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z" fill="currentColor"></path></svg> <div style="color: #787588; margin-top: 16px;">Post by @RickiTarr@beige.party</div> <div style="font-weight: 500;">View on Mastodon</div> </a> </blockquote>

<script data-allowed-prefixes="https://beige.party/" async="" src="https://beige.party/embed.js"></script>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=46362&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/07/please-dont-give-away-your-twitter-api-keys-to-cloudinary/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[An eInk, Wrist-Mounted, TOTP Generator]]></title>
		<link>https://shkspr.mobi/blog/2023/07/an-eink-wrist-mounted-totp-generator/</link>
					<comments>https://shkspr.mobi/blog/2023/07/an-eink-wrist-mounted-totp-generator/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 08 Jul 2023 11:34:09 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[2fa]]></category>
		<category><![CDATA[arduino]]></category>
		<category><![CDATA[eink]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[watchy]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=46213</guid>

					<description><![CDATA[Behold! Thanks to the power of the Watchy development platform, I now have all my 2FA codes available at the flick of my wrist!    HOWTO  This uses Luca Dentella&#039;s TOTP-Arduino library.  You will need a pre-shared secret which is then converted into a Hex array. Use the OTP Tool for Arduino TOTP Library to get the Hex array, Base32 Encoded Key, and a QR Code to scan into your normal TOTP…]]></description>
										<content:encoded><![CDATA[<p>Behold! Thanks to the power of the <a href="https://shkspr.mobi/blog/2023/06/review-watchy-an-eink-watch-full-of-interesting-compromises/">Watchy</a> development platform, I now have all my 2FA codes available at the flick of my wrist!</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/07/Wrist-TOTP.jpg" alt="A chunky wristwatch showing the time and a selection of 6 digit codes and their corresponding entities." width="1024" height="1024" class="aligncenter size-full wp-image-46214">

<h2 id="howto"><a href="https://shkspr.mobi/blog/2023/07/an-eink-wrist-mounted-totp-generator/#howto">HOWTO</a></h2>

<p>This uses <a href="https://github.com/lucadentella/TOTP-Arduino/">Luca Dentella's TOTP-Arduino library</a>.</p>

<p>You will need a pre-shared secret which is then converted into a Hex array. Use the <a href="https://www.lucadentella.it/OTP/">OTP Tool for Arduino TOTP Library</a> to get the Hex array, Base32 Encoded Key, and a QR Code to scan into your normal TOTP generator.</p>

<p>Add the Hex array into the code below.</p>

<p>To check that it is functioning correctly, either scan the QR code from the OTP Tool above, or use the Base32 Encoded Key with <a href="https://totp.danhersam.com/">an online TOTP generator</a>.</p>

<p>Here's how the code interfaces with the Watchy:</p>

<pre><code class="language-c">#include &lt;Watchy.h&gt; //include the Watchy library
#include "settings.h"
#include "sha1.h"
#include "TOTP.h"

class MyFirstWatchFace : public Watchy{ //inherit and extend Watchy class
    public:
        MyFirstWatchFace(const watchySettings&amp; s) : Watchy(s) {}
        void drawWatchFace(){

          ...

          RTC.read(currentTime);
          time_t epoch = makeTime(currentTime) - 3600; // BST offset


          // The shared secret - convert at https://www.lucadentella.it/OTP/
          uint8_t hmacKey[] = {}; // e.g. {0x4d, 0x79, 0x4c, 0x65, 0x67, 0x6f, 0x44, 0x6f, 0x6f, 0x72};
          int hmacKeyLength = sizeof(hmacKey) / sizeof(hmacKey[0]);

          TOTP totp = TOTP(hmacKey, hmacKeyLength);
          char* epochCode = totp.getCode( epoch );

          display.print(  "TOTP Code Twitter: ");
          display.println( epochCode );

          ...
</code></pre>

<p><a href="https://gitlab.com/edent/watchy-faces/-/tree/main">You can grab the full code from GitLab</a>.</p>

<p>I'm not very good at C++ - so please let me know what terrible mistakes I've made.</p>

<h2 id="is-this-a-good-idea"><a href="https://shkspr.mobi/blog/2023/07/an-eink-wrist-mounted-totp-generator/#is-this-a-good-idea">Is this a good idea?</a></h2>

<p>Well... Yes and no.</p>

<p>TOTP is a strong-ish form of Multi-Factor Authentication. It helps prevent attacks where someone already knows your username and password.  Having a convenient way to get your TOTP codes may make you more likely to use them. It also prevents you from getting locked out of your accounts if your phone dies or is stolen.</p>

<p>Convenient security is <em>good</em> security.</p>

<p>But... Having them on your wrist for everyone to see? I've deliberately made the font as small as I can so it is only readable up close. However, if someone is shoulder-surfing your details, they may well see your wrist. The watch isn't encrypted - so even if you hid the codes behind a button press, anyone who steals your watch will have your codes. If they steal your phone, they need to get through your PIN / biometrics.</p>

<p>Who are your adversaries? If you are trying to evade state-level actors, thieves specifically targeting you for your crypto-holdings, or an untrustworthy spouse - this probably isn't a great idea.  If you don't use 2FA because you don't keep your phone with you - this will probably increase your security posture.</p>

<p>Ultimately, all security measures are a trade-off between convenience and control.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=46213&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/07/an-eink-wrist-mounted-totp-generator/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>
