2011-02-15 36 views
0

在我的表单中我有一个隐藏字段:函数来设置的auth_token

<input type="hidden" name="auth_token" value="<?php echo $auth_token; ?>"> 

此值也存储在一个会话和变量:

$_SESSION['auth_token'] = hash('sha256', rand() . time() . $_SERVER['HTTP_USER_AGENT']); # TODO: put this in a function 
$auth_token = $_SESSION['auth_token']; 

当表单提交两个值进行比较。这是一个基本的表单令牌。

这应该做成两个函数还是只有一个重构? set_form_token()get_form_token(),get_form_token()返回会话值,然后我可以在我的主代码中进行比较。这样做的正确方法是什么?

编辑:

同时考虑乔尔L和RobertPitt的答案,我做这些:

function set_auth_token() 
{ 
    if (!isset($_SESSION['auth_token'])) 
    { 
     $_SESSION['auth_token'] = hash('sha256', rand() . time() . $_SERVER['HTTP_USER_AGENT']); 
    } 
} 

function get_auth_token() 
{ 
    if (isset($_SESSION['auth_token'])) 
    { 
     return $_SESSION['auth_token']; 
    } 
    else 
    { 
     die('No auth token.'); 
    } 
} 

function check_auth_token() 
{ 
    if (array_key_exists('auth_token', $_SESSION) && array_key_exists('auth_token', $_POST)) 
    { 
     if ($_SESSION['auth_token'] === $_POST['auth_token']) 
     { 
     # what happens if user fills the form in wrong first time(?) 
     $_SESSION['auth_token'] = hash('sha256', rand() . time() . $_SERVER['HTTP_USER_AGENT']); 
     } 
     else 
     { 
     return false; 
     } 
    } 
    else 
    { 
     return false; 
    } 
} 

然后我可以检查是否check_auth_token返回false与否,然后记录它的形式已经经过提交。这可以接受吗?

回答

1

在我的应用程序,我实际上有以下的辅助函数使用令牌:

generateToken() // generate and return hash, used in login process. 
       // hash then saved to session 

getToken() // returns user's token from session 

tokenField() // shortcut for echo '<input type="hidden" ... value="getToken()" />'; 
      // used in page templates 

checkToken() // get token from either 1) $_POST 2) request header or 3) $_GET 
      // and compare with getToken(). generate error if invalid. 

的checkToken()函数检查3个位置,因为该请求可以GET或POST,和任一这些的可能是通过AJAX。并且我有我的AJAX帮助程序自动将标记插入到每个请求的标题中)。

这样,我只需要在需要检查的地方拨打checkToken(),因此可以很容易地改变推测细节。

例如,我可以通过仅更改getToken()checkToken()开始使用一次性令牌。

如果您在代码中随处比较if (get_form_token() == $token),则没有这种灵活性。

+0

我只需要在两个地方检查(登录和注册),我不打算使用一次性令牌。 – 2011-02-15 17:04:15

0

首先,您应该准确理解工作流程,Joel L非常简单地解释这一点。

您应该封装在一个类的方法把一切都在一起,有些东西像SP:

class FormTokenizer 
{ 
    private $context = ""; 

    public function __construct($auth_token = "auth_token") 
    { 
     $this->context = $context; 
    } 

    public function generateToken() 
    { 
     $_SESSION[form_tokens][$this->context] = hash('sha256', rand() . time() . $_SERVER['HTTP_USER_AGENT']); 
     return $this; 
    } 

    public function getToken() 
    { 
     return isset($_SESSION[form_tokens][$this->context]) ? $_SESSION[form_tokens][$this->context] : false; 
    } 

    function generateField() 
    { 
     return sprintf('<input type="hidden" name="a_%s" value="%s">',$this->context,$this->getToken()); 
    } 

    public function validateToken() 
    { 
     if(isset($_POST["a_" . $this->context])) 
     { 
      return $this->getToken() == $_POST["a_" . $this->context]; 
     } 
     return false; 
    } 
} 

和简单的用法是:

$Token = new FormTokenizer("registration"); 

if(isset($_POST)) 
{ 
    if($Token->validateToken() === false) 
    { 
     //Token Onvalid 
    } 
} 

//Generate a fresh token. 
$hidden_input = $Token->generateToken()->generateField();