1

連想配列の構造についての理解が間違っているに違いないことは十分に認めます。

次のログイン スクリプトは、SQL Server データベース (具体的には Azure SQL) からクエリされた $username のハッシュされたパスワードとソルトで構成される連想配列を $userdata に入力します。ただし、提供されたパスワードのハッシュを作成し、DB で見つかったハッシュされたパスワードと比較するコードの部分は、$userdata[password] と $userdata[salt] が未定義であることを示すエラーで失敗します。

    <?php

    $username = $_POST['username'];  
    $password = $_POST['password'];

    // Connect to SQL Server
    include '../../phpconfig/connectstrings.php';

    try  
    {  
$conn = new PDO ( "sqlsrv:server = $serverstringname; Database = $databasestringname", "$usernamestringname", "$passwordstringname");  
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION, );  
    }

    catch ( PDOException $e )  
    {  
    print( "Error connecting to SQL Server." );  
    die(print_r($e));  
    }

    catch(Exception $e)  
    {  
    die(var_dump($e));  
    }


    //Query database for the hashed password and salt for the supplied username
    if(!empty($_POST)) {

        try
    {  
    $sql_select = $conn->prepare("SELECT password, salt FROM logins WHERE username = '$username'");  
    $sql_select->execute();  
    }
    catch(Exception $e)
    {
    die(var_dump($e));  
    }


    //Fetch all of the remaining rows in the result set  
    $userdata = $sql_select->fetchAll(PDO::FETCH_ASSOC);

    //check for a valid username  
    if(empty($userdata))  
    {  
    echo "User: $username was not found";  
    die;  
    }  

    //hash the queried salt and hash the supplied password  
    $hash = hash('sha256', $userdata['salt'] . hash('sha256', $password) );  

    //compare the hashed salted password supplied with that queried from database  
    if($hash = $userdata['password'])  
    {  
    echo "Welcome, $username!";  
    }  
    else  
    {  
    echo "Invalid password";  
    }  
                    }

    ?>

$sql_select から配列をフェッチする以外のコードの一部にデバッグが必要であることは間違いありませんが、 $userdata は変数の単一部分に割り当てられたすべての連想配列データを取得しているように見えるため、そこまではできません。次のダンプの出力:

    var_dump($sql_select);  
    //output = object(PDOStatement)#2 (1) { ["queryString"]=> string(61) "SELECT password, salt FROM logins WHERE username = 'mrtactics'" } 

    list($a[0], $b[1]) = $userdata;  
    var_dump($a);  
    var_dump($b);  
    //output = array(1) { [0]=> array(2) { ["password"]=> string(64) "f24704c0ce72a618cf1738894ebdd6001f4d3329802ab83bd418df66cbc46b1a" ["salt"]=> string(3) "6e0" } } array(1) { [1]=> NULL }

    var_dump($userdata["salt"]);  
    //output = NULL

    var_dump($userdata['salt']);  
    //output = NULL

    var_dump($userdata['password']);  
    //output = NULL

    foreach ($userdata as $item => $value)  
    echo "$item: $value<br>";
    //output = 0: Array

    $password = $sql_select->fetchColumn(0);  
    $salt = $sql_select->fetchColumn(1);  
    var_dump($password);  
    var_dump($salt);  
    //output = string(64) "f24704c0ce72a618cf1738894ebdd6001f4d3329802ab83bd418df66cbc46b1a" bool(false)

明らかな回避策は、提供されたユーザー名に対して単一の値を照会し、それぞれの変数をそれぞれ継承することです。ただし、これには DB への 2 倍の呼び出しが必要であり、連想配列がどのように構築されるか、および連想配列に格納されている情報をどのように使用できるかについては何も知りません。

取得しようとしているメソッドに対して間違った構造のオブジェクトをフェッチしているか、構文が単に悪いのではないかと思います。sql_* コマンドではなく、PDO を引き続き使用するつもりです。

編集:これをもっと簡単にしましょう:

    $userdatasql = $sql_select->fetchAll(PDO::FETCH_ASSOC);
    $userdata['password']="f24704c0ce72a618cf1738894ebdd6001f4d3329802ab83bd418df66cbc46b1a";
    $userdata['salt']="6e0";
    var_dump($userdata);
    var_dump($userdatasql);
    var_dump($userdata['password']);
    var_dump($userdatasql['password']);

    //Dump of $userdata = array(2) { ["password"]=> string(64) "f24704c0ce72a618cf1738894ebdd6001f4d3329802ab83bd418df66cbc46b1a" ["salt"]=> string(3) "6e0" }
    //Dump of $userdatasql = array(1) { [0]=> array(2) { ["password"]=> string(64) "f24704c0ce72a618cf1738894ebdd6001f4d3329802ab83bd418df66cbc46b1a" ["salt"]=> string(3) "6e0" } }

これら 2 つの配列の構造の違いに注意してください。それが何を意味するのか正確にはわかりませんが、それが私がここにいる理由です。私が推測していた場合、$userdatasql 配列には配列内に配列が含まれているように見えるため、呼び出しにはそのようにインデックスを付ける必要があります。

    //Dump of $userdata['password'] = string(64) "f24704c0ce72a618cf1738894ebdd6001f4d3329802ab83bd418df66cbc46b1a"
    //Dump of $userdatasql['password'] = NULL

より詳しい情報:

    echo (count($userdata));
    echo (count($userdatasql));
    //output = 2
    //output = 1
    echo (count($userdata, 1));
    echo (count($userdatasql, 1));
    //output = 2
    //output = 3

これは、PDO FETCH_ASSOC によって作成された配列が、手動で作成された配列とは異なる構造であることを示していますが、同じ 2 つのデータと同じ 2 つのインデックスを含んでいます。

この知識を武器に、0 インデックスの場所を含むようにダンプを変更したところ、予期されたデータが突然出力されました。

    var_dump($userdatasql['0']['password']);
    var_dump($userdatasql['0']['salt']);
    //password dump = string(64) "f24704c0ce72a618cf1738894ebdd6001f4d3329802ab83bd418df66cbc46b1a"
    //salt dump = string(3) "6e0"

これは、すべての PDO FETCH ASSOC 配列をインデックスで参照する必要があるということですか?
私が見つけたコード例がこれを示していないので、私は考えるべきではありません。
では、なぜ PDO FETCH ASSOC 配列の形式が正しくないのでしょうか?

4

1 に答える 1

1

まあ、私は連想配列から必要な情報を取得するために構文をフォーマットできるという意味で「答え」を持っています。手動で作成された連想配列と PDO FETCH ASSOC によって作成された連想配列の違いがわかりません。また、配列がここで提示されたものよりも大幅に複雑になった場合に、後でどのような影響があるかわかりません。

しかし、ここに「答え」があります:

PDO FETCH ASSOC によって作成された連想配列に格納された情報は、数値型ではない連想配列であるにもかかわらず、数値インデックス THEN 連想インデックスによって参照される必要があります (それは意味を成すためですよね?) 数値インデックスを前に含めることによって連想インデックスに対して、値が正しく取得されました。

    $var[0][index] //retrieves correctly
    $var[index]  //does not unless the array happened to be manually constructed

そして、最後の、本当の答えは、他の関連するコード例を何時間も勉強した後に推測されたものです:

->fetch ではなく ->fetchAll を使用しているため、コードはそのまま実行されます。単純に ->fetch を使用すると、数値インデックスと連想インデックスの両方を参照する必要がなくなり、連想配列に期待されるように連想インデックスを参照するだけで済みます。

修正されたコード構文は次のとおりです。

    <?php


    $username = $_POST['username'];
    $password = $_POST['password'];

    // Connect to SQL Server
    include '../../phpconfig/connectstrings.php';

    try
    {
    $conn = new PDO ( "sqlsrv:server = $serverstringname; Database = $databasestringname", "$usernamestringname", "$passwordstringname");
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    catch ( PDOException $e )
    {
    print( "Error connecting to SQL Server." );
    die(print_r($e));
    }

    catch(Exception $e)
    {
    die(var_dump($e));
    }


    //Query database for the hashed password and the salt for the supplied username
    if(!empty($_POST)) {

    try
    {
    $sql_select = "SELECT password, salt FROM logins WHERE username = ?";
    $stmt = $conn->prepare($sql_select);
    $stmt->bindValue(1, $username);
    $stmt->execute();
    }

    catch(Exception $e)
    {
    die(var_dump($e));
    }


    //Fetch the result set into an associative array
    $userdata = $stmt->fetch(PDO::FETCH_ASSOC);

    if(empty($userdata))
    {
    echo "User: $username was not found";
    die;
    }


    //hash the queried salt with a hash of the supplied password
    $hash = hash('sha256', $userdata['salt'].hash('sha256', $password));

    //compare the hashed salted password supplied with that queried from database
    if($hash == $userdata['password'])
    {
    echo "Welcome, $username!";
    }
    else
    {
    echo "Invalid password";
    //does the user wish to register> -> header('Location: register.php');
    die;        
    }
        }

    ?>
于 2012-12-03T20:27:24.387 に答える