【问题标题】:SSL Alternative - encrypt password with JavaScript submit to PHP to decryptSSL 替代方案 - 使用 JavaScript 加密密码提交给 PHP 进行解密
【发布时间】:2011-08-09 03:22:03
【问题描述】:

我正在建立一个网站,我的付款方式将是 Google Checkout 和 Paypal。会有链接/按钮将用户重定向到安全的 Google/Paypal 网站以处理付款。这意味着我不需要每年 150 美元的额外费用和为我的网站安装 SSL 证书的复杂性。

但是我想在用户登录时加密用户的密码,这样如果他们在网络上,一些运行 FireSheep 等的恶意人员在发送到服务器时无法读取用户的实际密码。网站的其余部分不需要加密,因为它不是真正的敏感数据,可能会显着降低用户体验。

我的想法是这可以通过公钥加密来实现。假设这个过程是这样的:

  1. 公钥在 JavaScript 外部文件中,私钥在服务器上的 PHP 中
  2. 用户在表单中输入他们的用户名和密码并点击提交
  3. JavaScript 运行并加密密码,并将其存储回文本字段中
  4. 表单提交到服务器,密码用PHP解密
  5. PHP 中的纯文本密码经过加盐和哈希处理,然后与数据库中的哈希值进行比较。
  6. 注册/更改密码功能可能类似的过程。

我认为像 RSA 这样的东西可以解决问题。但是我在网上搜寻了一个可以工作的 JavaScript 库来做这件事,但似乎没有一个与可用的 PHP 库兼容。无论如何,它需要生成一组与 JavaScript 和 PHP 兼容的密钥。

有人知道这个的实际工作解决方案吗?如果不是,我们写一个怎么样,然后开源它。不幸的是,编写加密/解密代码非常复杂,所以我真的不知道现有库在做什么以及如何修改它们以使其工作。我已经对会话固定/劫持提供了保护,所以我对此不感兴趣。只是想在数据到达网络服务器之前对其进行加密。

注意:请不要发布一堆指向独立 Javascript 或 PHP 加密库的链接,我已经在 Google 上找到了这些链接。这实际上没有用。我需要的是用于 JavaScript 加密和 PHP 解密的代码,它们实际上可以和谐地协同工作以产生上述预期结果。

另外,如果您可以避免发布“只使用 SSL”之类的 cmets。我实际上想要一个解决这个确切问题的方法,即使它不是最佳实践,它仍然很有趣。

非常感谢!

【问题讨论】:

  • 我的建议是接受它并支付 150 美元。如果您正在处理付款,则用户将希望使用 SSL,并且当您实施安全系统时(如果您确实这样做了),无论您支付多少,您很可能会花掉超过 150 美元/按小时付费。
  • 这绝不会阻止攻击。如果我可以嗅探并伪造明文密码,我就可以嗅探并伪造加密密码。加密文本成为密码。
  • Michael 是绝对正确的,我自己考虑过这种方法,然后意识到它的不足之处。
  • @Nick Jeffrey 提供了解决方案below,我已经成功实施了。
  • @aubreyrhodes 有许多支付提供商,如 PayPal,甚至大多数商家设施,都允许您将信用卡和其他支付数据的安全处理工作卸载到他们的 HTTPS 站点上。用户访问您的网站,将他们想要的商品添加到购物车,然后被重定向到使用 HTTPS 的安全支付网站。攻击者没有有用的数据。除非您想要一个完整的集成购物车和支付体验,用户永远不会离开您的网站,否则无需花费 SSL 证书。这也会让您承担一些责任。

标签: php javascript encryption ssl public-key-encryption


【解决方案1】:

只有一个问题:攻击者不需要知道实际密码。他只需要看到发送到服务器的值。该值允许用户登录。该值是什么无关紧要;无论是明文、加密文本还是猫的图片。它只是一个对用户进行身份验证的令牌。如果攻击者可以看到此令牌并重复相同的请求,并且相同的请求允许他登录,那么您将一无所获。

【讨论】:

  • @deceze:不是真的。请参阅我的答案以获得抗重放明文登录协议。
  • 但是如果是PKI,并且公钥在客户端,他们怎么会有密钥解密呢?
  • @0A0D 这就是重点,他们不需要真正解密它。
  • @deceze 点了。 SSL 证书定价现在终于与域名注册定价相差无几了,所以几乎没有任何借口。但是,我更希望浏览器支持匿名 SSL,而不会发出可怕的警告,也不会显示任何“安全连接”chrome,所以它看起来就像一个常规的不安全连接。如果不出意外的话,这会让劫机者的生活变得更加困难——他们不得不 MITM 而不是嗅探。
  • @0A0D 我是说我甚至没有兴趣解密你的加密密码。 :) 我只会嗅探您发送到服务器的加密密码并伪造完全相同的请求,将相同的加密密码发送到服务器,这将允许我以与您相同的方式登录。如果没有随机数,这种重放攻击是微不足道的,我不在乎您是否加密密码、加密密码的频率或频率。
【解决方案2】:

RSA 太过分了;您可能需要的是一个简单的质询-响应协议。例如:

  • 生成一个随机的随机值;这有助于防止重放攻击。
  • 将该 nonce 值和密码 salt 连同登录表单的其余部分一起发送到浏览器。
    • 您是以加盐和散列的形式存储密码,对吧?
  • 当用户输入密码时,让表单上的脚本计算并返回 hash(hash(password, salt), nonce)。
  • 当服务器收到表单提交时,让它计算哈希(storedSaltedPassword,nonce)并验证它是否等于提交的值。
    • 在服务器上保留nonce值;不要相信客户端会将其回显给您,否则您的重放保护已失效。

这种方案的弱点是数据库中的密码散列在某种意义上是密码等效的;虽然提取用于生成这些哈希的原始密码可能是不可行的,但了解存储的哈希足以在您的网站上冒充用户。

SSL 证书的用途完全不同:SSL 证书的目的是使第三方流氓服务器难以声称是您的服务器,因为它没有由某些相互信任的第三方签署的证书它属于您的域的一方。另一方面,如果您无法阻止流氓服务器冒充您的服务器,那么您就无法保护您的用户不向该流氓服务器提供密码,尽管有密码学。

【讨论】:

  • @Jeffrey 在页面发送到客户端时,攻击者无法嗅探随机数和盐值。他们不能重新创建哈希吗?另外,使用 GoDaddy SSL 证书,我认为您必须使用它们托管您的网站。此外,您必须每年更新,这样价格才会上涨。
  • @zoszsoz 您当然不必与他们一起托管以使用他们的证书。但是,如果您向他们注册了域,验证过程会更快。至于协议:hash 必须是单向函数——给定yz 使得z = hash(x, y),计算x 一定不可行。为了计算响应值,客户端必须知道hash(password, salt)——服务器提供salt,以便客户端可以从password 中得到它。 nonce 只是为了防止重复使用响应。
  • @Jeffrey 谢谢,是的,我知道这是如何工作的,我可以像这样实现它。但是,如果我想加密其他字段并在服务器端使用解密的明文,我仍然会对 JavaScript 加密/PHP 解密解决方案感兴趣。至少有教育意义。
  • @zoszsoz 如果您不将其作为两个单独的页面(一个是用户名,另一个是密码),那么您可能无法使用可提交的表单——相反,整个事情需要成为 AJAX。当点击登录按钮或按下回车键时,使用用户名触发 AJAX 请求以检索质询,然后在第二次调用中发送响应。
  • @zoszsoz 此协议仅适用于密码验证,不适用于创建帐户或更改密码。创建帐户是一项艰巨的任务,因为您没有现有的信任锚,但 Diffie-Hellman 密钥协议,然后使用生成的密钥对密码进行加密,足以阻止被动嗅探器。
【解决方案3】:

首先,我认为这不是一个好主意。我使用 Google 找到了一些可能对您有用的示例(但是我没有测试过这些示例):

GPL JavaScript Public Key Encryption

RSA Public Key Encryption Test in JavaScript

PGP Encryption in JavaScript

RSA Algorithm Example in JavaScript

您应该建立一些加盐机制来为每个加密值加盐,否则密钥可能会被泄露。

【讨论】:

  • 同意盐。但是,我需要使用此 JavaScript 的 PHP 库和代码来实际解密它。这是最难的部分,找到真正协同工作的 JavaScript 和 PHP。我有两块木头,但没有胶水可以把它们粘在一起。
  • @zoszsoz:为什么不使用自签名证书?它们是免费的,比任何通过 PHP 或 JavaScript 自己滚动的加密都要好。
【解决方案4】:

http://www.jcryption.org/ -- 是你要找的组合。

【讨论】:

    【解决方案5】:

    您不需要加密密码。您需要散列密码。您真的真的不想自己访问明文密码,否则您将失去不可否认性,这会产生严重的法律后果。在继续之前,您需要彻底调查它的含义。

    【讨论】:

    • 除了他仍然需要将盐或随机数传递给客户端并将其存储在服务器端以便他可以比较哈希值。
    【解决方案6】:

    这是获取输入然后通过java脚本加密内容的代码 整个代码也可以在 github 上找到。你们可以搜索它 encrypt_js_decrypt_php。问题一直存在。我想出了解决方案。只需将其导入本地主机。

    <html>
    
    <input type="text" id="code" name="code"/>
    <input type="submit" name="submit" value="submit" onclick="return encryptCode();"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script type="text/javascript">
    function rc4(key, str)
    {
        var s = [], j = 0, x, res = '';
        for (var i = 0; i < 256; i++) 
        {
            s[i] = i;
        }
        for (i = 0; i < 256; i++) 
        {
            j = (j + s[i] + key.charCodeAt(i % key.length)) % 256;
            x = s[i];
            s[i] = s[j];
            s[j] = x;
        }
        i = 0;
        j = 0;
        for (var y = 0; y < str.length; y++) 
        {
            i = (i + 1) % 256;
            j = (j + s[i]) % 256;
            x = s[i];
            s[i] = s[j];
            s[j] = x;
            res += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]);
        }
        return res;
    }
    
    function encryptCode()
    {
      var value = document.getElementById("code").value;
      var key = "secretKeyToProvide";  /*--Provide Your secret key here--*/
      var codeValue = rc4(key, value);
      var arr = {code:codeValue, Age:25};
      $.ajax({
                    url: "response.php",
                    type: "POST",
                    data: JSON.stringify(arr),
                    dataType: 'json',
                    async: false,
                    contentType: 'application/json; charset=utf-8',
                    success: function(data) 
                    {
                        alert(data);
                    }
                });   
    }
    </script>
    </html>
    

    现在,让我们解密php中的代码

    <?php
    
    function mb_chr($char) 
    {
        return mb_convert_encoding('&#'.intval($char).';', 'UTF-8', 'HTML-ENTITIES');
    }
    
    function mb_ord($char)
    {
        $result = unpack('N', mb_convert_encoding($char, 'UCS-4BE', 'UTF-8'));
        if (is_array($result) === true) 
        {
            return $result[1];
        }
            return ord($char);
    }
    
    function rc4($key, $str) 
    {   
        if (extension_loaded('mbstring') === true) 
        {
            mb_language('Neutral');
            mb_internal_encoding('UTF-8');
            mb_detect_order(array('UTF-8', 'ISO-8859-15', 'ISO-8859-1', 'ASCII'));
        }
        $s = array();
        for ($i = 0; $i < 256; $i++)
        {
            $s[$i] = $i;
        }
        $j = 0;
        for ($i = 0; $i < 256; $i++)
        {
            $j = ($j + $s[$i] + mb_ord(mb_substr($key, $i % mb_strlen($key), 1))) % 256;
            $x = $s[$i];
            $s[$i] = $s[$j];
            $s[$j] = $x;
        }
        $i = 0;
        $j = 0;
        $res = '';
        for ($y = 0; $y < mb_strlen($str); $y++)
        {
            $i = ($i + 1) % 256;
            $j = ($j + $s[$i]) % 256;
            $x = $s[$i];
            $s[$i] = $s[$j];
            $s[$j] = $x;
            $res .= mb_chr(mb_ord(mb_substr($str, $y, 1)) ^ $s[($s[$i] + $s[$j]) % 256]);
        }
        return $res;
    }
    
    $request_body = file_get_contents('php://input');
    $json = json_decode($request_body);
    $secretCode =$json->code ;
    $age =$json->Age  ;
    $key = "secretKeyToProvide";  /*--Provide Your secret key here what you have given in javascript--*/
    $decryptedSecretCode  = rc4($key, $secretCode) ;
    echo $decryptedSecretCode;
    exit;
    ?>
    

    【讨论】:

      猜你喜欢
      • 2014-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-03
      • 1970-01-01
      相关资源
      最近更新 更多