Go to content
Security

Common Authentication Pitfalls for Startups

Your software authenticates someone when it decides whether they are who they claim to be. You implement authentication when you build a login form, issue a session cookie, or hand a mobile client a token. Attackers exploit the mistakes you make there first. The Open Worldwide Application Security Project (OWASP) publishes the reference guidance: the Top Ten risk list, the Application Security Verification Standard (ASVS), and a set of cheat sheets that name specific implementation failures.

OWASP documents the small set of predictable mistakes behind most authentication failures precisely enough that you can check your own code against the list. The pitfalls that matter most for a startup fall into five areas: password policies that push users toward weak passwords, registration and recovery flows that leak information, session handling that leaves users logged in after logout, OAuth and token mistakes, and misplaced trust in multi-factor authentication (MFA). For each area, OWASP names the failure and the fix, and you can verify the behavior in your own code.

Password rules that make weak passwords worse

The most common authentication mechanism is still the static password, and users routinely defeat it in the name of convenience. The Web Security Testing Guide notes that users pick passwords like 123456, password, and qwerty when the system lets them. OWASP’s 2017 Top Ten entry for Broken Authentication describes attackers working from hundreds of millions of valid username and password pairs harvested from breaches. They feed those pairs into credential stuffing, default admin lists, brute force, and dictionary attacks against your login endpoint. Credential stuffing is the automated replay of stolen username and password pairs across many sites, and it works because people reuse passwords.

To counter weak passwords, OWASP recommends adding a second factor, enforcing a strong password policy, or ideally both. The Password Policies Cheat Sheet recommends adding two-factor authentication as an additional control, or enforcing a strong password policy covering length, complexity, reuse, and aging. The Identification and Authentication Failures guidance adds a concrete floor: enforce a minimum length of 10 to 12 characters with a mix of cases and numbers, and deploy MFA.

The second scenario in the same 2017 Top Ten entry explains why complexity rules backfire: most authentication attacks happen because passwords remain the sole factor, and password rotation and complexity requirements encourage users to create and reuse weak passwords. OWASP recommends stopping those practices per NIST 800-63, the US National Institute of Standards and Technology’s digital identity guideline, and using MFA instead. Both NIST and the UK’s National Cyber Security Centre also recommend against forcing regular password expiry, because scheduled rotation pushes people toward predictable patterns. If a compliance standard such as PCI DSS, the payment card industry’s security standard, requires expiry, you implement it, but you should not add it on your own.

Do not ship default credentials such as admin or Password123. Reject the worst passwords; OWASP suggests checking new passwords against a list of the top 10,000. And set the minimum length high enough that 10 to 12 characters is the lower bound rather than the upper bound.

Registration and recovery flows that leak or accept weak credentials

The registration and recovery flows are the first forms you build and the easiest to leave unhardened, so many authentication flaws enter through them. The Authentication Testing Cheat Sheet lists the registration failures directly: the form accepts weak passwords, the page runs over plain HTTP, and the error messages tell an attacker which usernames already exist.

An attacker discovers which accounts exist by reading the differences in your error messages, a technique called user enumeration. When your registration form says "that email is already registered" and your login form says "incorrect password" for a known account but "no such user" for an unknown one, you have built an oracle that scripts can harvest. An attacker collects valid usernames this way, then targets those accounts with credential stuffing or phishing. The Identification and Authentication Failures guidance recommends generic error messages, rate limiting, and CAPTCHA to slow automated attempts. It also calls out inadequate password recovery and knowledge-based answers as a common risk.

The cheat sheet lists password recovery failure modes: weak delivery of reset links, verification that an attacker can brute force, and reset flows that accept weak passwords. The Web Security Testing Guide adds a related warning: some applications accept alternative credentials, such as birthdates, Social Security numbers, PINs, or security questions, and these are considerably weaker than passwords, especially when they are the only value the user supplies. The Authentication Testing Cheat Sheet also covers the change flow: the application should require the current password before accepting a new one, and it should reject weak passwords at change time just as it does at registration.

Sessions that survive logout

After login, the server keeps the user logged in with a session: a server-side record identified by a session ID, the random value the browser sends with each request. The Authentication Testing Cheat Sheet lists the logout failures: no logout function at all, a logout that does not invalidate the session token on the server, an idle timeout set too long, and an idle timeout that does not invalidate the token.

The 2017 Top Ten adds related session weaknesses: session IDs exposed in URLs, session IDs not rotated after login, and session IDs not invalidated properly. A session ID in a URL leaks through browser history, referrer headers, and proxy logs. If the server does not rotate the session ID at login, an attacker who captured the pre-login ID can keep using it after the user authenticates.

OWASP recommends a server-side session manager that generates a new random session ID with high entropy after login and invalidates the old one. In your code, the server must delete or invalidate the stored session when a user logs out, and issue a fresh ID when a user logs in. If your logout handler only clears the browser cookie, the server still accepts the old session when it is replayed.

OAuth and token mistakes

Once you expose an API, OAuth 2.0 is the standard for protecting it. OpenID Connect builds federated login on top of it, letting users sign in through an external provider such as Google or GitHub. The OAuth 2.0 Cheat Sheet lists the token mistakes.

A bearer token is the simplest kind of token: a string the client sends in the Authorization header, and anyone who possesses it can use it. The cheat sheet says bearer tokens should be audience-restricted, preferably to a single resource server, so a token stolen from one service does not work against another.

Refresh tokens are the long-lived credentials that fetch a new access token when the old one expires, and they need stronger protection. OWASP recommends sender-constraining them with DPoP or mTLS, techniques that bind the token to the specific client that requested it, or using refresh token rotation, where the server issues a new refresh token on each use and invalidates the previous one.

The Implicit Grant, an older OAuth flow where the authorization server returned a token directly in the URL fragment, is deprecated by RFC 9700 and removed from OAuth 2.1. The cheat sheet says clients must use the Authorization Code Grant with PKCE for all client types. With PKCE (Proof Key for Code Exchange), the client generates a secret value called the code verifier, sends a hash of it with the authorization request, and presents the verifier when redeeming the code. An attacker who intercepts the code cannot redeem it without the verifier.

The Resource Owner password credentials grant is another flow OWASP says to avoid. The client collects the user’s username and password and sends them to the authorization server, exposing the user’s credentials to the client application.

The cheat sheet adds two details: authorization servers should authenticate clients with asymmetric methods such as mTLS or private_key_jwt, where the client proves its identity with a private key instead of a shared secret. Authorization responses must never travel over unencrypted connections. Redirect URIs using the http scheme are not allowed except for native clients using loopback redirection, where the response returns to a local address on the device.

What MFA does and does not stop

Multi-factor authentication (MFA) requires the user to present two different kinds of proof, such as a password and a one-time code. It is the most consistently recommended control in OWASP’s guidance, because it resists theft of a single factor: a leaked password alone does not unlock the account, and a leaked one-time code alone does not either.

But MFA has a documented limit that startups miss. An ASVS issue discussion on MFA requirements notes that MFA is not generally effective against phishing, because a phishing site can collect the password and the one-time code together and replay them. In practice, MFA protects against credential stuffing and brute force, but it does not stop a user who types their credentials into a lookalike site.

Checking your work against the ASVS

The Application Security Verification Standard (ASVS) is OWASP’s checklist for this work. The OWASP Developer Guide describes it as an open standard that sets the coverage and level of rigor expected for web application security verification. The standard is organized into sections V1 through V14, including authentication and session management. Version 4.0.3 contains 286 verification requirements.

ASVS defines three verification levels. Level 2 is the recommended target for most applications, and Level 3 applies to high-value transactions or sensitive medical data. A typical startup product sits in the Level 2 band. The ASVS turns the pitfalls into a testable list. Instead of debating whether your password policy is good enough, you check the authentication and session requirements against your code and fix the ones that fail.

More in Security Back to top ↑