简体   繁体   中英

How to store credentials for basic HTTP authenication into the database on server side?

For learning purposes, I have created a simple PHP application which implements a web service with a basic authentication mechanism. I know that basic authentication is not the best mechanism, but I just want to start with this mechanism before I start with OAuth2.0 or something else.

So my current environment looks like this:

Multiple clients provide data in JSON representation only over an https connection.

These resources are protected through a basic authentication header and the clients provide data only if the credentials are valid:

public function provideData()
{
    if (!isset($_SERVER['PHP_AUTH_USER'])) {
        header('WWW-Authenticate: Basic realm="Example BasicAuth"');
        header('HTTP/1.0 401 Unauthorized');
        die('Authentication failed');
    }

    $user = $_SERVER['PHP_AUTH_USER'];
    $password = $_SERVER['PHP_AUTH_PW'];

    // not relevant database stuff ...

    $password = hash('sha512', $password . $saltDb);

    if (!hash_equals($password, $passwordDb)) {
        die('Authentication failed');
    }

    // not relevant data handling stuff ...

    return json_encode($data);
}

So every client stores his own credentials into a client database and provides data only if the entered data is valid. (I don't want to store credentials as plain text into the client database so I store salted hashed passwords.). This is working great for me, but now comes the part where I got into trouble:

There is a server which should collect the data of all these clients. For this reason, I can create multiple client objects on the server side which contains data like the resource URL, username, password and so on, but I don't want to store the client passwords as plain text into the server database, but I can't store hashed client passwords on server side too, because the server will don't know about the real password then.

So I think the only solution is to implement a custom encrypt and decrypt function to store an encrypted client password into the server database. Then only the server knows about encryption and decryption and could handle this or is there a better way to store the required client credentials on the server side?

EDIT

I will try to clarify "the server will don't know about the real password". Please interrupt me if I am wrong, but a basic HTTP authentication will use plain client data (username and password) and sends a base64 encoded string of them, eg "Authorization: Basic dXNlcjpwYXNzd29yZA==" to get access to the resource. For this reason, the server has to use the password as plaintext to build a request with a basic authentication header.

The problem is when I create a client object on the server side, then I have to store the credentials into the server database and I could hash the password there, but hash functions are one-way functions so the server will not be able to get the plain password from the hashed password back, without storing the plain password too, but if the server needs the plain password to get access to the resource then I can't use a hash function to "protect" the password into the database.

If I understand what you are trying to do ( which I probably don't ) it seems like a good fit for asymmetric encryption ( like RSA )

Asymmetric encryption has 2 keys, a public and a private key:

  • public key can decode only stuff encrypted by private key
  • private key can decode only stuff encrypted by public key

This seems to meet what you would need:

  1. You can encrypted the password with public key on the client
  2. You can store the encrypted password in a client database
  3. You can securely send the encrypted password to the server
  4. The server can decode it using it's private key
  5. Many clients can all use the same public key, making it easier to deploy.
  6. Any given client cannot decode another clients password, as only the private key can decode stuff encoded by the public key.

A quick implementation using openSSL ( you have to enable and extension for this ) and certificates ( this an be a self signed cert )

Public key implementation on the client:

public function asymEncodePublic($string){
    //public cert
    $file = __DIR__.'/public/server.crt';
    if(file_exists($file)){
        $fp = fopen($file,"r");
        $public_key = fread($fp,8192);
        openssl_get_publickey(public_key);
        fclose($fp);

        //var to store output
        $crypttext = '';
        openssl_public_encrypt($string, $crypttext, $pub_key);
        //convert from bin to hex
        $crypttext = bin2hex($crypttext);
        return $crypttext;
    }
}

Private key implementation on the server:

public function asymDecodePrivate($crypttext){
    //private key
    $file = __DIR__.'/private/server.key';
    if(file_exists($file)){         
        $fp = fopen($file,"r");
        $private_key = fread($fp,8192);
        openssl_get_publickey($private_key);
        fclose($fp);
        //convert hex to bin
        $crypttext = hex2bin($crypttext);
        //var to store output
        $decrypted = '';
        //decrypt
        openssl_private_decrypt($crypttext, $decrypted, $private_key);

        return $decrypted;
    }
}

One thing if I remember correctly, the length of data you can encrypt (with openSSL) at one time is based on the complexity of the certificate. So for example with a 2048 cert you might be able to only encrypt up 256 bytes at a time. It's been a while sense I had to deal with this stuff, so I don't remember exactly.

A few notes Somethings are not clear to me.

because the server will don't know about the real password then

Does the server really need to know the "real" password. The only reason I could think that the server would need to know, is if the password is for a 3rd party application. An example would be sFTP on the clients machine.

This could be used for pulling or putting files on the remote client from the server. In this case the server would always need access to a plain text version of the password. So it can login to the remote machine. In this case there is no real way to secure it 100%. You can make it more difficult. For example with OpenSSL a hacker would need the encrypted password, the private key, and possibly your implementation of OpenSSL encryption.

Disclaimer:

For learning purposes, I have created a simple PHP application

I don't claim to be a cryptography expert ...

The HTTP basic authentication requires plaintext credentials which have to be base64 encoded. So the server has to store the plaintext credentials per client into the database for example if this should work, BUT this is really bad, because if someone steals the server database then he got access to the data of all clients. To obscure, the passwords leads to security through obscurity and is not really better.

So this is the wrong way to go. It seems that HTTP basic authentication is not suitable for a machine-to-machine communication via a REST API and I have to use a proper and secure mechanism like OAuth2.0 or @ArtisticPhoenix suggested.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM