如何将基本HTTP身份验证的凭据存储到服务器端的数据库中?

时间:2017-11-04 18:24:24

标签: php rest authentication basic-authentication

出于学习目的,我创建了一个简单的PHP应用程序,它实现了一个带有基本身份验证机制的Web服务。我知道基本身份验证不是最好的机制,但我只是想在开始使用OAuth2.0之前先用这个机制开始。

所以我现在的环境是这样的:

多个客户端仅通过https连接以JSON表示形式提供数据。

这些资源通过基本身份验证标头进行保护,只有在凭据有效时,客户端才会提供数据:

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);
}

因此,每个客户端都将自己的凭据存储到客户端数据库中,并仅在输入的数据有效时提供数据。 (我不想将凭证作为纯文本存储到客户端数据库中,因此我存储了盐渍哈希密码。)。这对我来说很有用,但现在我遇到麻烦了:

有一台服务器应该收集所有这些客户端的数据。出于这个原因,我可以在服务器端创建多个客户端对象,其中包含资源URL,用户名,密码等数据,但我不想将客户端密码作为纯文本存储到服务器数据库中,但是我不能在服务器端存储哈希客户端密码,因为服务器将不知道真正的密码。

所以我认为唯一的解决方案是实现自定义加密和解密功能,以将加密的客户端密码存储到服务器数据库中。那么只有服务器知道加密和解密,可以处理这个问题,还是有更好的方法在服务器端存储所需的客户端凭证?

修改

我将尝试澄清“服务器将不知道真实密码”。如果我错了,请打断我,但基本的HTTP身份验证将使用普通客户端数据(用户名和密码)并发送base64编码的字符串,例如“授权:基本dXNlcjpwYXNzd29yZA ==”以获取对资源的访问权限。因此,服务器必须使用密码作为明文来构建具有基本身份验证标头的请求。

问题是当我在服务器端创建客户端对象时,我必须将凭据存储到服务器数据库中,我可以在那里散列密码,但散列函数是单向函数,因此服务器不会能够从哈希密码中获取普通密码,而不存储普通密码,但如果服务器需要普通密码才能访问资源,那么我就不能使用哈希函数将密码“保护”到数据库。

2 个答案:

答案 0 :(得分:0)

如果我理解你要做的事情(我可能不会这样做),它似乎非常适合非对称加密(如RSA)

非对称加密有2个密钥,一个公钥和一个私钥:

  • 公钥只能解码由私钥加密的内容
  • 私钥只能解码用公钥加密的内容

这似乎符合你的需要:

  1. 您可以使用客户端上的公钥加密密码
  2. 您可以将加密的密码存储在客户端数据库中
  3. 您可以安全地将加密密码发送到服务器
  4. 服务器可以使用它的私钥解密它
  5. 许多客户端都可以使用相同的公钥,从而更易于部署。
  6. 任何给定的客户端都无法解码其他客户端密码,因为只有私钥才能解码由公钥编码的内容。
  7. 使用openSSL(您必须为此启用和扩展)和证书(这是一个自签名证书)的快速实施

    客户端上的公钥

    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;
        }
    }
    
    服务器上的

    私钥实施:

    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;
        }
    }
    

    如果我没记错的话,可以一次加密(使用openSSL)的数据长度取决于证书的复杂程度。因此,例如使用2048证书,您一次只能加密256个字节。我不得不处理这些事情,所以我不记得确切。

    一些注释 有些事情对我来说并不清楚。

      

    因为服务器不会知道真实密码

    服务器真的需要知道"真正的"密码。我认为服务器需要知道的唯一原因是密码是否适用于第三方应用程序。一个例子是客户端机器上的sFTP。

    这可用于从服务器提取或放置远程客户端上的文件。在这种情况下,服务器始终需要访问密码的纯文本版本。所以它可以登录到远程机器。在这种情况下,没有真正的方法来100%保护它。你可以让它变得更加困难。例如,对于OpenSSL,黑客需要加密密码,私钥以及可能的OpenSSL加密实现。

    <强>声明:

      

    出于学习目的,我创建了一个简单的PHP应用程序

    我不是自称是加密专家......

答案 1 :(得分:0)

HTTP基本身份验证需要明文凭据,必须使用base64编码。所以服务器必须将每个客户端的明文凭证存储到数据库中,例如,如果这应该有效,这真的很糟糕,因为如果有人窃取了服务器数据库,那么他就可以访问所有数据了。客户端。为了模糊,密码通过默默无闻导致安全,并不是更好。

所以这是错误的方法。似乎HTTP基本身份验证不适合通过REST API进行机器到机器的通信,我必须使用适当的安全机制,如OAuth2.0或@ArtisticPhoenix建议。