SSL over Javascript

时间:2014-02-26 08:35:57

标签: php ssl openssl ssl-certificate cryptico

我已经看到了一些类似的问题似乎并没有解决我的确切用例,我认为我已经找到了答案,但在安全方面,我是一个完全的菜鸟,RSA,几乎与之相关的一切。我对这些概念基本熟悉,但到目前为止我所做的所有实际实现都是关于编辑其他人的代码而不是生成我自己的代码。无论如何,这就是我所在的地方:

我知道Javascript本身就是加密的好地方。有人可能会对你的回应进行调整并破坏JS,因此你最终会通过网络发送未加密的数据。它应该通过HTTPS SSL / TLS连接完成,但是这种托管需要花钱,官方签名的证书应该是真实的连接。

话虽这么说,我认为我要这样做的方式可以绕过JS加密的中间人的弱点,因为我只加密一件事(密码哈希)对于一个RESTful服务调用,然后仅使用该密码哈希来签署来自客户端的请求,以便将它们认证为来自请求声明的用户。这意味着JS只负责在创建用户帐户时加密密码哈希值,如果服务器无法解码该密码,那么它就知道它已经存在。

我还要保存一些客户信息,特别是$_SERVER['REMOTE_ADDR'],以保证某人不会M-i-t-M进行注册交换。

我正在使用PHP的openssl_pkey_函数生成非对称密钥,以及客户端的Cryptico库。我的计划是让用户向REST服务发送“预注册”请求,这将导致服务器生成密钥,将私钥和客户端信息存储在由电子邮件地址索引的数据库中,然后响应用公钥。

然后,客户端将使用公钥加密用户的密码哈希,并将其作为另一种请求类型发送到REST服务以完成注册。服务器将解密并保存密码哈希,使客户端信息和私钥无效,因此不能使用该信息进行进一步的注册,然后使用200状态代码进行响应。

要登录,用户可以输入他们的电子邮件地址和密码,密码将在注册期间进行哈希处理,附加到请求正文,然后再次进行哈希处理,以便向登录终端签署请求,该终端会尝试附加将哈希存储到请求主体并对其进行哈希以根据请求中的签名验证签名,从而对用户进行身份验证。对服务的进一步数据请求将遵循相同的身份验证过程。

我错过了任何明显的漏洞吗?是否可以将$_SERVER['REMOTE_ADDR']值欺骗为特定的值?我不需要IP地址准确或与用户登录时相同,我只需要知道“预先注册”并获得公钥的同一台机器跟进并完成注册而不是劫持者使用窥探公钥完成注册。当然,我想如果他们能够做到这一点,他们就会在创建时劫持帐户而不是恢复,合法用户将无法使用自己的密码完成注册,这也是可以的。

底线,除非我派出真正的SSL主机,否则有人仍然会破解我的服务吗?我是否将Javascript的弱点作为加密工具?


当我编写和调试我的代码时,如果有人想使用它,我会在这里发布。如果我的网站遭到任何攻击,请告诉我。

这些函数用于验证客户端请求对头文件中的哈希,生成私钥,将其保存到数据库,使用公钥进行响应,以及解密和检查密码哈希。

        public function validate($requestBody = '',$signature = '',$url = '',$timestamp = '') {
            if (is_array($requestBody)) {
                if (empty($requestBody['signature'])) { return false; }
                if (empty($requestBody['timestamp'])) { return false; }
                if ($requestBody['requestBody'] === null) { return false; }

                $signature = $requestBody['signature'];
                $timestamp = $requestBody['timestamp'];
                $requestBody = $requestBody['requestBody'];
            }

            if (($requestBody === null) || empty($signature) || empty($timestamp)) { return false; }

            $user = $this->get();

            if (count($user) !== 1 || empty($user)) { return false; }
            $user = $user[0];

            if ($signature !== md5("{$user['pwHash']}:{$this->primaryKey}:$requestBody:$url:$timestamp")) { return false; }

            User::$isAuthenticated = $this->primaryKey;
            return $requestBody;
        }

        public function register($emailAddress = '',$cipher = '') {
            if (is_array($emailAddress)) {
                if (empty($emailAddress['cipher'])) { return false; }
                if (empty($emailAddress['email'])) { return false; }

                $cipher = $emailAddress['cipher'];
                $emailAddress = $emailAddress['email'];
            }

            if (empty($emailAddress) || empty($cipher)) { return false; }

            $this->primaryKey = $emailAddress;
            $user = $this->get();

            if (count($user) !== 1 || empty($user)) { return false; }
            $user = $user[0];

            if (!openssl_private_decrypt(base64_decode($cipher),$user['pwHash'],$user['privateKey'])) { return false; }
            if (md5($user['pwHash'].":/api/preRegister") !== $user['session']) { return false; }

            $user['session'] = 0;
            if ($this->put($user) !== 1) { return false; }

            $this->primaryKey = $emailAddress;
            User::$isAuthenticated = $this->primaryKey;
            return $this->getProfile();
        }

        public function preRegister($emailAddress = '',$signature = '') {
            if (is_array($emailAddress)) {
                if (empty($emailAddress['signature'])) { return false; }
                if (empty($emailAddress['email'])) { return false; }

                $signature = $emailAddress['signature'];
                $emailAddress = $emailAddress['email'];
            }

            if (empty($emailAddress) || empty($signature)) { return false; }

            $this->primaryKey = $emailAddress;

            $response = $this->makeUserKey($signature);
            if (empty($response)) { return false; }

            $response['emailAddress'] = $emailAddress;
            return $response;
        }

        private function makeUserKey($signature = '') {
            if (empty($signature)) { return false; }

            $config = array();
            $config['digest_alg'] = 'sha256';
            $config['private_key_bits'] = 1024;
            $config['private_key_type'] = OPENSSL_KEYTYPE_RSA;

            $key = openssl_pkey_new($config);
            if (!openssl_pkey_export($key,$privateKey)) { return false; }
            if (!$keyDetails = openssl_pkey_get_details($key)) { return false; }

            $keyData = array();
            $keyData['publicKey'] = $keyDetails['key'];
            $keyData['privateKey'] = $privateKey;
            $keyData['session'] = $signature;

            if (!$this->post($keyData)) { return false; }

            $publicKey = openssl_get_publickey($keyData['publicKey']);
            $publicKeyHash = md5($keyData['publicKey']);

            if (!openssl_sign($publicKeyHash,$signedKey,$privateKey)) { return false; }
            if (openssl_verify($publicKeyHash,$signedKey,$publicKey) !== 1) { return false; }

            $keyData['signedKey'] = base64_encode($signedKey);
            $keyData['rsa'] = base64_encode($keyDetails['rsa']['n']).'|'.bin2hex($keyDetails['rsa']['e']);
            unset($keyData['privateKey']);
            unset($keyData['session']);

            return $keyData;
        }

2 个答案:

答案 0 :(得分:3)

您要做的是用自定义JavaScript替换证书颁发机构签署的SSL证书的需要。我不是安全专家,但据我所知,简单的答案是这是不可能的。

基本的事实是,在公共互联网上,服务器无法信任客户端所说的内容,客户端无法信任服务器所说的内容,完全是因为中间人攻击。证书颁发机构必须开始的原因是建立某种公正的信任基础。浏览器供应商的CA是carefully vetted,它是目前公共互联网上唯一可用的信任,尽管肯定是not perfect

答案 1 :(得分:-1)

我很想知道为什么一个相对便宜的SSL证书(比如Digicert的1年只需175美元)是不可能的。特别是如果这是一个企业,175美元/年是一个合理的费用(它可以达到约12.60美元/月)。