Introduction
In mid-April, I posted about TOTP, detailing how it works and how to generate a shared secret and a six-digit TOTP code from that shared secret. I included the following information:
When you set up Two-Factor Authentication (2FA), some sites will give you an option to get the text secret. It may be hidden behind some link that says “unable to scan QR?” or “use another authentication app”. The account providers do not always offer this option readily. The account providers don’t always make this option obvious. However, with a little digging (like clicking links labeled ‘Can’t scan QR?’) you can often access the shared secret. Alternatively, if you have access to on-screen QR code readers on your Windows or Mac machine, you may be able to extract the shared secret.
This kicked off the investigation for today’s post.
Currently, QR Code readers are integrated into both Android and iOS systems. Additionally, applications such as Google Authenticator request camera access to scan and import QR codes, particularly for two-factor authentication (2FA) codes provided by many websites. These websites generally expect users to utilize their mobile devices to scan these codes. I believe the original intent was likely for users to employ their smartphones as secondary devices separate from their laptops or desktop computers, thereby adding an extra layer of security through the requirement of additional hardware for authentication. However, this is less applicable today, as many smartphone applications also utilize 2FA codes, leading users to switch between apps on the same device. Consequently, the advantage of using two separate devices has largely diminished.
Google Authenticator does have a “Privacy Screen” setting that will require some sort of secondary authentication (such as Face ID or device PIN) to unlock these authenticator codes, which mitigates risk if your phone is lost or stolen and lock screen is somehow defeated. Additionally, in terms of backup, Google Authenticator allows for the information to be backed up into your Google Account. You can alternatively export these by using a series of QR Code scans from one device to another.
I personally do not feel comfortable having these “shared secrets” that Google Authenticator uses stored inside my Google Account (“in the cloud”). This isn’t a knock on Google either, their security is overall, very solid. Additionally, I employ secure methods such as a hardware key for my account. However, if my Google Account is ever compromised by whatever insanely clever zero-day exploit is discovered in the future, this would give the bad actor access to essentially every other high-value account I may have. Therefore, it may make sense to “intercept” these shared secrets on your own and store in a more secure manner of your choosing. Please note the emphasis on “secure manner” – as this is very important. I won’t advocate which method to use here. Personally, one option may be to back up these secrets on a secure VeraCrypt container on a USB Drive which itself is physically stored in a secure location. Personally, I’d prefer this method of “off-siting” over storing these in my Google Account.
But, as mentioned above – not every site will give you the textual “shared secret”. This necessitates the use of a screen-based QR Code reader if you are working on a Macbook or a Windows Workstation.
I’m sometimes ignorant of features that either Microsoft or Apple will quietly add into their operating systems. I figured it was worth a quick check to see if either had added a built-in QR code reader into desktop/laptop operating systems.
I wouldn’t be shocked if my limited search on the baked-in options for QR code reading was in error; if anyone knows of a solution that’s built into the OS for either Windows or Mac, please let me know in comments below. But based on the answers above, it seems like both operating systems require some other hardware (such as a webcam on windows, or an iPhone on Mac).
Is this kind of utility/development helpful to you?
Consider a one-time tip to sonnik.
Previously, I had used CodeTwo’s QR Code Desktop Reader & Generator. CodeTwo’s QR Code Desktop Reader is still usable, but deprecated. I’m not sure how long it’ll keep working. I’m sure that there are many options in the app store, but I’m not sure what kind of privacy compromises, advertising, or costs any of these have. Again, if someone has a good modern 3rd party solution that they like, feel free to comment below.
Therefore, I decided to come up with some more Python code to read or generate QR codes.
Design and Development
Both the Mac and Windows version of this Python code use tkinter to offer simple user interfaces for either the Explorer or Finder UI planes. I hadn’t used tkinter before; I’m more comfortable using C#/WinForms for these kinds of solutions on Windows. Fortunately, the requirements for a graphical UI aren’t too elaborate. We simply need to be able to prompt for the user’s desired action and display some results.
def ask_mode_and_dispatch():
"""
Asks the user to choose between capturing a QR code from the screen or generating one from text.
:return:
"""
def choose(mode_choice):
win.destroy()
if mode_choice == "capture":
ScreenSnip()
elif mode_choice == "generate":
generate_qr()
win = tk.Tk()
win.title("QR Tool - Select Mode")
win.geometry("300x120")
win.resizable(False, False)
label = tk.Label(win, text="Choose an action:", font=("Arial", 12))
label.pack(pady=10)
btn_frame = tk.Frame(win)
btn_frame.pack(pady=5)
capture_btn = tk.Button(btn_frame, text="Capture QR from Screen", width=25, command=lambda: choose("capture"))
capture_btn.grid(row=0, column=0, padx=5)
generate_btn = tk.Button(btn_frame, text="Generate QR from Text", width=25, command=lambda: choose("generate"))
generate_btn.grid(row=1, column=0, padx=5, pady=5)
win.mainloop()
def show_text_window(self, text):
"""
Displays the decoded text in a new window.
:param text:
:return:
"""
win = tk.Tk()
win.title("Decoded QR Text")
win.geometry("400x200")
textbox = tk.Text(win, wrap='word')
textbox.insert(tk.END, text.strip())
textbox.pack(expand=True, fill='both')
textbox.focus_set()
textbox.bind("<Escape>", lambda e: win.destroy())
win.mainloop()
Differences Between the Two Versions
There were two major differences between the Mac and Windows versions.
The first difference was the method for interfacing with the clipboard. I wanted the generated QR code to be copyable to the clipboard. In the Windows version, this is handled by way of pywin32. The Mac version uses Appkit’s NSPasteboard. I tested this on both Windows and Mac by posting a generated QR Code into an empty Microsoft Word document.
The second difference was the screen capture methodology. In the Windows version, PIL (Python Imaging Library) offers ImageGrab (used in conjunction with with a rough tikinter rectangle drag-the-rectangle method). This works on Windows, but I faced two challenges with it on Mac. The first issue was that Mac OS Sequoia had a few permissions considerations for “screen reading”. Once I granted my development environment (PyCharm) access through system settings, I faced the next challenge. The second appeared to be that the alpha (transparency) when drawing your capture rectangle didn’t work, meaning the whole screen would go black and you’d just capture a black image. ChatGPT suggested the workaround of using the “screencapture” binary provided by Mac OS. This approach isn’t as exciting as the Windows solution, but it does work.
def capture_and_decode_qr():
"""
Obtains QR code and decodes it
:return:
"""
tmpfile = "/tmp/qr_snap.png"
subprocess.run(["screencapture", "-i", "-x", tmpfile])
try:
img = Image.open(tmpfile)
except FileNotFoundError:
return # User canceled
result = decode(img)
text = ""
if result:
for obj in result:
text += obj.data.decode('utf-8') + "\n"
else:
text = "No QR code found."
show_text_window(text)
For Mac, I also needed to complete a brew installation for zbar (bar code reader library).
brew install zbar
Once the process is complete, you’ll need to export the location
export DYLD_LIBRARY_PATH=/opt/homebrew/opt/zbar/lib:$DYLD_LIBRARY_PATH
Note: The above is for Apple Silicon, for older Intel-based systems, the export path should be
/usr/local/opt/zbar/lib
Making it Easy to Use
For the Windows version, you can easily create a batch file or a shortcut that will trigger a run.
pythonw "C:\path-to-code\QR-Reader\qr-reader-win.py"
Obviously replacing “path-to-code” accordingly. Using pythonw.exe should eliminate the unneeded terminal window that will pop up if just using the terminal based python.exe. If you are using a shortcut, you can use an icon of your choosing.
I’ve supplied a few different Icon formats in the Git project under QR-Reader/icons. (The QR code in the provided icon is the URL for “the sonnik chronicles”)
I didn’t do this in my testing Mac version, but you could create a bash script/command file (set the executable flag), and then associate an icon to the bash script. Apparently, Automator on the Mac OS has similar functionality, but I’ve never used that.
Source Code / Git
The repository is located here. If you’d rather have direct links to just the Python code or icons:
Conclusion
If you’ve used something like this before, or you’ve got a better method—drop it in the comments below. I’m especially curious if anyone’s found a fully native desktop solution that doesn’t require third-party installs or workarounds. Likewise, if you try out this script and run into issues (or want to adapt it), I’d love to hear how it goes.