Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

By what process does HTTP Basic Auth actually authenticate credentials?

I've been bringing myself up to speed on HTTP Basic Authentication.

I understand that this is fundamentally an insecure access mechanism (even when used over HTTPS, which it always should be) but I recognise that HTTP Basic Auth is not entirely without utility and I would like to be familiar with it, even if situations seldom arise in which I might deploy it.


My understanding so far:

After some reading, I understand that:

  • a server may request authorisation for a resource by returning 401 (unauthorised)
  • a WWW-Authenticate response header determines that the authentication to be used to access this resource will be Basic
  • HTTP Basic Authentication requires either that:
  • a) the user submit username and password via a browser-generated console; or that
  • b) once successfully submitted manually, the same username and password combination will be automatically submitted via an Authorization request header prepended to each HTTP request

So far, so good.


Issues to be aware of:

I also understand that there are some issues with HTTP Basic Auth which have evolved over time, like:

  • some browsers no longer accept URL syntax such as https://mylogin:[email protected]/my-resource.html
  • where PHP is being run through CGI or FastCGI then the submitted Authorization Credentials will not be passed to $_SERVER['HTTP_AUTHORIZATION'] unless a hack is deployed - the most common recommendation being a URL rewrite via .htaccess mod_rewrite

and other issues which have persisted from the very beginning, like:

  • it's a non-trivial problem to "log out" of HTTP Basic Auth since it was never intended or designed to have a log-out mechanism

The missing piece of the puzzle:

However, I'm still confused, because even where the user (or the Authorization request header) has submitted valid authentication credentials... how does the server know they are valid?

In every document I have come across discussing the mechanics of HTTP Basic Authentication the discussion stops short of the point at which the credentials are actually authenticated.


Question:

How are the submitted credentials actually authenticated?

Where is the server comparing the submitted credentials to... anything?


Bonus Question:

N.B. This is related to my main question immediately above because my use of .htaccess and queryString parameters to convey credentials (see below) renders deployment of HTTP Basic Auth entirely redundant - if I go down this route, I can convey credentials using .htaccess and queryString parameters alone and I don't need to deploy HTTP Basic Auth at all.

As a way to circumvent the CGI/FastCGI issue, I often see variations of these .htaccess lines cited:

RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

or

RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]

or

SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0 

though my attempts to get any of these methods to populate PHP Environment Variables with the credentials have all proven unsuccessful.

Instead, I have deployed the following (using queryString parameters instead of Environment variables), successfully:

# WRITE HTTP BASIC AUTHENTICATION CREDENTIALS TO QUERY STRING
RewriteCond %{HTTP:Authorization} [NC]
RewriteCond %{QUERY_STRING} ^basicauth=login$ [NC]
RewriteRule ^my-document.php https://example.com/my-document.php?basicauth=login-submitted&credentials=%{HTTP:Authorization} [NC,L]

which appends the credentials as queryString parameters.

I am not unhappy with my own mod_rewrite solution above, but I am stumped that I cannot get Environment Variables working at all.

I'm idly wondering if there is something obvious I'm missing when it comes to the latter - like... might they be switched off in my PHP Configuration?

(And, if so, which entries would I need to check in PHPInfo to confirm that they were actually switched on and receptive to values transferred to them via mod_rewrite?)

like image 378
Rounin - Glory to UKRAINE Avatar asked Oct 12 '25 07:10

Rounin - Glory to UKRAINE


1 Answers

The explanation stops short of the actual authentication because how you do this is entirely up to you.

The username and password are sent as clear text* to you. So, same as when someone would submit a login form with a username and password field, you can then decide what to do with it.

*: Yes, they are base64-encoded. But my point was that they aren't hashed or encrypted or anything like that.

So, you may...

  • Compare the username & password to some other clear-text value, for example from an environment variable
  • Hash the password and compare the hash with something, for example a hashed password in a database
  • Forward the credentials to some external authentication service
  • ...?

Example of how that could look on the server (this example assumes the use of node.js, Koa, koa-router, Mongoose and bcrypt, and for simplification purposes it assumes neither username nor password are allowed to contain a colon):

router.get('/protectedPage', async ctx => {
  const [authMethod, authData] = ctx.get('authorization')?.split(' ') ?? []
  
  if (authMethod === 'Basic') {
    try {
      const decoded = Buffer.from(authData, 'base64').toString()
      const [username, password] = decoded.split(':')
      
      // Find user in database and verify password
      const user = await User.findOne({ username })
      if (user && await bcrypt.compare(password, user.encryptedPassword)) {
        // User is authenticated now
        ctx.state.user = user
      }
    } catch (e) {
      console.error(`Failed to process auth header "${authData}"`, e)
    }
  }
  
  if (ctx.state.user) {
    return ctx.render('protectedPage')
  } else {
    return ctx.throw(401, null, {
      headers: { 'WWW-Authenticate': 'Basic realm="Protected Page"' }
    })
  }
})

Essentially, how you use the credentials is not in the scope of the mechanism.

(About logging out: I usually redirect people to https://[email protected] or something like that, so that from now on the requests will use this invalid username and empty password, causing a 401 again. Note that this won't work if the page is in cache because the cached version would be delivered anyway, so this aspect may need extra consideration in that case - probably authenticated pages should not be cached anyway.)

In regards to the "insecure" aspect: It's not really insecure if you use HTTPS, because the aspect of "the credentials are sent in every request in clear text" is not relevant anymore then. It does however present other issues outlined in this answer, most importantly that nowadays clear-text password validation should intentionally be designed as a slow operation to avoid brute-force attacks, but with a system like basic auth where this has to be performed on every request, this puts a heavy burden on the server which can easily be abused for denial-of-service attacks.


About how to get the authorization header forwarded to PHP: Unless something else is configured in a strange way, setting CGIPassAuth on in Apache's configuration should be all that's needed.

From the docs:

CGIPassAuth allows scripts access to HTTP authorization headers such as Authorization, which is required for scripts that implement HTTP Basic authentication. Normally these HTTP headers are hidden from scripts. This is to disallow scripts from seeing user ids and passwords used to access the server when HTTP Basic authentication is enabled in the web server. This directive should be used when scripts are allowed to implement HTTP Basic authentication.

Afterwards, the $_SERVER['HTTP_AUTHORIZATION'] variable should be set properly, and then PHP can also parse the header for you automatically and provide $_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW'].

like image 155
CherryDT Avatar answered Oct 14 '25 21:10

CherryDT