FlawOpen

flawopen.com/Type Juggling/PHP ==

क्या hashes की तुलना के लिए PHP == सुरक्षित है?

CWE-697: Incorrect Comparisonसंदर्भ पृष्ठ
संक्षिप्त उत्तर

नहीं। == के साथ PHP दो संख्यात्मक strings की तुलना संख्यात्मक रूप से करता है, इसलिए वैज्ञानिक संकेतन वाली अलग strings PHP 8 में भी बराबर हो सकती हैं। पहले input का प्रकार जाँचें। सटीक मानों के लिए === और secret tokens या MAC के लिए hash_equals() उपयोग करें, ताकि सामग्री-निर्भर timing leakage न हो।

असुरक्षित
<?php
// Both strings are numeric and represent zero
$storedHash = "0e462097431906509019562988736854";
$submittedHash = "0e987654321";

// == returns true on PHP 7 and PHP 8
if ($storedHash == $submittedHash) {
    login();
}

// Request data may also have the wrong type:
$submitted = $_POST['token'] ?? null;
if ($storedHash == $submitted) {
    login();
}
// ?token[]=x supplies an array, not a string.
ठीक किया गया
<?php
$submitted = $_POST['token'] ?? null;
if (!is_string($submitted)) {
    http_response_code(400);
    exit;
}

// For secret tokens or MACs:
$tokenIsValid = hash_equals($storedToken, $submitted);
if ($tokenIsValid) {
    login();
}

// For non-secret identifiers, strict
// comparison checks both type and value:
$idsMatch = ($expectedId === $submitted);

// Passwords require password_verify(),
// not a hand-written hash comparison:
$passwordIsValid = password_verify($plain, $storedPasswordHash);

“Magic Hash” की समस्या

PHP 8 से पहले और बाद में दो संख्यात्मक strings की तुलना संख्यात्मक रूप से होती है। 0e से शुरू होकर केवल अंकों वाली hexadecimal hash वैध वैज्ञानिक-संकेतन string है जिसका मान शून्य है। उदाहरण के लिए, 0e462097431906509019562988736854 और 0e987654321 अलग strings हैं, पर दोनों शून्य दर्शाते हैं और == से बराबर हैं। PHP 8 ने string-to-number तुलना बदली, दो संख्यात्मक strings वाला यह मामला नहीं।

PHP 8 ने संख्या और गैर-संख्यात्मक string की तुलना बदली: अब string को शून्य मानने के बजाय उनके string रूपों की तुलना होती है। दो संख्यात्मक 0e… strings की क्लासिक तुलना नहीं बदली। किसी भी संस्करण में authentication के लिए == न उपयोग करें।

Request के प्रकार भी trust boundary हैं

Request पैरामीटर हमेशा strings नहीं होते। ?token[]=x जैसी query एक array बनाती है। इसे string API में देने से PHP संस्करण और फ़ंक्शन के अनुसार type error या अनपेक्षित code path हो सकता है। तुलना या hash_equals() से पहले request boundary पर string होना अनिवार्य करें।

मजबूत बचाव के दो हिस्से हैं: boundary पर input type जाँचें, फिर जरूरत के अनुसार तुलना चुनें। === type और value जाँचता है; hash_equals() secret strings की सामग्री-निर्भर timing leakage रोकता है।

अक्सर पूछे जाने वाले प्रश्न

क्या PHP 8 इसे पूरी तरह ठीक करता है?

नहीं। PHP 8 ने string-to-number तुलना बदली, लेकिन दो संख्यात्मक strings अब भी संख्यात्मक रूप से तुलना की जाती हैं। अलग 0e… strings बराबर हो सकती हैं। सटीक typed values के लिए ===, secrets के लिए hash_equals() और सभी समर्थित संस्करणों में type validation करें।

=== के बजाय hash_equals() क्यों?

PHP hash_equals() को string सामग्री से जुड़े timing attacks के विरुद्ध सुरक्षित बताता है; सामान्य तुलना का समय अंतर के स्थान पर निर्भर कर सकता है। पहले ज्ञात secret और फिर user string दें। Password के लिए password_verify() उपयोग करें।

क्या अन्य भाषाओं में भी ऐसा होता है?

JavaScript के == में भी अप्रत्याशित परिणाम हो सकते हैं; इसलिए सामान्यतः === की सलाह दी जाती है। PHP का विशिष्ट Magic Hash मामला hexadecimal strings और वैज्ञानिक संकेतन की तुलना से पैदा होता है।

संदर्भ

यह भी देखें क्या Rust का unsafe ब्लॉक मेमोरी-सुरक्षित है? क्या strncpy(), strcpy() से अधिक सुरक्षित है? CWE बनाम CVE: क्या अंतर है? Mass Assignment: जोखिम और सुरक्षित field allowlist Serverless डेटा रिसाव: अनुरोध के अनुसार अलगाव