[php] SSL через Javascript


0 Answers

Мне любопытно узнать, почему относительно недорогой сертификат SSL (например, 1 год от Digicert по цене 175 долларов США) не может быть и речи. Особенно, если это для бизнеса, $ 175 / год - разумный расход (он составляет около 12,60 долларов США в месяц).

Question

Я видел несколько подобных вопросов, которые, похоже, не подходят для моего конкретного случая использования, и я ДУМАЮ, Я понял ответ, но я полный noob, когда дело доходит до безопасности, RSA и в значительной степени все, что с ним связано. У меня есть основное знакомство с концепциями, но все фактические реализации, которые я сделал до этого момента, касались редактирования чужого кода, а не создания собственного. Во всяком случае, вот где я:

Я знаю, что Javascript является по сути плохим местом для шифрования. Кто-то может реагировать и реагировать на JS, чтобы вы отправили незашифрованные данные по проводке. Это ДОЛЖНО выполняться через соединение HTTPS SSL / TLS, но такой хостинг стоит денег, и поэтому официальные подписанные сертификаты должны реалистично идти с соединением.

При этом я думаю, что способ, которым я собираюсь это сделать, обходит недостаток JS-шифрования Man-in-the-Middle в силу того факта, что я только когда-либо шифровал одну вещь (хэш пароля) для одного RESTful сервисный вызов, а затем только использование этого хеша пароля для подписи запросов от клиента, чтобы аутентифицировать их как исходящие от пользователя запросы. Это означает, что JS несет ответственность только за шифрование хэша паролей один раз при создании учетной записи пользователя, и если сервер не может декодировать этот шифр, то он знает, что это было.

Я также собираюсь сохранить некоторую информацию о клиенте, в частности $_SERVER['REMOTE_ADDR'] чтобы гарантировать, что кто-то не делает MitM регистрационным обменом сам.

Я использую функции openssl_pkey_ PHP для генерации асимметричного ключа и библиотеки 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;
        }



Related