CryptoPost ile HTTP POST Parametelerini RSA ile Şifrelemek

Merhaba

Web Uygulamalarına gelen saldırılar, HTTP talepleri ile gönderilen parametelerin değiştirilmesine dayanmaktadır. Örnek olarak aşağıdaki ekran görüntüsü analiz edildiğinde, gönderilen HTTP parametrelerinin isimleri ve değerleri From Data sekmesinde gözükmektedir. Bu değerler, Burp Suite gibi proxy yazılımlar ile durdurulup, değiştirilerek web uygulamasına saldırılar gerçekleştirilir.

http plain-text

 

CryptoPost Nedir ?

CryptoPost ben ve @abilgegunduz tarafından geliştirilmekte olan jquery temelli bir kütüphanedir. Kaynak kodlara https://github.com/mmetince/crypto_post adresinden erişilebilir.

CryptoPost, html sayfalarında bulunan form’ların submit butonuna basıldığında aktif hale gelerek o anda gönderilen HTTP talebinin POST parametelerini alır, RSA ile şifreler ve tüm clear-text değişkenleri silerek, yerlerine şifreli string’i yazar. Bu şekilde saldırganlar, hedef uygulama için üretilen POST taleplerinin içeriğini, parametreleri ve parametrelerin değerlerini görememektedirler.

Kullanımı

CryptoPost’un temelinde public ve private key bulunmaktadır. Kullanıcıya public key gönderilir. Tüm verileri bu key ile şifrelenmektedir. Sunucu tarafındaki uygulama ise gelen bu talepleri, sunucu tarafından saklı tutulan private key ile çözer. Bu sayede tüm parameteler saldırganlardan gizlenmiş olur.

Demo

CryptoPost’u kullanan basit bir PHP implementasyonu aşağıdadır. Aynı mantığı kullanarak kendi uygulamalarınıza entegre edebilirsiniz.

Client’a gönderilen view dosyası aşağıdadır. RSA Key’ler oluşturularak kullanıcının oturum bilgilerinde saklanır. Ardından sadece public key javascript değişkeni olarak kullanıcıya sunulur.

<?php
session_start();

include('lib/Crypt/RSA.php');

$rsa = new Crypt_RSA();
extract($rsa->createKey());
$_SESSION['private_key'] = $privatekey;
$_SESSION['public_key'] = $publickey;
?>
<html xmlns="http://www.w3.org/1999/html">
    <head>
        <script type="application/javascript" src="../../js/jquery-1.11.0.min.js"></script>
        <script type="application/javascript" src="../../js/jquery.cryptopost.js"></script>
        <script>
            var public_key = '<?php echo preg_replace('/\s+/', ' ', trim($_SESSION['public_key']));?>';
        </script>
    </head>
    <body>
        <div style="text-align: center">
            <form action="server.php" method="POST">
                <input type="name" name="field_one"/>
                <br>
                <input type="name" name="field_two"/>
                <br>
                <input type="submit" name="submit" value="Submit"/>
            </form>
        </div>
    </body>
</html>

jquery.cryptopost.js dosyasında ise aşağıdaki kodlar bulunmaktadır. Görüldüğü üzere tüm form’lara on metodu ile hook atılmıştır. Bu hook üzerinden gönderilen anlık HTTP talebine müdahile edilerek parametreler şifrelenmiştir.

    //Initialize of JSEncryption Object
    var crypt = new JSEncrypt();
    // We're setting public_key to cryption class. public_key defined demo.php file
    crypt.setPublicKey(public_key);

    // Capture all fomr element
    $('form').on('submit', function (event) {
        // Serialize and encrypt current form of post body
        var encrypted_data = crypt.encrypt($(this).serialize());
        // Delete all post parameters
        $(this).find('input').each(function(index, elm){
            $(this).remove();
        });

        // Append our new encrypted string into to the post body.
        $('<input />').attr('type', 'hidden')
            .attr('name', 'encrypted_data')
            .attr('value', encrypted_data )
            .appendTo($(this));
        // Send it.
    });

GÖnderilen HTTP talebine tekrar bakıldığında aşağıdaki şekilde şifrelenmiş olacaktır.

cryptopost

Sunucu tarafında çalışan server.php uygulaması aşağıdadır. SESSION’da saklanan ve kullanıcılar ile paylaşılmayan private_key ile gelen talep çözümlenmektedir. Ekrana gönderilen HTTP Talebinin body kısmı ve çözümlenilince elde edilen string yazdırılmaktadır.

<?php
// Session start to reach priv8 and public key.
session_start();

include('lib/Crypt/RSA.php');
$rsa = new Crypt_RSA();

// Setting private key to encryption object to decrypt post body.
$rsa->loadKey($_SESSION['private_key']);
// JS and PHP encryption class have to use same encryption method!
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
// Decryption of post body
$ciphertext = $rsa->decrypt(base64_decode($_POST['encrypted_data']));

echo "<pre>";
echo "<b>Post body</b><br>";
print_r(@$_REQUEST);
echo "</pre>";
echo "<b>Plain-text of post body.</b><br>";
echo $ciphertext;
?>

İleriki Adımlar

İlk fırsatta popüler frameworkler için -tabikide ASP.NET hariç!- destek verebilmeyi planlamaktayız. Bu noktada github üzerinden fork’lara ve yeni fikirlere açığız.