Metin Kriptolama, Kütüphaneler, API Yöntemleri ve Örnekler

 

Metin Kriptolama

Kaynaklar

https://www.php.net/manual/en/refs.crypto.php

https://diptendud.medium.com/difference-between-aes-and-sha256-706d6b2eb2ef

https://encode-decode.com/glossary/encryption/

https://www.keyfactor.com/blog/symmetric-vs-asymmetric-encryption/

https://www.veriff.com/blog/types-of-authentication-methods

https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication

Metin Kriptolama Araçları

Metin kriptolama için yaygın olarak kullanılan araçlar şunlardır:

OpenSSL: Asimetrik ve Simetrik kriptolama yöntemlerini sağlar. API sistemleri için kullanılabilir. "OpenSSL is an open-source command line tool that is commonly used to generate private keys, create CSRs, install your SSL/TLS certificate, and identify certificate information."

Sodium Kütüphanesi: OpenSSL gibi Asimetrik ve Simetrik kriptolamanın yanında şifre hashleme ve dosya şifreleme/açma için de kullanılır. API sistemleri için kullanılabilir. "Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more. Its goal is to provide all of the core operations needed to build higher-level cryptographic tools."

Password Hashing (Şifre Gizleme/Hashleme) Fonksiyon ve Algoritmaları: Sunucu tarafı yazılım dillerinin sağladığı araçlardır. Temel olarak, şifrelerin sunucu veritabanında şifreli olarak kaydedilmesini ve buna uygun şekilde "Session Login" girişi yapılmasında kullanılır. Bunun yanında, adli işlemlerde bir dosyanın veri bütünlüğünün korunup korunmadığını anlamak için de kullanılır.

Metin Kriptolama Yöntemleri

1. Asimetrik Kriptolama:

OpenSSL

a. Anahtarların Oluşturulması

    
        //PHP
        
        $res = openssl_pkey_new(array('private_key_bits' => 2048));
        
        /* Extract the private key from $res to $privKey */
        openssl_pkey_export($res, $privKey);
        
        /* Extract the public key from $res to $pubKey */
        $pubKey = openssl_pkey_get_details($res);
        $pubKey = $pubKey["key"];
        
        /*Public Key Client sunucuya verilir. Client, mesajını bu anahtar ile şifreler. Her Clienta bu anahtar verilebilir. Çünkü, şifrelenen mesajı bu anahtarla açamazlar, yalnızca şifreleyebilirler.*/
        echo $pubKey;
        
        /*Private Key, sunucuda kalır. Şifreli mesaj yalnızca bu anahtarla açılabilir.*/
        echo $privKey;              
    
  

b. Public Key İle Mesajın Şifrelenmesi

    
        //PHP
        
        openssl_public_encrypt("Bu bir mesajdır.", $encrypted, $pubKey);
        
        echo $encrypted;        

    

c. Private Key İle Mesajın Çözümlenmesi

    
        //PHP
        
        openssl_private_decrypt($encrypted, $decrypted, $privKey);
        
        echo $decrypted;        
        
    

2. Simetrik Kriptolama:

OpenSSL

a. Mesajın Şifrelenmesi

    
        //PHP
        
        /*Initialization Vector-iv kriptonun benzersiz olmasını sağlayan rasgele bir değerdir. Kullanımda şifrelenmiş mesajla birlikte bu iv değerinin de sunucuya pas edilmesi gerekir ki sunucu mesajı çözümleyebilsin.*/
        $iv="12345";
        
        /*Şifrelenecek bilgiler*/        
        $params = new \stdClass();
        $params->param1="Text1";
        $params->param2="Text2";
        
        /*Şifreleme*/
        $algorithm = "AES-256-CBC";
        $api_token = base64_encode(openssl_encrypt(json_encode($params), $algorithm, $db_api_secret, $options=OPENSSL_RAW_DATA, $iv));
        
        /*Çıktı*/
        echo $api_token;        
    

b. Mesajın Çözümlenmesi
    
        //PHP
        
        /*Aynı algoritma ve iv değeriyle çözümleme*/
        $iv="12345";
        $algorithm = "AES-256-CBC";
        $decrypted = openssl_decrypt(base64_decode($api_token), $algorithm, $db_api_secret, OPENSSL_RAW_DATA, $iv);
        
        /*Aktarılan bilgiler*/
        $post=json_decode($decrypted);
        
        /*Çıktı*/
        echo $decrypted;        
    

Sodium Kütüphanesi

OpenSSL de aynı kelimelerin aynı şifreyi vermemesi için kullanılan iv değişkeni yerine, bu kütüphanede nonce değişkeni kullanılıyor.

a. Mesajın Şifrelenmesi
    
        //PHP
        
        $secret_key = sodium_crypto_secretbox_keygen();
        $message = 'Sensitive information';
        $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $encrypted_message = sodium_crypto_secretbox($message, $nonce, $secret_key);
        
        echo $encrypted_message;        
        
    
b. Mesajın Çözümlenmesi
    
        
        //PHP
        
        $decrypted_message = sodium_crypto_secretbox_open($encrypted_message, $nonce, $secret_key);
        
        echo $decrypted_message; 
        
    

3. Tek Yönlü Kriptolama (Hashing):

a. Metnin Şifrelenmesi - Örnek Fonksiyonlar

    
        
        echo hash('sha256', "Metin"); 
         
        echo hash('md5', "Metin");  
         
        echo md5("Metin"); 
         
        echo hash_hmac('sha256', "Metin", "Secret Password");
         
        echo password_hash("Metin", PASSWORD_DEFAULT);
         
        $options = [
          'cost' => 11
        ];
        echo password_hash("Metin", PASSWORD_BCRYPT, $options);
        
        /*Bir metinden bağımsız rasgele bir token oluşturma ihtiyacı olduğunda*/
        /*<PHP 7*/
        $token = bin2hex(openssl_random_pseudo_bytes(64));
        /*>PHP 7*/
        $token = bin2hex(random_bytes(64));
        
    

b. Şifreli Metnin Karşılaştırılması

    

        //PHP
        
        /*Bir metnin şifrelenmesi*/
        $hashed_value = hash('sha256', "Metin");
        
        /*Aynı metnin aynı yöntemle önceden şifrelenmiş hali*/
        $hashed_expected = "80422e38652c596485382fcbf77342f8b14b944d5817ec80db71891a0b8d8d92";
        
        /*Şifreli metinle gelen metnin şifrelenmiş halinin karşılaştırılması*/
        //if ($hashed_expected==$hashed_value){ //veya
        if (hash_equals($hashed_expected, $hashed_value) ) {
            echo "Hashes match!";
        }   
        
        
    

    

        //PHP
        
        /*Bir metnin şifrelenmesi*/
        $hash = password_hash("Metin", PASSWORD_DEFAULT);
        
        /*Metnin şifreli hali ile açık halinin karşılaştırılması*/
        if (password_verify("Metin", $hash)) {
            echo 'Password is valid!';
        }        
        
    

Kriptolamanın Kullanım Örnekleri

API Auth Yöntemleri

1. Basic Auth

Kullanıcı Tarafı

PHP ile

    client.php
    
    $username = 'demo';
    $password = '12345';
    
    $token = base64_encode("$username:$password");
    
    $ch = curl_init('https://demo.proctorstone.com/docs/kriptolama/basic/api.php');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Basic ' . $token,
    ]);
    
    $response = curl_exec($ch);
    
    if (curl_errno($ch)) {
        echo 'Curl error: ' . curl_error($ch);
    } else {
        echo $response;
    }
    
    curl_close($ch);       
    

JAVASCRIPT İle

    client.html
    
        
            
        <html> 
         <head> 
          <title> Ajax JavaScript Basic Auth Example </title> 
         </head> 
         <body>
          <!-- HTML5 Input Form Elements -->
          <button id="upload-button" onclick="submit()"> Submit </button>
          <input id="username" value="demo">
          <input id="password" value="12345">
        
          <!-- Ajax JavaScript File Upload Logic -->
          <script>
          
          /*Dosya yüklemenin kullanıcı adı ve şifre ile yapıldığı bir örnek.*/
          
          async function submit() {
        
            let username=document.getElementById("username").value;
            let password=document.getElementById("password").value;
            
            let headers = new Headers();
            //headers.append('Content-Type', 'text/json');
            headers.set('Authorization', 'Basic ' + btoa(username + ":" + password));
            
            /*Form bilgisi post edilmesi gerekirse aşağıdaki gibi edilebilir.*/
            // let formData = new FormData(); 
            // formData.append("file", fileupload.files[0]);  
            // formData.append("other_parameter", "other_value");   
            
            const response = await fetch('https://demo.proctorstone.com/docs/kriptolama/basic/api.php', {
                headers: headers,
                // credentials: 'user:passwd',
                /*Form bilgisi post edilmesi gerekirse aşağıdakiler kullanılmalıdır.*/
                // method: "POST", 
                // body: formData
            }); 
            const data = await response.text();
            console.log(data);
            alert(data);
          }  
          
          </script>
        
         </body> 
        </html>
        
        
    

Sunucu Tarafı

    api.php
    
  //PHP
        function getBasicToken() {
            $headers = getallheaders()['Authorization'];
            // HEADER: Get the access token from the header
            if (!empty($headers)) {
                $auth_array = explode(" ", $headers);
                $un_pw = explode(":", base64_decode($auth_array[1]));
                $un = $un_pw[0];
                $pw = $un_pw[1];
                
                $params = new \stdClass();
                $params->username=$un;
                $params->password=$pw;
                return $params;
            }
            return null;
        }
        
        if (isset($_SERVER['PHP_AUTH_USER'])) {
        
            $basic_user = getBasicToken()->username;
            /*Sifre kullanicidan acik geliyor.*/
            $basic_password = getBasicToken()->password;    
        
            /*DB de sifreyi hashli tutuyoruz. DB'den kullanici adina karsilik gelen hashli sifreyi alıp onu gelen sifreyle karsilatiracagiz. Asagidaki kısım DB den alinan hashli sifreyi simüle ediyor. Örneğimizdeki şifre 12345. Kullanıcı tarafından da açık olarak bu şifre gelmeli.*/
            $password_from_db=password_hash("12345", PASSWORD_DEFAULT);    

            /*Alternatif dogrulama fonksiyonları*/
            /*Kullanıcdan gelen şifreyi hashleyip db dekiyle karşılaştır.*/
            // $hashed_basic_password = password_hash($basic_password, PASSWORD_DEFAULT);
            // if ($hashed_basic_password == $password_from_db) {
            // if (hash_equals($hashed_basic_password, $password_from_db) ) {
            // if ($hashed_password == crypt($user_supplied_password, $hashed_password)) { 
            
            /*Gelem sifreyi veritabanındakiyle karsilastir. Burada kullanıcıdan gelen şifre açık haliyle, veritabanından gelen hashlenmiş şifre de aynı şekilde mevcut haliyle fonksiyona konuluyor.
            SIFRE VERITABANINDA HASHLENMIS HALIYLE TUTULMALIDIR.*/
            if (password_verify($basic_password, $password_from_db)) {
                echo 'Password is valid!';
            } 
            else {
                header('HTTP/1.0 401 Unauthorized');        
                header('WWW-Authenticate: Basic realm="My Website"');
                echo 'Access denied.';
                exit;
            }
        }
        
        //PHP
    

2. Bearer Token - OAuth2

Kullanıcı Tarafı

PHP ile

client.php
    
        //PHP
        /* API URL */
        $url = 'https://demo.proctorstone.com/docs/kriptolama/bearer/server.php';
            
        /* Init cURL resource */
        $ch = curl_init($url);
            
        /* Array Parameter Data */
        $data = ['command'=>'get_users', 'timestamp'=>$timestamp];
            
        /* pass encoded JSON string to the POST fields */
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
            
        /* set the content type json */
        $headers = [];
        $headers[] = 'Content-Type:application/xml';
        $token =  base64_encode("your_token");
        $headers[] = "Authorization: Bearer ".$token;
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            
        /* set return type json */
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            
        /* execute request */
        $result = curl_exec($ch);
             
        /* close cURL resource */
        curl_close($ch);
        
        echo $result;
        //PHP
        
    

JAVASCRIPT ile

client.html
    
        
        <script>
        
        async function post_token(){
            let token=btoa("your_token");
            let headers = new Headers();
            headers.set('Authorization', 'Bearer ' + token);
                
            let formData = new FormData(); 
            formData.append("command", "get_users"); 
            
            const response = await fetch('https://demo.proctorstone.com/docs/kriptolama/bearer/server.php', {
                headers: headers,
                method: "POST", 
                body: formData
            }); 
            
            const data = await response.text();
            console.log(data);
            alert(data);            
        }
        
        post_token();
        
        </script>       
        
    

Sunucu Tarafı

server.php
    
    //PHP
    function getBearerToken() {
        $headers = getallheaders()['Authorization'];
        // HEADER: Get the access token from the header
        if (!empty($headers)) {
            $auth_array = explode(" ", $headers);
            $token = base64_decode($auth_array[1]);    
            // You would typically validate the token and extract relevant information here
            return $token;
        }
        return null;
    }
    echo getBearerToken();
    //PHP
    

3. Parolalı Mesaj Gönderme - JWT:

Gönderilen bilginin gizlenmesi gerekmediği, fakat gönderen kişinin kimliğinin teyit edilmesi gereken durumlarda JWT kullanılabilir. JWT, mesajın header ve payload kısmıyla birlikte, bu iki kısmın imzalanmış halinin gönderilmesinden ibarettir. İmzalı kısım, gönderilen mesajın doğru kişi tarafından ve orijinal halinden değiştirilmeden gönderildiğini ispatlar.

a. JWT Oluşturma

    
        /*Source: https://dev.to/robdwaller/how-to-create-a-json-web-token-using-php-3gml*/
        
        // Create token header as a JSON string
        $header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
        
        // Create token payload as a JSON string
        $payload = json_encode([    
            'user_id' => 1,
            'role' => 'admin',
            'exp' => 1593828222
        ]);
        
        // Encode Header to Base64Url String
        $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
        
        // Encode Payload to Base64Url String
        $base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));
        
        // Create Signature Hash
        $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, 'abC123!', true);
        
        // Encode Signature to Base64Url String
        $base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
        
        // Create JWT
        $jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
        
        echo $jwt;        
    

b. JWT'yi Teyit Etme

    
        /*Source: https://developer.okta.com/blog/2019/02/04/create-and-verify-jwts-in-php*/
        
            $jwt = $argv[1];
            
            // split the token
            $tokenParts = explode('.', $jwt);
            $header = base64_decode($tokenParts[0]);
            $payload = base64_decode($tokenParts[1]);
            $signatureProvided = $tokenParts[2];
            
            // check the expiration time - note this will cause an error if there is no 'exp' claim in the token
            $expiration = Carbon::createFromTimestamp(json_decode($payload)->exp);
            $tokenExpired = (Carbon::now()->diffInSeconds($expiration, false) < 0);
            
            // build a signature based on the header and payload using the secret
            $base64UrlHeader = base64UrlEncode($header);
            $base64UrlPayload = base64UrlEncode($payload);
            $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret, true);
            $base64UrlSignature = base64UrlEncode($signature);
            
            // verify it matches the signature provided in the token
            $signatureValid = ($base64UrlSignature === $signatureProvided);
            
            echo "Header:\n" . $header . "\n";
            echo "Payload:\n" . $payload . "\n";
            
            if ($tokenExpired) {
                echo "Token has expired.\n";
            } else {
                echo "Token has not expired yet.\n";
            }
            
            if ($signatureValid) {
                echo "The signature is valid.\n";
            } else {
                echo "The signature is NOT valid\n";
            }        
       
    

Comments