Of course for a fast authentication implementation I could have used tuupola/slim-basic-auth
, I know that well.
But, HTTP Basic Authentication is known as insecure (see some reasons of why here!).
On the other hand, is HTTPS almighty? I know there are lot of leverages, both on a hardened server-side (security headers, HSTS, CSP, CORP, CORS) and client side (secure browser, secure memory),
to prevent a wide range attacks but does this really mean that a password should transit in almost clear text? Or even in clear text over TLS in certain scenarios (e.g form-based authentication).
I am pretty sure that in some cases or sophisticated attack (or just proxies on the internet or acting as proxies) the Basic base64 encoded credentials pair make it really easy for attackers to
retrieve those precious login + password couple.
First authentication from client is realized with a lookalike HTTP Digest inspired from RFC 7616
Client MUST respond a challenge issued by server, for instance:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest
realm="[email protected]",
qop="auth",
nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
opaque="15a349825c74ea8c5b4581c809497291"
If the client fails to answer correctly this challenge the nonce is invalidate from our part (server side). And user client must retry using a different nonce. The Algorithm implemented is NOT a real implementation of HTTP Digest, because:
- hash algorithm is fixed to
Argon2id
- uri and HTTP method are fixed for now
- opaque string is used transport hash salt
- Final KD (Keyed Digest) is realized using HMAC-256 algorithm and A1 as the key
Nonetheless, this should be secure... especially over HTTPS. And this PoC can be improved ! Also I try to enforce usage, as much as possible, of lib sodium for cryptographic implementation throughout this project. Sodium is a C extension for PHP and others languages, based on NaCl (Networking and Cryptography library, prononced salt) and designed by Daniel J. Bernstein.
Argon2 is a key derivation function that was selected as the winner of the Password Hashing Competition in July 2015. It was designed by Alex Biryukov, Daniel Dinu, and Dmitry Khovratovich from the University of Luxembourg.
This client response to server's challenge should look like this:
Authorization: Digest username="Mufasa",
realm="[email protected]",
uri="/login",
nonce="7ypf/xlj9XXwfDPEoM4URrv/xwf94BcCAzFZH4GiTo0v",
nc=00000001,
cnonce="f2/wE4q74E6zIJEtWaHKaf5wv/H5QzzpXusqGemxURZJ",
qop=auth,
opaque="15a349825c74ea8c5b4581c809497291",
response="753927fa0e85d155564e2e272a28d1802ca10daf449
6794697cf8db5856cb6c1"
- The nonce was given (and generated) by server.
- nc is chosen by client (changes at every try)
- cnonce is generated by client (changes at every try)
- with this implementation responses are calculated like this:
response = "HMAC-256( Argon2id(username:realm:passwd), nonce:nc:cnonce:qop:Argon2id(Method:request-uri))"
Where Argon2id(username:realm:passwd)
is the keyed used to sign the challenge's data.
For the purpose of this demo project, client generated string and challenge's response will be made available from src/DevTools/authenticationHelper.php
.
And that is why, for ease of use while testing, nc, (http) method and uri are fixed. Those could be implemented later so it is not a big problem. Same thing for the salt, it could be improved, for instance regenerated every 24/48 hours.
Still, even with those shortcuts taken, the use of Argon2id
and custom implementation as it is should be enough to discourage h4cK3rs.
Once a client is authenticated via the previous challenge. Server will pass it a token. This token is temporary, limited in time (it should be expired) and after its expiry client will be challenged again.
This token is associated to a given user and is just a proof that client was previously challenged and could go on for a specified time.
I have taken inspiration from RFC 6750 (OAuth 2.0 Bearer Token Usage) and the java implementation of various token based authentication mechanisms from Neil Madden (Manning - API Security in Action).
Authorization: Bearer 10689711bd1eb4ed00e4751e4ab594d4b2b1c91a9e83
Final word is: this system could slightly enhanced with the use of an old good in-memory key => value Data Store like Redis (or Memcached or APCu).
Draft By Laurent LEGAZ - June 2022