الأحد، 27 أبريل، 2014

التوكن "Token"



السلآم عليكم !

كمآ وعدتكم اليوم سوف أقوم بشرح موضوع عن التوكن "Token" وسوف أشرح بعض من فوائده  !


أولآ مآ هو التوكن :

التوكن عبارة عن رقم معين أو جلسة خآصة بكل مستخدم وبكل صفحة وبكل وقت "الوقت يتم تحديده حسب الكود البرمجي للتوكن!"


ثانيا مآ فوائد التوكن :

سوف أشرح بعض فوائده !

1. الوقوف أمام أنواع كثيرة من الثغرات الخطيرة مثل : XSRF CSRF
2. الامان للمستخدم والمستفيد خصوصا في المواقع الكبيرة أو مواقع البيع والشراء



شرح التوكن وبرمجة و تطبيق عملي :

أولآ رآح أقوم بتنزيل كود برمجي للتوكن وهو عبارة عن مصدر مفتوح أو مجاني !

لنضع الكود البرمجي التالي في ملف باسم token.class.php :

رمز PHP:
<?php /* This file is really *free* software, not like FSF ones.
*  Do what you want with this piece of code, I just enjoyed coding, don't care.
*/

/**
* Provides a way to prevent Session Hijacking attacks, using session tokens.
*
* This class was written starting from Claudio Guarnieri's {@link http://www.playhack.net Seride}. I took Claudio's code and learned how it works; then I wrote my own code tring to improve his.
* @author Francesco Ciracì <hide@address.com>
* @link http://sydarex.org
* @version 0.2
* @copyright Copyleft (c) 2009/2010 Francesco Ciracì
*/

/**
* Token class.
*
* Provides a way to prevent Session Hijacking attacks, using session tokens.
*
* This class was written starting from Claudio Guarnieri's {@link http://www.playhack.net Seride}. I took Claudio's code and learned how it works; then I wrote my own code tring to improve his.
*
* @author Francesco Ciracì <hide@address.com>
* @copyright Copyleft (c) 2009, Francesco Ciracì
*/ 
class Token {

    
/**
     * Stores the question ID.
     *
     * @access private
     * @var integer
     */
    
private $timeout;

    
/**
     * Stores the new token.
     *
     * @access private
     * @var string
     */
    
private $token null;

    
/**
     * Stores the error code raised by Check method.
     *
     * The possible values are:
     * 0 No error detected.
     * 1 No Request Token detected.
     * 2 No Session Token corrisponding to the Request Token.
     * 3 No value for the Session Token.
     * 4 Token reached the timeout.
     * @access private
     * @var integer
     */
    
private $error 0;

    
/**
     * Class constructor. Sets class vars, and starts the session if it isn't started yet. Generates the new token.
     *
     * @param integer $timeout token timeout period, in minutes. Default 5 minutes.
     */
    
function __construct($timeout=5) {
        
$this->timeout $timeout;
        if(!isset(
$_SESSION)) session_start();
        
$this->tokenSet();
    }

    
/**
     * Generates tokens. Generation tecnique taken from {@link http://www.playhack.net Seride}.
     *
     * @access private
     */
    
private function tokenGen() {
        
// Hashes a randomized UniqId.
        
$hash sha1(uniqid(rand(), true));
        
// Selects a random number between 1 and 32 (40-8)
        
$n rand(132);
        
// Generate the token retrieving a part of the hash starting from the random N number with 8 of lenght
        
$token substr($hash$n8);
        return 
$token;
    }

    
/**
     * Destroys token.
     *
     * @param string $token the token to destroy.
     */
    
function tokenDel($token) {
        unset(
$_SESSION[$token]);


    }

    
/**
     * Destroys all tokens except the new token.
     */
    
function tokenDelAll() {
        
$sessvars array_keys($_SESSION);
        
$tokens = array();
        foreach (
$sessvars as $var) if(substr_compare($var,"spackt_",0,7)==0$tokens[]=$var;
        unset(
$tokens[array_search("spackt_".$this->token,$tokens)]);
        foreach (
$tokens as $token) unset($_SESSION[$token]);
    }

    
/**
     * Sets token.
     *
     * @access private
     */
    
private function tokenSet() {
        
$this->token $this->tokenGen();
        
$_SESSION["spackt_".$this->token] = time();
    }

    
/**
     * Sets a token to protect a form. In fact, it prints a hidden field with the token.
     */
    
function protectForm() {
        echo 
"<input type=\"hidden\" name=\"spack_token\" value=\"".$this->token."\" />";
    }

    
/**
     * Sets a token to protect a link. In fact, it puts in the querystring the token. Returns the protected link.
     *
     * @param string $link the link to protect.
     * @return string
     */
    
function protectLink($link) {
        if(
strpos($link,"?")) return $link."&spack_token=".$this->token;
        else return 
$link."?spack_token=".$this->token;
    }

    
/**
     * Checks if the request have the right token set; after that, destroy the old tokens. Returns true if the request is ok, otherwise returns false.
     */
    
function Check() {
        
// Check if the token has been sent.
        
if(isset($_REQUEST['spack_token'])) {
            
// Check if the token exists
            
if(isset($_SESSION["spackt_".$_REQUEST['spack_token']])) {
                
// Check if the token isn't empty
                
if(isset($_SESSION["spackt_".$_REQUEST['spack_token']])) {
                    
$age time()-$_SESSION["spackt_".$_REQUEST['spack_token']];
                    
// Check if the token did not timeout
                    
if($age $this->timeout*60$this->error 4;
                }
                else 
$this->error 3;
            }
            else 
$this->error 2;
        }
        else 
$this->error 1;
        
// Anyway, destroys the old token.
        
$this->tokenDelAll();
        if(
$this->error==0) return true;
        else return 
false;
    }

    
/**
     * Gets the error code.
     */
    
function Error() {
        return 
$this->error;
    }
 }
 
?>

الآن لنقوم بشرح بعض النقاط في الكود البرمجي التالي !

^ أولآ الكود مبرمج بلغة الـ php كمآ ترون شرح بسيط عن Token وفوائده

والكود السابق عبارة عن class ودآخله أكثر من function والسبب لتسريع استدعاء الاومر والتأكد والتسهيل على المبرمج في كثير من الأمور !!

لنرى مآ هو مهم في الكود واللي يستطيع تعديله المستخدم العادي !

لنرى المقطع التالي من الكود :

رمز PHP:
    private function tokenGen() {
        
// Hashes a randomized UniqId.
        
$hash sha1(uniqid(rand(), true));
        
// Selects a random number between 1 and 32 (40-8)
        
$n rand(132);
        
// Generate the token retrieving a part of the hash starting from the random N number with 8 of lenght
        
$token substr($hash$n8);
        return 
$token;
    }  

في الجزء السابق function المحدد واللي اسمه tokenGen يقوم بتوليد كود التحقق "التوكن" شرح بسيط عن Token وفوائده

طبعا التوكن كما تعلمون هو عبارة عن هاش أو عدد عشوائي "من الممكن جعله مكون من حروف أيضا!"

 تشرح لنا الكود السابق بشرح سريع وحلو بسيط Token وفوائده
الكود السابق يقوم اولآ بتكوين هآش بشفرة
sha1
ورقم عشوائي بين 1 و 32 ثم يكون التوكن بينها !

^ عن نفسي في الكود البرمجي السابق مشكلة !

في التطبيق سوف نرا أن الكود ممكن أن يجعل التوكن عبارة عن عدد أو رمز وآحد فقط أو أكثر بقليل في بعض الحآلآت !
لذا في آخر التطبيق سوف أقوم بتعديل بسيط عالكود + الشفرة لجعله أسهل وثابت وقوي بسيط Token وفوائده

طيب الآن حفظنا الكود كيف نقوم بجعله مفعل ؟

أولآ لنرى الملفان اللذان نريد حمايتهما بالتوكن :

اول ملف هو : test.php

والملف الثاني هو : buy.php

الملف يحوي على الكود التالي :
رمز PHP:
<form action="buy.php" method="post">
<
br /> Amount: <input type="text" name="amount">
<
input type="submit" value="Send">
</
form>  

والملف الثاني يحوي عالكود التالي :

رمز PHP:
<?php
    
echo "Thx For Buying!"?>
طيب الآن نريد أن يكون في الملف الأول كود التوكن !

والملف الثاني كود التأكد من التوكن ليتم الشراء !!

أولآ لنقوم باستدعاء ملف الكلآس والفنكشنز تبع كود التوكن "token.class.php"

بالكود التالي :

رمز PHP:
include_once 'token.class.php';  
ولنقوم بعمل جلسة في كل صفحة بالكود التالي :

رمز PHP:
session_start();  

طيب الآن الملف مكون من كلآس وفنكشنز !!

كيف نقوم باستدعائها ؟ عبر الكود التالي وسوف أشرحه شرح بسيط :
رمز PHP:
$rs = new Token$rs ->protectForm();  

حددنا اسم الكلآس في المتغير rs وثم استدعينا الفنكشن المعين واللذي هو protectForm لأنه في كود التوكن "token.class.php"

الفنكشن protectForm عبارة عن كود يقوم بطباعة فورم ويكون الفورم مخفي ومحتواه رقم التوكن !

الآن محتوى الملف الأول "test.php" هو :

رمز PHP:
<?php
session_start
();
include_once 
'token.class.php'?> <form action="buy.php" method="post">
<?php
$rs 
= new Token$rs->protectForm(); ?> <br />
Amount: <input type="text" name="amount">
<!-- Other Fields -->
<input type="submit" value="Send">
</form>

طيب الآن لنتأكد من التوكن في الملف الثاني "buy.php"

أولآ نستعدي ملف الكلآس :

رمز PHP:
include_once 'token.class.php';  
ثم نحدد الكلآس + الفنكشن وسوف نقوم بتسمة المتغير الخآص في الكلآس هو باسم hk

رمز PHP:
$hk= new Token(2);  

مهم في الكود السابق أضفنا مع اسم الكلآس رقم !

الرقم هذا عبارة عن الوقت المحدد للتوكن في الصفحة !

ووضعت أنا 2 أي دقيقتين طبعا على حسب وفي الكود تبع الكلآس الوقت الافتراضي هو 5 دقايق !
^ إذا لم تقم بتحديد الوقت "Time Out"

الآن لنتحقق من التوكن في الكود التالي :

if($hk->Check()) {
رمز PHP:
  
استدعينا الفنكشن Check من الكلآس اللذي حددناه في الكود السابق والذي هو Token

الآن كيف نتحقق بدمجه مع الصفحة ؟؟

طبعا بالثابت if كالتالي :
رمز PHP:
if($hk->Check()) {
    echo 
"Thx For Buying!";
   }
else {
  die(
header("Location: login.php?error=session"));
  }  
في الكود السابق قلت لو كآن التوكن صحيح اطبع "Thx For Buying!" ولو كآن التوكن خآطئ قم بالذهاب للصفحة login.php?error=session

إذن الآن الكود الكلي للملف الثاني "buy.php" هو :


رمز PHP:
<?php include_once 'token.class.php'$hk = new Token();
if(
$hk->Check()) {
    echo 
"Thx For Buying!";
   }
else {
  die(
header("Location: login.php?error=session"));
  } 
?>
الآن لو تذهب للصفحة الأولى "test.php" وتفتح السورس تبع الصفحة سوف تجد التالي :

رمز PHP:
<input type="hidden" name="spack_token" value="80ac9d228e8553f9014bc911c2b9b00e" /><br />  

الكود أو المحتوى "value" يتغير حسب كل مستخدم "عشوائي"!


طبعا المشكلة أنه من الممكن أن يكون التوكن حرف وآحد كما قلت سابقا في بداية الموضوع !

ولحل المشكلة نقوم بالتعديل على الفنكشن tokenGen :

واللي هو عبارة عن الكود التالي :

رمز PHP:
    private function tokenGen() {
        
// Hashes a randomized UniqId.
        
$hash sha1(uniqid(rand(), true));
        
// Selects a random number between 1 and 32 (40-8)
        
$n rand(132);
        
// Generate the token retrieving a part of the hash starting from the random N number with 8 of lenght
        
$token substr($hash$n8);
        return 
$token;
    }  
ونقوم بتبديله بالآتي :
رمز PHP:
        private function tokenGen() {
        
// Hashes a randomized UniqId.
//        $hash = md5(uniqid(rand(), true));
        // Selects a random number between 1 and 32 (40-8)
//        $n = rand(1, 32);
        // Generate the token retrieving a part of the hash starting from the random N number with 8 of lenght
//        $token = substr($hash, $n, 59);
        
$token md5(uniqid(rand(), true));
        return 
$token;
    }  
التعديل بسيط وهو جعل التوكن عبارة عن كود عشوائي بتشفيرة MD5 بسيط Token وفوائده

على فكرة من الممكن استعمال الكلآس تبع التوكن في أكثر من طريقة مثال عالسريع كالتالي :

رمز PHP:
<?php // Example of a page protected from Session Riding (CSRF) by Token.

// Including Token class file 
include_once "token.class.php"// Getting an istance of Token; the new token will be created here. $T = new Token(2); // We choose, in this example, two minutes of timeout for our tokens. In a true website, two minutes will be not enough, probably.

// Now, We'll check how the situation is.
// The Check method returns true if the request token is right; false otherwise.
// Obviously, if the page is not supposed to be protected, the return value of Check doesn't matters to you!
if($T->Check()) { // If Check goes right, the page have got the right token.
    
echo "Allright, the token is fine!";
}
else { 
// Else, there is some error..
    // We can catch the error with the Error method.
    // It returns an integer error code; see the documentation to find out the meanings of the various codes.
    
switch($T->Error()) {
        case 
1: { echo "No Request Token detected."; break; }
        case 
2: { echo "No Session Token corrisponding to the Request Token."; break; }
        case 
3: { echo "No value for the Session Token."; break; }
        case 
4: { echo "Token reached the timeout."; break; }
    }
// Now, let's print a link to this page using the token! echo "<br /><a href=\"".$T->protectLink("example.php")."\">Retry using the session token!</a>"?>

0 التعليقات:

إرسال تعليق

لا تنسى ان تشارك samir soltani بتعليقك
او نشر الموظوع جزاك الله خيرا