Obviously there are exceptions
Prediction: in 10 years nearly everyone will be using a password manager; it will come with their OS (Android or iOS) with browser plugins for other OS’s, and the integration with mobile apps and mobile web will be so tight that people will not even realize they are using passwords, most of the time.
Apple just massively revamped their own manager in the latest iOS release. They already have pretty good integration with mobile web and with App Store apps.
In the next couple of years I expect to see pw manager integration made a firm requirement for App Store apps, and I expect to see web standards for account signup and login that make pw managers reliable.
I suspect Google will follow suit although I am not familiar with Android’s capabilities in that area.
So in a few years you will not type an email address and password to sign up for things; the OS will prompt you: “foo.com is asking you to sign up, would you like to do this automatically?” and if you respond in the affirmative you’ll get a site-specific email address and password automatically created and stored for you, and that will be used whenever you want to log in. Recovery will shift to a mobile account centric workflow (Apple ID or Google account) rather than email based password reset links.
If a data breach is reported the pw manager app can notify you and give you a one-button-click experience to reset your password.
The downside is that if you get canceled by Apple or Google it will be a special kind of hell to recover.
And then realize that you need to support them, because they are the most universal solution there is for an average user. Email/Username + Password is the most portable way to do login as a user that we have invented.
Every project has some amount of "being quirky/different" capital. If your project is not explicitly trying to innovate, or does not for some particular reason need to be very secure, then do not spend that capital on confusing users with the login flow. You'll turn a bunch of users away and cause a whole lot of support tickets, for very little benefit. Make users only think about stuff by making it unintuitive or different if it's really worth it to your product.
It's a lot simpler to implement (just one flow instead of signin / signup / forgot), less catastrophic when your data is breached, piggybacks on the significant amount of work that already goes into securing email, gives you 90% of the benefits of 2FA / FIDO / Web Authn / whatever for free with 0 implementation cost, makes account sharing harder (good for business), and is easy to extend/replace with oAuth for specific domains.
(I've also seen phone + phone OTP, but oh please never ask me for a phone number ever again. My phone number should always only be for making and receiving calls, not for verifying any sort of identity or personhood.)
Of course, nothing beats the security and privacy of username + password + TOTP (or security key), but you can't necessarily expect normal users to know to do that (or how).
Hell, I've seen at least one site that keeps the login username (what you actually use to sign into your account) separate from the public username (what everyone else sees), just to even more disconnect the login credentials from anything a potential attacker would have access to. But this is overkill for most scenarios (that particular platform does have a good reason).
The world is not improved or made more robust if every experience online must be gated through some third-party vendor's physical widget (or non-trivial software).
There are parts of our lives that benefit from the added securiry that comes alongside that brittleness and commercial dependence, and parts that don't. Let's not pretend otherwise.
It might help your mental model to think about them as identical to hardware security keys. Except now you don't need to buy a specific hardware key, your password manager is it. You can also just use your hardware key as your passkey, same thing (as long as the key supports FIDO2).
Specifically for your question on what happens if you lose face/fingerprint sensor. So this would be assuming you use Android/iOS's password managers, in that case even with biometrics failing you can just use the code you set on your device as both have fallbacks.
There are two critical things you lose with OAuth. First, it's centralization so you must trust that player and well now if that account is compromised everything down steam is (already a problem with email, who are the typical authorities). Second is privacy. You now tell those players that you use said service.
Let me tell you as a user another workflow. If you use bitwarden you can link Firefox relay, to auto generate relay email addresses. Now each website has not only a unique password, but a unique email. This does wonders for spam and determining who sells your data, AND makes email filters much more useful for organization. The problem? Terrible UX. Gotta click a lot of buttons and you destroy your generated password history along the way (if you care). No way could I get my parents to do this, let alone my grandma (the gold standard of "is it intuitive?" E.g Whatsapp: yes; Signal: only if someone else does the onboarding).
There's downsides of course. A master password, but you do control. At least the password manager passes the "parent test" and "girlfriend test", and they even like it! It's much easier to get them (especially parents) to that one complicated master passphrase that the can write down and put in a safe.
A lot of security (and privacy) problems are actually UI/UX problems. (See PGP)
OAuth recognized this, but it makes a trade with privacy. I think this can be solved in a better way. But at minimum, don't take away password as an option.
Sure many places only implement Google/Meta/Githun/Discord etc but that's not a requirement, specially for your own app. You can implement and run your own oAuth server if you so wished, much good it would be.
But regardless, that's why FIDO2 and webAuthN was developed, but even that has it's issues.
> Email addresses are case-insensitive.
From https://thecopenhagenbook.com/email-verification
The email standard says they are case sensitive.
If you lowercase emails during send operations, the wrong person may get the email. That's bad for auth.
Some (many) popular email providers choose to offer only case-insensitive emails. But a website about general auth should recommend the general case.
https://stackoverflow.com/questions/9807909/are-email-addres...
Side remark: It is not always clear/obvious what's the other case of a given character is, and may change over time. For example, the German capital ß was added in 2008 to Unicode. So it's best to avoid case sensitivity where you can, in general programming.
The standard says one thing, yet implementers do another. In this case following the letter of the standard gets you in trouble in the real world.
This means that you send emails to the case-sensitive address originally entered, but the user is free to login case insensitively.
The downside is that you cannot have two distinct users with emails that only differ in their case. But I feel rather OK about that.
I would argue the risk matrix of having case-insensitive emails looks much better than the risk matrix of having case-sensitive emails (meaning, you should lowercase all the emails and thus The Copenhagen Book is right, once again).
In most cases an accented character is a typo. If you have a non ASCII email I guess you are used to pain on the internet.
MacAdam is a surname, like the Scottish engineer John Loudon McAdam who invented the road construction known as "macadam". "Sandy.MacAdam@example.com" comes across rather different than "sandy.macadam@example.com".
A hypothetical DrAbby@example.com probably would prefer keeping that capitalization over "drabby@example.com".
I'm sure there are real-world examples.
On a related note, I knew someone with an Irish O'Surname who was very particular that the computer systems support his name. (As https://stackoverflow.com/questions/8527180/can-there-be-an-... puts it, "People do have email addresses with apostrophes. I see them not infrequently, and have had to fix bugs submitted by angry Hibernians.") No doubt some of them also want to see the correct capitalization be used.
A possibly better alternative is to recommend that the normalization be used only for internal use, while using the user-specified address for actual email messages, and to at least note some of the well-known issues with normalizing to lower-case.
I'm so tired of having my day constantly interrupted by expiring sessions. GitHub is my least favourite; I use it ~weekly, so my sessions always expire, and they forced me to use 2FA so I have to drag my phone out and punch in random numbers. Every single time.
As well as being terrible UX, though I have no evidence to back this up, I'm pretty sure this constant logging in fatigues users enough to where they stop paying attention. If you log into a site multiple times a week, it's easy for a phishing site to slip into your 60th login. Conversely if you've got an account that you never need to log into, it's going to feel really weird and heighten your awareness if it suddenly does ask for a password.
Regardless, companies should learn that everyone has a different risk appetite and security posture, and provide options.
Side-note, Github's constant session expiry & 2FA annoyed me so much that I moved to Gitea and disabled expiry. That was 90% of the reason I moved. It's only available on my network too, so if anything I feel I gained on security. Companies 100% can lose customers by having an inflexible security model.
Roughly, that it should continue for 30 days if used within 30 days.
A few weeks back I logged in right before a recurring meeting to take notes, and for several weeks running it's been interrupting me in the middle of that meeting to force me to log in again.
Part of the issue is I use it weekly from 3 different devices, so there's always one device that needs another login.
I know it's not my browser, as on my own web apps I set the maxAge of my session cookies to 10 years and they work perfectly.
This is at least what we do for our web application, where users are automatically refreshed indefinitely unless they are inactive for more than a few days (enough to cover Saturday/Sunday when they are not working). We have an access token that is refreshed in 5 minute intervals. The refresh request also provides a new refresh token with an extended expiration. A deactivated user can use it for a maximum of a few minutes until the access token expires, because the refresh request will fail. It's fine for our use case, but it may not be for everyone. We could potentially include a token black-list in the backend for emergency uses, but we haven't seen the need for it yet.
[0] https://github.com/lucia-auth/lucia
has he written up why? lots to learn here
edit: oh: https://github.com/lucia-auth/lucia/discussions/1707
this is great. he saw the coming complexity explosion, that the library was no longer useful to him personally, and took the humble route to opt out of the Standard Model of library slop development. rare.
The thing is, 99% of people really do just need 'log in / log out', and this is an incredibly useful thing to have as a library.
If you need Web 8.0 passkeys served via WASM eliptic curve sockets or whatever, sure, roll your own or use Auth0. But it feels really silly for the consensus around auth to be 'oh, you're making a CRUD cooking app to share your love of baking? cool, well here's the OAuth spec and a list of footguns, go roll some auth'. It's not a good use of that person's time - they should be focussed on their actual idea rather than being forced to reinvent plumbing - and tons of people are going to get it wrong and end up with effectively no auth at all.
With all the respect, I'm a bit skeptical about this document for such reasons:
- Name is quite pompous. It's a very good marketing trick: calling some document like if it was written by group of researchers from a Copenhagen university. :)
Yes, Lucia is a relatively popular library but it doesn't mean that it is promoting best practices and that its author should be considered an authority in such important field unless opposite is proven.
- I don't like some aspects of Lucia library design: when user token is almost expired - instead of generating new security token Lucia suggesting just to extend life of existing one. I see it as a very insecure behavior: token lives forever and can be abused forever. This violates one of the security best practices of limited token lifetime.
But both Lucia and "Copenhagen Book" encourages this practice [1]:
``` if time.Now().After(session.expiresAt.Sub(sessionExpiresIn / 2)) { session.ExpiresAt = time.Now().Add( updateSessionExpiration(session.Id, session.ExpiresAt) } ```
[1]: https://thecopenhagenbook.com/sessions#session-lifetime
The link you posted shows code to extend the session, which is common practice (it's called rolling session), not to "extend" the token's life (which should be impossible, a token needs to be immutable in the first place, which is why refreshing a token gives you a new token instead of mutating the original).
In the first scenario, the attacker steals the token and uses it forever. In the second, the attacker steals the token and they'll get a fresh one near its expiry. They can still impersonate the user forever. The user might notice something when they get kicked out (because the attacker renewed the token, rendering the old one invalid) but it's unlikely. For good UX, you need a grace period anyway, otherwise legitimate users can have problems with parallel requests (request one causes a token refresh, request two gets rejected because it was initiated before the first one was completed).
You can use a second token (a refresh token) but it only pushes the risk to the second token. Now we need to worry about the second token being stolen and abused forever.
Refresh tokens are useful for not having to hit the database on every request though: Typically, the short lived session token can be validated without hitting the db (e.g. it's a signed JWT). But it means that you can't invalidate it when stolen, it will be valid until expiry time so the expiry time has to be short to limit the damage. For the refresh token, on the other hand, you do hit the db. Using a second token doesn't add any security, hitting the db does, because the refresh token can be invalidated (by deleting it from the db).
Lucia always hits the db (at least in their examples), so you can invalidate tokens anytime. To mitigate risks, you can allow the user to see and terminate their active sessions (preferably with time, location, and device info: "Logged in 12 AM yesterday from an iPhone in Copenhagen"). You could also notify the user when someone logs in from a new location or device.
That's about all you can do. There's simply no fully secure way of implementing long-lived sessions.
- Session token has two timepoints: validUntil and renewableUntil. - If now > validUntil && now < renewableUntil - I'm regenerating session token.
This way user is not logged out periodically but session token is not staying the same for 5 years.
But maybe I'm just overthinking it. :)
Nice to hear someone touch on one of them: you absolutely NEED to use a transaction as a distributed locking mechanism when you use a token.
This goes double/quadruple for refresh tokens. Use the same token more than once, and that user is now signed out.
It doesn't matter if your system runs on one machine or N machines; if you have more than one request with a refresh token attached in flight at once - happens all the time - you are signing out users, often via 500.
Refresh tokens are one-time use.
The other thing devs and auth frameworks miss is the "state" parameter.
Seriously, the way auth in general is developing right now, I think we approach a point of insecurity through obscurity.
And with applications states, you need to adapt the application logic to authentication and the application then would have to check if someone maybe stole your refresh token.
In that document, refresh token rotation is preferred, but it also addresses the obvious difficulty in clustered environments: https://datatracker.ietf.org/doc/html/rfc6819#section-5.2.2....
What do you mean?
I'll keep an eye on these comments to see if there are any dissenting opinions or caveats but I know I'll be reviewing this against my own auth projects.
One thing I would like to see would be a section on JWT, even if it is just for them to say "don't use them" if that is their opinion.
One of my most frequent criticisms of teams responsible for security is that they spend a lot of time telling people what not to do instead of proactively providing them with efficient tools or methods for doing what they want to do.
Crypto relies on number theory and a complexity theoretical assumption that N!=NP (i.e. that there exists one-way/trapdoor functions).
I think it is opaque by the very nature of how it works (math).
Understanding finite fields or elliptic curves (integer groups really) made me able to grok a lot of crypto. It is often a form of the discrete-logarithm problem somehow.
- I learned the other day here on HN that SHA256 is vulnerable to a length extension attack, so if you want 256 bits of SHA goodness, you should use SHA512 and truncate it to 256 bits. This is terrible naming! Name the bad one "SHA-DoNotUse" if it's broken and this is known from the start. Why does it even exist?
- For the first decade or so of JWT library support, many verifiers happily accepted "alg: 'none'" payloads, letting attackers trivially bypass any actual verification. If you wanted JWT safely, you were supposed to know to tell the verifier to only accept the algorithms you were going to use when creating tokens.
- Hash algorithms have names such as "MD5", "SHA1", "bcrypt" and "argon2", ie meaningless character soup. I can't blame novice programmers for just using whatever hash algorithm is the default of their language's hash function, resulting in MD5-hashed passwords being super common until about a decade ago.
Security resources and libraries for programmers should be focused on how a thing should be used, not on how it works. That's what this book gets right (and what its page on elliptic curves gets so wrong).
Or, for another example, my favourite bit of crypto library design is PHP's `password_hash()` function[0]. They added it to the language after the aforementioned decade of MD5-hashed passwords, and that fixed it in one fell swoop. `password_hash()` is great, because it's designed for a purpose, not for some arbitrary set of crypto properties. The purpose is hashing a password. To verify a hashed password, use its brother `password_verify()`. Easy peasy! It's expertly designed, it supports rehashing passwords when necessary, and you don't need to understand any crypto to use it! I don't understand why all other high level programming languages didn't immediately steal this design.
I mean why can't all security libraries be like this? Why do most encryption libs have functions named "crypto_aead_chacha20poly1305" instead of "encrypt_message_symmetrically"? Why do they have defaults that encourage you to use them wrong? Why do they have 5 nearly identically named functions/algorithms for a particular purpose but actually you shouldn't use 4 of them and we won't tell you which ones? Do you want GCM or CCM? Or do you prefer that with AEAD? Do you want that with a MAC, and HMAC, or vanilla? Gaah I just want to send a secret! Tell me how to get it right!
[0] https://www.php.net/manual/en/function.password-hash.php
It looks like they mean authentication but it would be nice if they were clear.
It seems MitID isn't mentioned in The Copenhagen Book: https://www.google.com/search?q=site%3Athecopenhagenbook.com...
Iceland and the Faroes follow the same one-for-all approach: https://www.audkenni.is/, https://www.samleikin.fo/.
Things are a bit more fragmented in Finland, Norway and Sweden: https://www.norden.org/en/info-norden/electronic-identificat..., https://www.norden.org/en/info-norden/electronic-identificat..., https://www.norden.org/en/info-norden/electronic-identificat...
So, it's maybe not too much of a stretch to say that "a Copenhagen way" to authenticate is to integrate with MitID, either through a certified broker or by becoming one: https://www.mitid.dk/en-gb/broker/broker-certification/
I don't think it protects against timing attack because the common way of doing it is just to use sha256 and use the resulting hash to do a lookup in the database. This is not a fixed time operation
- Use libraries like zxcvbn to check for weak passwords.
These rules might be good for high-security sites, but it's really annoying to me when I have to generate a length-15 string password with special characters and uppercase for some random one-off account that I use to buy a plane ticket or get reimbursed for a contact lens purchase.
I especially appreciated the note that while UUIDv4 has a lot of entropy, it’s not guaranteed to be cryptographically secure per the spec. Does it matter? For nearly all applications, probably not, but people should be aware of it.
> Set all the other bits to randomly (or pseudo-randomly) chosen values.
Section 4.5, which is actually scoped to UUIDv1, does hint at it being a good idea:
> Advice on generating cryptographic-quality random numbers can be found in RFC1750.
But absolutely nothing stops you from doing this:
import random
import string
def terrible_prng(k: int) -> int:
_int = int("".join(random.choices(string.digits, k=k)))
return _int << (128 - _int.bit_length())
def make_uuid_v4(_int: int) -> str:
_int &= ~(0xC000 << 48)
_int |= 0x8000 << 48
_int &= ~(0xF000 << 64)
_int |= 4 << 76
_uuid_hex = "%032x" % _int
return "%s-%s-%s-%s-%s" % (
_uuid_hex[:8],
_uuid_hex[8:12],
_uuid_hex[12:16],
_uuid_hex[16:20],
_uuid_hex[20:],
)
They'll all be RFC4122-compliant (depending on how you interpret "randomly chosen values", since with `k=10`, for example, only the first field will be unique), but terrible, e.g. '83f1a1ea-0000-4000-8000-000000000000'.In fairness, RFC9562, which supersedes RFC4122, says this in Section 6.9:
> Implementations SHOULD utilize a cryptographically secure pseudorandom number generator (CSPRNG) to provide values that are both difficult to predict ("unguessable") and have a low likelihood of collision ("unique").
And the RFC2119 definition of SHOULD requires that you "[understand and carefully weigh the] full implications before choosing a different course."
I recently learned about the SRP protocol [1], and I’m surprised that it’s not more widely used/mentioned: with a relatively simple protocol, you can do a ZKP and generate a session token between the server and client in one fell swoop.
[1]: https://en.wikipedia.org/wiki/Secure_Remote_Password_protoco...
Embedded browsers make it impossible (literally in some cases, figuratively in others) to use social OAuth. If you click a link on Instagram, which by default opens in Instagram's browser, and that link has "Sign in with Google", it simply will not work, because Google blocks "insecure browsers", which Instagram is one. There are even issues getting "Sign in with Facebook" to work, and Meta owns Instagram and Facebook! The Facebook embedded browser suffers from similar issues.
It's virtually never useful to me when I click on a link in Slack or whatever, then respond to a text message, and go back to my browser expecting to find my page there, and it's nowhere because Slack has gobbled it up in its own browser.
Fortunately I just checked and there's a way to disable the embedded browser in Slack.
> When comparing password hashes, use constant time comparison instead of ==.
If you were comparing plaintext you'd get some info, but it seems overly cautious when comparing salted hashes. Maybe anticipating an unknown vulnerability in the hash function?
At a previous employer, people built some tool that auto-built Kube manifests and so on. To be honest, I much preferred near raw manifests. They were sufficient and the tool actually added a larger bug space and its own YAML DSL.
Two tradeoffs I see is that it is a bit abstract, and also a bit brief/succinct in some places where it just says it as it is and not the why. But neither of those are really negatives on my book, just concessions you have to make when doing a project like this. You can dig deeper in any topic, and nowadays libraries have pretty good practical setups, so as a place where it is all bound together as a single learning resource is AMAZING. I'm even thinking of editing it and printing it!
Also when it's set to strict? Or if it requires a PUT or other method that doesn't work with top-level navigation? Is it about ancient or obscure browsers that didn't/don't implement it (https://caniuse.com/same-site-cookie-attribute)?