[php] PHP를 사용하여 CSRF (Cross-Site Request Forgery) 토큰을 올바르게 추가하는 방법

내 웹 사이트의 양식에 보안을 추가하려고합니다. 양식 중 하나는 AJAX를 사용하고 다른 하나는 간단한 “문의하기”양식입니다. CSRF 토큰을 추가하려고합니다. 내가 가지고있는 문제는 토큰이 HTML “값”에 가끔 표시된다는 것입니다. 나머지 시간에는 값이 비어 있습니다. 다음은 AJAX 양식에서 사용중인 코드입니다.

PHP :

if (!isset($_SESSION)) {
    session_start();
$_SESSION['formStarted'] = true;
}
if (!isset($_SESSION['token']))
{$token = md5(uniqid(rand(), TRUE));
$_SESSION['token'] = $token;

}

HTML

 <form>
//...
<input type="hidden" name="token" value="<?php echo $token; ?>" />
//...
</form>

어떤 제안?



답변

보안 코드의 경우 다음과 같이 토큰을 생성하지 마십시오. $token = md5(uniqid(rand(), TRUE));

이것을 시도하십시오 :

CSRF 토큰 생성

PHP 7

session_start();
if (empty($_SESSION['token'])) {
    $_SESSION['token'] = bin2hex(random_bytes(32));
}
$token = $_SESSION['token'];

(!) 참고 : 하나의 내 고용주의 오픈 소스 프로젝트가 백 포트에 대한 이니셔티브 random_bytes()random_int()PHP 5 프로젝트에. MIT 라이센스가 있으며 Github 및 Composer에서 paragonie / random_compat 로 사용할 수 있습니다 .

PHP 5.3 이상 (또는 ext-mcrypt 사용)

session_start();
if (empty($_SESSION['token'])) {
    if (function_exists('mcrypt_create_iv')) {
        $_SESSION['token'] = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
    } else {
        $_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(32));
    }
}
$token = $_SESSION['token'];

CSRF 토큰 확인

단지 사용하지 마십시오 ==또는 ===사용 hash_equals()(PHP 5.6+ 만하지만과 이전 버전에서 사용할 해시의 compat 라이브러리).

if (!empty($_POST['token'])) {
    if (hash_equals($_SESSION['token'], $_POST['token'])) {
         // Proceed to process the form data
    } else {
         // Log this as a warning and keep an eye on these attempts
    }
}

양식 별 토큰으로 더 나아 가기

을 사용하여 특정 양식에서만 사용할 수 있도록 토큰을 추가로 제한 할 수 있습니다 hash_hmac(). HMAC는 더 약한 해시 함수 (예 : MD5)에서도 사용하기에 안전한 특정 키 해시 함수입니다. 그러나 대신 SHA-2 해시 함수 제품군을 사용하는 것이 좋습니다.

먼저 HMAC 키로 사용할 두 번째 토큰을 생성 한 다음 다음과 같은 논리를 사용하여 렌더링합니다.

<input type="hidden" name="token" value="<?php
    echo hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
?>" />

그런 다음 토큰을 확인할 때 합동 작업을 사용합니다.

$calc = hash_hmac('sha256', '/my_form.php', $_SESSION['second_token']);
if (hash_equals($calc, $_POST['token'])) {
    // Continue...
}

한 양식에 대해 생성 된 토큰은 알지 못하면 다른 컨텍스트에서 다시 사용할 수 없습니다 $_SESSION['second_token']. 페이지에 방금 놓은 토큰이 아닌 별도의 토큰을 HMAC 키로 사용하는 것이 중요합니다.

보너스 : Hybrid Approach + Twig Integration

Twig 템플릿 엔진 을 사용하는 사람은 누구나 Twig 환경에이 필터를 추가하여 단순화 된 이중 전략의 이점을 누릴 수 있습니다.

$twigEnv->addFunction(
    new \Twig_SimpleFunction(
        'form_token',
        function($lock_to = null) {
            if (empty($_SESSION['token'])) {
                $_SESSION['token'] = bin2hex(random_bytes(32));
            }
            if (empty($_SESSION['token2'])) {
                $_SESSION['token2'] = random_bytes(32);
            }
            if (empty($lock_to)) {
                return $_SESSION['token'];
            }
            return hash_hmac('sha256', $lock_to, $_SESSION['token2']);
        }
    )
);

이 Twig 함수를 사용하면 다음과 같이 범용 토큰을 모두 사용할 수 있습니다.

<input type="hidden" name="token" value="{{ form_token() }}" />

또는 잠긴 변형 :

<input type="hidden" name="token" value="{{ form_token('/my_form.php') }}" />

Twig는 템플릿 렌더링에만 관련됩니다. 여전히 토큰의 유효성을 올바르게 검증해야합니다. 제 생각에 Twig 전략은 최대의 보안 가능성을 유지하면서 더 큰 유연성과 단순성을 제공합니다.


일회용 CSRF 토큰

각 CSRF 토큰을 정확히 한 번만 사용할 수 있어야한다는 보안 요구 사항이있는 경우 가장 간단한 전략은 각 성공적인 유효성 검사 후에 토큰을 다시 생성합니다. 그러나 그렇게하면 한 번에 여러 탭을 탐색하는 사람들과 잘 섞이지 않는 모든 이전 토큰이 무효화됩니다.

Paragon Initiative Enterprises는 이러한 코너 케이스에 대해 Anti-CSRF 라이브러리 를 유지합니다 . 일회성 형식 별 토큰으로 만 작동합니다. 세션 데이터에 충분한 토큰이 저장되면 (기본 구성 : 65535) 가장 오래된 사용되지 않은 토큰이 먼저 순환됩니다.


답변

보안 경고 : md5(uniqid(rand(), TRUE))난수를 생성하는 안전한 방법이 아닙니다. 자세한 내용과 암호화 보안 난수 생성기를 활용하는 솔루션 은 이 답변 을 참조하십시오 .

if에 다른 것이 필요한 것 같습니다.

if (!isset($_SESSION['token'])) {
    $token = md5(uniqid(rand(), TRUE));
    $_SESSION['token'] = $token;
    $_SESSION['token_time'] = time();
}
else
{
    $token = $_SESSION['token'];
}


답변

변수 $token가 세션에있을 때 세션에서 검색되지 않습니다.


답변