Passwords Part Two
I spent the past several weeks designing & implementing my own password manager.
For context on that, take a look at Password Managers. Under the heading Security, I noted that I would revisit this in-depth after working through the project.
I’ve decided not to use my own online password manager. This post explains what I found & why I’ve decided that it’s not more secure for me.
Introduction & Methodology
To maintain unambiguous compliance with the Computer Fraud and Abuse Act (CFAA), I utilized only systems over which I had complete ownership & control. No third party systems were tested, penetrated or analyzed during this exercise.
The term Password Manager refers to an online service. When referring to an offline password manager, that will be specified in-context.
I’ll introduce & briefly analyze my dimensions of interest. Each will be scored with a +1, -1 or 0 to indicate whether a PM is less secure (-1), more secure (+1), or neither (0) along that dimension. A net positive score is better than a net negative score. A net zero score would indicate that PMs are neither more nor less secure than offline methods.
Any risk discussed must present an observable, measureable metric. I’m not interested in “…this feels like [a]” or “…I didn’t like [b]“.
Let’s dig in with some basics.
For a password manager (PM) to provide the promised convenience of “just working”, it must implement a mechanism to intercept & inspect pages for login forms, determine whether the system holds credentials to a site (i.e. a login page) and then present those credentials to the remote site. In cases where a visited page does contain a login form but no matching credentials exist in the PM, the PM must present a way for the user to supply the necessary credentials, which the PM will both present to the remote site as well as store for use in an automated, fast login on subsequent visits.
That functionality is implemented in browser extensions. Each major browser engine has its own implementation of extensions. For my exercise, I created a Chrome extension which uses the Blink engine. While my experiences had some Blink-specific elements, you would expect to encounter similar issues targeting either WebKit or Gecko.
Anything you do in a browser - visit a site, follow a link, scroll, login to a service - occurs in the window object of the browser. Loosely, this is the parent of three extension execution worlds that are isolated from each other. The three worlds are:
- Content scripts
- the extension itself
- the web page
In theory, these three worlds are isolated. None has access to the variables or functions created in any of the others. This is due to scope (blah blah etc.) & generally works well.
There is at least one scenario (and really, probably more) where a variable that’s used in one world leaks to the window.
The window is the parent, remember? The parent (again, loosely) exposes all its variables and functions to the children. This is usually a good thing, but in the case of extensions, it is Very Bad.
Leaked variables create an attack vector. Through use of
eval(), a leaked variable can be appended to the DOM instantly taking over a privileged script that is injected into the web page you’re visiting.
I’m grossly over-simiplying the technical explanation here, but this exact issue was found in LastPass’ browser extension in 2017. It’s well documented (and fixed) today, but it’s a great example of how easily scope and isolated worlds can behave in ways that your design expectations & assumptions didn’t include.
None of this is in play when offline PM is used.
Security in Transit
You still have to engage a PM over the wire - that is, via a connection that traverses the public internet.
PM’s don’t present any particular new challenge here. Your traffic to & from the PM must be securely handled in the same way as your email or online banking systems.
While it may be a higher-value target for sniffing, PM-bound traffic that utilizes HTTPS (encrpyted web traffic) is simply table stakes. There’s nothing about a PM that makes using HTTPS more susceptible to sniffing, or upon interception, cracking.
There is a risk here when transmitting my own passwords across the wire involving both MiTM’ing my requests to remote sites as well as the remote sites’ responses. This risk assumes that an attacker could plausibly inject themselves into a req/res block or somewhere more fundamental to the communication flow. I’m exploring that in the book I’m currently writing.
Stated plainly, data in transit is always at risk.
This is one of the more interesting things that a PM provides. It generates a unique password for your identity on the remote services it manages.
When you engage a PM (at least the most popular ones), it will first authenticate you to the PM service itself, then allow you to generate passwords for third party sites that you want to automatically login to later. This is the convenienece factor that I discussed in the original post.
The password that the PM creates for you will only exist in the PM system - you will not store it nor will you ever even see it. The PM will use some form of seeded-hash to generate a password for you which will then be encrypted.
Here’s the interesting part: that encryption uses a passcode that you possess. It doesn’t exist in the PM’s system anywhere and is used solely during the encryption event. Implementation details may vary, but generally this encryption occurs in the user’s browser. Upon completion, the encrypted password is transmitted to the PM where it is stored in a table.
Did you catch that?
The encryption process involves using the classic
hash() function, but with a twist: the passcode is never present on the PM system. This means that if an attacker did gain access to the PM’s system, they would have… a bunch of encrypted strings that were essentially useless. (math geeks: yes, you could point a Cray at this for 10,000 years or whatever and probably decrypt some of the passwords).
If a typical user did encrypt their own passwords using a locally-managed passcode, then there would be no appreciable advantage in using a PM. That’s not (of course) the behavior of a typical user and is part of why the TAM for PMs is so large.
The credential creation process is clever and assumes that the identities paired to the encrypted passwords are masked or otherwise unmatchable to discreet strings.
In order to present a set of credentials to a remote site that you’re visiting, the PM must store those credentials somewhere. There are only two places where this can occur:
- at the PM service
- on your local filesystem
At the PM Service
This is the most common storage location. A database table will store the site name and the encrypted credentials for a user. This can be one giant table or hundreds of thousands of tables. It doesn’t matter for this discussion.
What matters is that your credentials - the keys to your account at [n] services - exists at a remote location that is 100% outside of your control. You have no control over (or perhaps even visibility into) the access controls that the PM has in place. You don’t know how well - if at all - they respond to power outages or other Acts of God that can take services offline.
If the PM has the only “copy” of your password for the dozens or hundreds of sites you use, what happens if they experience a catastrophic loss of systems? What’s their Disaster Recovery plan? How long will it take for you to regain access to your stored passwords?
Does the PM provide their own data center or are they using one of the big three - AWS, Google or Microsoft? What if they’re using another service? Does that create more risk for you?
Accepting these risks requires that you understand exactly what’s in place at your PM. This may not be easy to determine.
Managing your own passwords offline does not include these risks. If you store your master password list in your safe (I do), then a series of catastrophic & cascading failures would have to occur in order for any passwords to be lost.
On your Local Filesystem
Some PM Chrome extensions utilize local storage. This is a directory structure on your computer where the browser can store files. This allows subsequent uses of the extension to rapidly locate & consume passwords when you visit sites for which you have previously stored a password.
The security model here has to deal with risk along two sub-dimensions:
a) physical access to the computer and
b) browser behavior across all sites that you visit after first storing any password
Physical security is easy to understand. If you control access to the device, you are by definition controlling who has access to stored passwords.
Browser behavior is significantly more difficult to understand. Recall the isolated worlds exploits? There are undoubtedly exploits that would allow an attacker to gain access to local storage.
These type of exploits are impossible to describe, as general knowledge of them doesn’t typically exist until after a big hack in which they are… hey… exploited.
One aspect of credential storage that must be noted is that you - as the user of various services to which you authenticate using passwords, no matter where they are stored - are subject to whatever practices those remote services have in place for storing your credentials.
Imagine you have a password string of “abc123” for some site and that it is stored encrypted in the remote service’s system. If an attacker compromises the remote system & obtains your encrypted password, they can’t do anything immediately useful with it.
The attacker would need to supply the incoming raw password - “abc123” - which would be (in a minimal scheme) hashed with a passcode in real-time, producing the encrypted password. Once obtained, that encrypted password could be queried in the service’s member table, and if found, used to map & provide access to protected resources.
But wait - if the attacker has your encrypted password and that is what’s stored in the service’s credentials table, can’t they just supply that to login as you?
Well, probably not. If the remote service has a login flow that includes hashing an incoming password prior to querying for that resultant encrypted password, it would require the attacker to inject the captured encrypted password into that login flow. This is difficult, but not impossible. It’s a variation of MiTM and requires access to the login workflow that occurs after a password is presented. This isn’t kiddie-script stuff.
If you are engaging with a system that does not encrypt passwords prior to storage, you are effectively swinging in the breeze here. Your password - even the super-secret, encrypted one that the PM created for you - will be stored in plain-text on the remote system. When that system is compromised, your encrypted password that you paid to have generated for you will be visible to the attacker. The attacker can then use it as though they are you.
This risk exists whether you control & present a password to remote sites or a PM does it on your behalf. The risk of interception exists at the handoff of the incoming password to the remote site.
In summary, using an online PM represents more measurable risk to me than my offline password lists.