KAHIBARO
Discord Login Register

30.4 Security Testing

Why Security Testing Matters for Authentication

Authentication code is a primary target for attackers. If there is a bug here, someone may:

Security testing for your authentication system is about actively trying to break it in a controlled way, before attackers do.

You do not need to be a security expert to start. You only need a checklist, some basic tools, and a repeatable process.

Goal of security testing for authentication:
Try realistic attack scenarios against registration, login, tokens, password reset, and email verification, and verify that the system behaves securely in every case.


Basic Security Test Checklist

When you test your authentication system, you can use a simple checklist. For each item, you will try to write tests (automated or manual) that confirm the system is safe.

A compact checklist:

AreaKey Questions
RegistrationCan I create accounts with invalid or duplicate data?
LoginCan I bypass login or guess passwords too quickly?
Sessions / TokensCan I steal, reuse, or forge tokens?
Password ResetCan I reset another user's password?
Email VerificationCan I skip verification or reuse verification links?
Rate limitingCan I brute-force login or reset codes?
Input handlingCan I inject SQL or break JSON parsing?
Error messagesDo errors leak sensitive information?

Use this checklist both when you test manually (with a browser or tools like Postman) and when you write automated tests.


Testing Registration

Registration looks simple, but it can leak information or allow weak inputs that later become vulnerabilities.

Test cases for registration

Create tests for these situations:

  1. Weak password rejection
    • Try passwords like "123456", "password", "qwerty".
    • Expected: The API returns a clear validation error.
    • The error should not reveal password rules in extreme detail, but it must guide the user enough.
  2. Password policy
    • Test boundaries: minimum length, maximum length.
    • Example: If minimum is 8 characters:
      • 7 characters -> fail.
      • 8 characters -> pass.
    • Test with long passwords, for example 100 characters, to ensure no crash or truncation.
  3. Email uniqueness
    • Create account with email "user@example.com".
    • Try to create another account with "user@example.com".
    • Expected: 409 Conflict or 400 Bad Request. Response must not say whether the email exists in a way that leaks extra data. A simple message like "Email already registered" is acceptable in many applications, but be consistent.
  4. Username / email enumeration through error messages
    • Try registering with:
      • Existing email.
      • New email.
    • Check if error responses differ in a way that reveals if an email is already registered.
    • Decide your policy:
      • Either you allow “Email already used” at registration, or you do not reveal this.
    • Important: Be consistent with login and password-reset flows.
  5. Input validation

Test invalid data:

Expected:

  1. Unexpected / extra fields
    • Send extra fields like "is_admin": true in the registration payload.
    • Expected: Server ignores or rejects them, but never sets elevated roles from user input.

Simple JSON examples to test:

json
// Valid registration
{
  "email": "user@example.com",
  "password": "Str0ng_Password_123",
  "name": "Alice"
}
// Attempt to escalate privileges
{
  "email": "evil@example.com",
  "password": "Str0ng_Password_123",
  "name": "Mallory",
  "is_admin": true
}

Your test should confirm that "is_admin": true is ignored or rejected.


Testing Login and Brute-Force Protection

Login is where attackers will try password guessing, token theft, and login bypass.

Functional login tests

  1. Correct credentials
    • Given a valid user, login returns:
      • 200 OK.
      • A valid session cookie or access token.
  2. Incorrect password
    • Wrong password for existing email:
      • Expected: 401 Unauthorized or 400 Bad Request.
      • Message should be generic, for example "Invalid credentials".
  3. Nonexistent email
    • Email not in database with any password:
      • Expected: same response as incorrect password, so that attacker cannot distinguish.
  4. Disabled or unverified accounts
    • For unverified accounts:
      • Either disallow login until verified, or allow with limited access.
    • For disabled / banned users:
      • Login should fail gracefully, not reveal internal flags.

Brute-force and rate limiting tests

To prevent brute-force attacks, you should test both:

Example test scenarios:

  1. Multiple wrong passwords
    • Send 10 failed login attempts for the same email, same IP.
    • Expected:
      • Either increasing delay, or a lockout, or a 429 Too Many Requests response.
      • No account lockout should be permanent without a safe unlock method.
  2. Account lockout behavior
    • After N failed attempts (for example 5):
      • Check that valid login is still possible or that user receives a clear message and a secure unlock pathway.
    • Ensure lockout can not be abused as a denial of service.
      For example, attacker should not be able to lock out any user forever.
  3. Distributed attempts
    • Many failed attempts from different IPs for the same account.
    • If you have IP-based or user-based rate limits, test both.

In automated tests, you can simulate this with a loop:

python
for i in range(10):
    response = client.post("/login", json={
        "email": "user@example.com",
        "password": f"wrong-{i}"
    })

Then check that after some attempts, the behavior changes appropriately.


Testing Session and Token Security

If you use sessions or JSON Web Tokens (JWT), you must ensure they cannot be easily stolen, forged, or misused.

Testing token structure and lifetime

For JWTs:

  1. Check token contents
    • Decode the token without verifying the signature (most libraries can do this).
    • Ensure it does not contain sensitive data such as:
      • Password hashes.
      • Security answers.
      • Internal secrets.
  2. Expiration
    • Inspect the exp claim to ensure it is present and has a reasonable lifetime.
  3. Issued-at and other claims
    • Check iat (issued at) and sub (subject, usually user ID) are correct.

Testing token forgery and reuse

Create tests for these scenarios:

  1. Tampered token
    • Take a valid token and modify the payload, for example change "role": "user" to "role": "admin".
    • Keep the same signature.
    • Expected: Server must reject it, because the signature is invalid for modified content.
  2. Unsigned or wrongly signed tokens
    • Create a token with no signature (algorithm "none").
    • Create a token signed with a wrong secret key.
    • Expected: Server rejects these tokens.
  3. Expired tokens
    • Create a token with an expiration time in the past.
    • Expected: API returns 401 Unauthorized or 403 Forbidden and a clear message that token has expired.
  4. Token revocation behavior
    • If your application supports revoking tokens (for example logout, forced logout, or rotation):
      • Get a token.
      • Revoke or rotate it.
      • Try using the old token.
      • Expected: It fails.

Example: Testing expired token in Python-like pseudocode

python
expired_token = create_jwt({"sub": user_id, "exp": past_time})
response = client.get("/protected-endpoint", headers={
    "Authorization": f"Bearer {expired_token}"
})
assert response.status_code == 401

Session cookie tests

If you use cookies instead of JWT:

You can inspect cookies in browser developer tools or through automated tests.


Testing Password Reset Flows

Password reset is often the weakest part of authentication. Attackers try to reset passwords of other users.

Flow overview to test

Typical password reset flow:

  1. User requests reset with email.
  2. System sends a password reset link with a token.
  3. User clicks the link and sets a new password.
  4. Token becomes invalid after first use or after expiration.

You should test each step.

Requesting a password reset

Tests:

  1. Generic responses
    • Request reset for:
      • Existing email.
      • Nonexistent email.
    • Expected: Same generic response, for example "If this email is registered, we have sent a reset email."
    • This prevents email enumeration.
  2. Rate limiting
    • Request many resets for the same email or from the same IP.
    • Expected:
      • Throttling or 429 Too Many Requests.
      • No spam of reset emails.
  3. Input validation
    • Invalid email formats.
    • Empty email.
    • Extremely long email values.

Reset tokens

After the user receives a reset link such as:

https://example.com/reset-password?token=<token>

you should test:

  1. Single use
    • Use the token once to change password. It should succeed.
    • Use the same token again. Expected: It fails with an error like "Invalid or expired token".
  2. Expiration
    • Create a token that is already expired, or wait past expiration.
    • Expected: Token is rejected.
  3. Token binding to user
    • Use a reset token intended for user A while logged in as user B, or send it for user B's email.
    • Expected: It only works for the correct account.
  4. Guessability
    • Ensure tokens are long and random. For example 32+ bytes of randomness, base64 or hex encoded.
    • Test that short or simple tokens are not accepted if you manually try to craft them.
  5. Password re-use and history (if implemented)
    • If your system prevents reusing the last password, test that you cannot reset to the same password as before.

Example bad and good token examples:


TokenProblem
12345Too short, guessable
user@example.comDerived directly from user identity
random-32-byte-valueGood, high entropy

Testing Email Verification Flows

Email verification is similar to password reset in structure but often less protected, which can be dangerous.

Common tests

  1. Verification without login
    • Click verification link while not logged in.
    • Expected: Email is verified or a clear message is shown, but you are not automatically logged in as that user unless that is an explicit feature.
  2. Reuse of verification link
    • Use the same verification link multiple times.
    • Expected: First time succeeds, later times:
      • Either show “email already verified” without errors.
      • Or show “invalid or expired link.”

In all cases, the link must not be able to change ownership of an email.

  1. Changing email before verification
    • Register with one email, then change the email address in DB (if allowed) before verification.
    • Use old verification link.
    • Verify that this does not verify the wrong email or user.
  2. Expired verification link
    • Try verifying after expiration.
    • Expected: Link fails and user must request a new one.
  3. Tampering with verification codes
    • If you use short codes (for example 6 digits) in email:
      • Try invalid formats, like letters, longer numbers, leading zeros.
    • Test brute-force rate limiting for verification codes, similar to login.

Testing Input Validation and Error Messages

Input validation is both a security and usability concern.

Input validation tests

Test every authentication endpoint:

For each one:

  1. Wrong data type
    • Send number where string is expected, or null.
    • Example:
json
     {
       "email": 12345,
       "password": null
     }
  1. Missing required fields
    • Omit "password" or "email".
  2. Unexpected fields
    • Add "role": "admin" during login, or "verified": true during registration.
    • Server must ignore or reject them, but never accept them.
  3. Boundary values
    • Maximum lengths, minimum lengths, empty strings.

Error message tests

You do not want to leak secrets in error messages.

Check for each endpoint:

  1. No stack traces to the client
    • Internal errors must be logged on server side, not returned as raw stack traces.
  2. No SQL or implementation details
    • Error should not say "SQL error near column password_hash".
  3. No detailed existence hints
    • Prefer a unified message for “user not found” and “wrong password”.

Example of good vs bad messages:


ScenarioBad messageBetter message
Wrong password"Password incorrect for email user@x.com""Invalid credentials"
Nonexistent email"No account with this email""Invalid credentials"
Reset for unknown mail"Email not found""If this email is registered..."

Automated Security Tests vs Manual Testing

Both styles are important.

Automated tests

Automated tests live in your project and run with your test framework (for example pytest).

What to automate:

Benefits:

Example of a simple automated security test (in pseudocode):

python
def test_login_does_not_reveal_user_existence(client, db_user):
    # Nonexistent email
    response1 = client.post("/login", json={
        "email": "unknown@example.com",
        "password": "any"
    })
    # Correct email, wrong password
    response2 = client.post("/login", json={
        "email": db_user.email,
        "password": "wrong"
    })
    assert response1.status_code == 401
    assert response2.status_code == 401
    assert response1.json()["detail"] == response2.json()["detail"]

This test checks that your login endpoint does not allow user enumeration.

Manual testing

Manual tests use tools like:

Manual tests are useful for:

A healthy process:

Using Tools for Security Testing

You can also use basic security tools to extend your testing.

HTTP and API tools

Static and dependency analysis

Although this is not specific to authentication, it is related:

Security scanners

There are specialized web security scanners. For beginners:

Building a Security Testing Strategy for Your Project

For your Authentication System project, you can follow a simple strategy.

  1. List all authentication-related endpoints

Example:

  1. For each endpoint, list threats

Example for /login:

  1. Convert threats into tests

Example:

  1. Add tests to your test suite
    • Use your chosen test framework.
    • Run tests on every commit (CI).
  2. Review regularly
    • When you add new authentication features, for example multi-factor authentication or social login, repeat the steps.

Key Principles to Remember

To summarize the most important ideas when testing authentication security:

Security Testing Principles for Authentication

  1. Test flows, not just single endpoints, especially for password reset and email verification.
  2. Ensure that error messages do not help attackers enumerate users or learn internal details.
  3. Confirm that tokens and links are:
    • Long, random, and unguessable.
    • Bound to a user and purpose.
    • Single use or short lived when appropriate.
  4. Test rate limiting for login, reset, and verification codes to resist brute force.
  5. Automate as many security tests as possible and run them in CI.
  6. Never log passwords, reset tokens, or raw authentication secrets.

If you follow these principles in your Authentication System project, you will already be ahead of many real-world applications in terms of security testing quality.

Views: 14

Comments

Please login to add a comment.

Don't have an account? Register now!