Analyse du principe de vérification csrf et de la solution de mise en cache des jetons du framework Yii2

little bottle
Libérer: 2023-04-06 11:06:01
avant
2260 Les gens l'ont consulté

Cet article est principalement divisé en trois parties. Premièrement, il présente brièvement CSRF, puis se concentre sur l'analyse du principe de vérification du framework Yii basé sur le code source, et propose enfin une solution réalisable pour la mise en cache des jetons causée par mise en cache des pages. Les points de connaissances concernés seront joints en annexe à la fin de l’article. Les amis intéressés peuvent le découvrir.

1. Description CSRF

CSRF signifie "Cross-Site Request Forgery" et est une attaque lancée au cours de la SESSION légitime de l'utilisateur. Les pirates intègrent un code de requête Web malveillant dans des pages Web et incitent les victimes à accéder à la page. Lors de l'accès à la page, la demande est lancée sous l'identité juridique de la victime, à son insu, et les actions attendues du pirate informatique sont exécutées. Le code HTML suivant fournit une fonction « supprimer le produit » :

<a href="http://www.shop.com/delProducts.php?id=100" "javascript:return confirm(&#39;Are you sure?&#39;)">Delete</a>
Copier après la connexion

En supposant que le programmeur n'effectue pas la vérification de légalité correspondante sur la demande de « suppression du produit » en arrière-plan, tant en tant qu'utilisateur Après avoir visité ce lien, le produit correspondant sera supprimé. Le pirate informatique peut alors tromper la victime en lui faisant visiter une page Web avec le code malveillant suivant, puis supprimer le produit correspondant à l'insu de la victime.

Le principe de vérification csrf de 2.yii/vendor/yiisoft/yii2/web/Request.php est abrégé en Request.php

/vendor /yiisoft/yii2/web/Controller.php est abrégé en Controller.php

Activer la vérification csrf

Définissez activateCsrfValidation sur true dans le contrôleur , alors toutes les opérations dans le contrôleur permettront la vérification. L'approche habituelle consiste à définir activateCsrfValidation sur false et à définir certaines opérations sensibles sur true pour activer la vérification partielle.

public $enableCsrfValidation = false;
/**
 * @param \yii\base\Action $action
 * @return bool
 * @desc: 局部开启csrf验证(重要的表单提交必须加入验证,加入$accessActions即可
 */
public function beforeAction($action){
    $currentAction = $action->id;
    $accessActions = [&#39;vote&#39;,&#39;like&#39;,&#39;delete&#39;,&#39;download&#39;];
    if(in_array($currentAction,$accessActions)) {
        $action->controller->enableCsrfValidation = true;
    }
    parent::beforeAction($action);
    return true;
}
Copier après la connexion

Générer le champ de jeton

dans Request.php

passez d'abord la sécurité Le composant Security obtient une chaîne aléatoire de 32 bits et la stocke dans un cookie ou une session. Il s'agit d'un jeton natif

/**
 * Generates  an unmasked random token used to perform CSRF validation.
 * @return string the random token for CSRF validation.
 */
protected function generateCsrfToken()
{
    $token = Yii::$app->getSecurity()->generateRandomString();
    if ($this->enableCsrfCookie) {
        $cookie = $this->createCsrfCookie($token);
        Yii::$app->getResponse()->getCookies()->add($cookie);
    } else {
        Yii::$app->getSession()->set($this->csrfParam, $token);
    }
    return $token;
}
Copier après la connexion

Ensuite, grâce à une série d'opérations de remplacement de chiffrement, le chiffrement_. csrfToken, c'est le jeton transmis au navigateur. Générez d'abord aléatoirement le masque de chaîne de longueur CSRF_MASK_LENGTH (la valeur par défaut est de 8 bits dans Yii2)

et effectuez les opérations suivantes sur le masque et le jeton str_replace(&#39;+&#39;, &#39;.&#39;, base64_encode($mask . $this->xorTokens($token, $mask))); $this->xorTokens($arg1,$arg2)

/**
 * Returns the XOR result of two strings.
 * If the two strings are of different lengths, the shorter one will be padded to the length of the longer one.
 * @param string $token1
 * @param string $token2
 * @return string the XOR result
 */
private function xorTokens($token1, $token2)
{
    $n1 = StringHelper::byteLength($token1);
    $n2 = StringHelper::byteLength($token2);
    if ($n1 > $n2) {
        $token2 = str_pad($token2, $n1, $token2);
    } elseif ($n1 < $n2) {
        $token1 = str_pad($token1, $n2, $n1 === 0 ? &#39; &#39; : $token1);
    }
    return $token1 ^ $token2;
}
public function getCsrfToken($regenerate = false)
{
    if ($this->_csrfToken === null || $regenerate) {
        if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
            $token = $this->generateCsrfToken();
        }
        // the mask doesn&#39;t need to be very random
        $chars = &#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.&#39;;
        $mask = substr(str_shuffle(str_repeat($chars, 5)), 0, static::CSRF_MASK_LENGTH);
        // The + sign may be decoded as blank space later, which will fail the validation
        $this->_csrfToken = str_replace(&#39;+&#39;, &#39;.&#39;, base64_encode($mask . $this->xorTokens($token, $mask)));
    }

    return $this->_csrfToken;
}
Copier après la connexion
. 🎜> Il s'agit d'une opération XOR de premier remplissage

Jeton de vérification

/**
 * @inheritdoc
 */
public function beforeAction($action)
{
    if (parent::beforeAction($action)) {
        if ($this->enableCsrfValidation && Yii::$app->getErrorHandler()->exception === null && !Yii::$app->getRequest()->validateCsrfToken()) {
            throw new BadRequestHttpException(Yii::t(&#39;yii&#39;, &#39;Unable to verify your data submission.&#39;));
        }
        return true;
    }
    
    return false;
}
public function validateCsrfToken($token = null)
{
    $method = $this->getMethod();
    if (!$this->enableCsrfValidation || in_array($method, [&#39;GET&#39;, &#39;HEAD&#39;, &#39;OPTIONS&#39;], true)) {
        return true;
    }

    $trueToken = $this->loadCsrfToken();//如果开启了enableCsrfCookie,CsrfToken就从cookie里取,否者从session里取(更安全)

    if ($token !== null) {
        return $this->validateCsrfTokenInternal($token, $trueToken);
    } else {
        return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
            || $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
    }
}
Copier après la connexion
Appelez la méthode validateCsrfToken dans request.php dans le contrôleur. php

$this->getBodyParam($this->csrfParam)
Copier après la connexion
Obtenez le client entrant

private function validateCsrfTokenInternal($token, $trueToken)
{
    if (!is_string($token)) {
        return false;
    }
    $token = base64_decode(str_replace(&#39;.&#39;, &#39;+&#39;, $token));
    $n = StringHelper::byteLength($token);
    if ($n <= static::CSRF_MASK_LENGTH) {
        return false;
    }
    $mask = StringHelper::byteSubstr($token, 0, static::CSRF_MASK_LENGTH);
    $token = StringHelper::byteSubstr($token, static::CSRF_MASK_LENGTH, $n - static::CSRF_MASK_LENGTH);
    $token = $this->xorTokens($mask, $token);

    return $token === $trueToken;
}
Copier après la connexion
puis validezCsrfTokenInternal

str_replace(&#39;+&#39;, &#39;.&#39;, base64_encode(mask.mask.this->xorTokens(token,token,mask)));

Utilisé pour le cryptage est
token1=token^mask
Copier après la connexion
Décryptage 1. Remplacez d'abord . par + 2. Puis base64_decode puis retirez masque et masque et this->xorTokens(token,token,mask) selon la longueur respectivement pour la commodité de ; explication this−>xorTokens(this−>xorTokens(token, $mask) est ici appelé token1 puis effectuez l'opération XOR de mask et token1 pour obtenir le jeton. Notez que lors du cryptage

token=mask^token1=mask^(token^mask)
Copier après la connexion
Ainsi lors du décryptage

3.Solution de mise en cache des jetons

Lorsque la page entière est mise en cache, le jeton est également mis en cache. La mise en cache provoque l'échec de la vérification. Une solution courante consiste à réobtenir le jeton avant chaque soumission, afin que la vérification puisse réussir , cette fonction renvoie le résultat de l'entrée complétée au spécifié. length à partir de l'extrémité gauche, de l'extrémité droite ou des deux extrémités. Si le paramètre facultatif pad_string n'est pas spécifié, l'entrée sera complétée par des espaces, sinon elle sera complétée par pad_string à la longueur spécifiée ; La fonction 🎜>

brouille une chaîne, en utilisant n'importe quel schéma de tri possible. Le cryptage et le déchiffrement de la vérification yii2 csrf impliquent une opération XOR str_pad()

, vous devez donc le faire. Complétez d'abord les connaissances pertinentes sur l'opération XOR de chaîne en PHP. Si vous n'en avez pas besoin, vous pouvez ignorer str_shuffle()

^ Si l'opération XOR est différente, elle renvoie 1, sinon elle renvoie. 0. En langage PHP, il est souvent utilisé pour les opérations de chiffrement. Le décryptage utilise également ^ directement lors de l'exécution d'opérations sur les chaînes, le code ascii du caractère est converti en 2. Base pour calculer l'opération sur un seul caractère

1. Pour un seul caractère et un seul caractère, le résultat peut être directement calculé, comme a^b

dans le tableau 2. comme ab^cd dans le tableau, calculer le résultat correspondant à a^c et concaténer les caractères correspondant au résultat correspondant à b^d

Tutoriels associés : Tutoriel vidéo PHP

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:cnblogs.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal