10960
PHPでユーザログインのAPIを作成しています。以下のコードを作成し、クライントサイドからメールアドレスとパスワードをPOSTしたのですが、401エラーで正しく認証できませんでした。  
ユーザデータはデータベースにあるのでメールアドレスとパスワードの打ち間違いはありません。どこを間違えているかわかりますか。またデバッグ方法などありましたら教えて下さい。よろしくお願いします。

**ソーズコード**

```

      1 <?php
      2 header("Content-Type: application/json; charset=UTF-8");
      3 header("X-Content-Type-Option: nosniff");
      4 header("Access-Control-Allow-Origin: *");
      5 header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
      6 require_once 'functions.php'
      7 $pdo = initDB()
      8 switch($_SERVER['REQUEST_METHOD']){
      9 case 'POST':
     10   $in = json_decode(file_get_contents('php://input'), true);
     11   if(isset($in['mail_address'])&&isset($in['password'])){
     12     $st = $pdo->prepare("SELECT * FROM user WHERE (mail_address) VALUE (:mail_address)");
     13     $st->bindParam(':mail_address', $in['mail_address'], PDO::PARAM_STR);
     14     $st->execute();
     15     while($row = $st->fetch(PDO::FETCH_ASSOC)){
     16       $password = $row['password'];
     17     }
     18     if($password==$in['password']){
     19       onPostSuccess($in['mail_address']);
     20     }else{
     21       header("HTTP/1.0 401 Unauthorixed");
     22     }
     23   }else{
     24     header("HTTP/1.0 400 Bad Request");
     25   }
     26 }
     27
     28 function onPostSuccess($mailAddress){
     29   $accessToken = makeAccessToken();
     30   $st = $pdo->prepare("UPDATE user SET accessToken=:accessToken WHERE mail_address=:mail_address");
     31   $st->bindParam(':accessToken', $accessToken, PDO::PARAM_STR);
     32   $st->bindParam(':mail_address', $mailAddress, PDO::PARAM_STR);
     33   $st->execute();
     34   header("HTTP/1.0 200 OK");
     35   echo json_encode(array('accessToken' => $accessToken));
     36 }
    
```
CC BY-SA 3.0
2015-06-01T12:37:56.717
10846
2015-06-04T01:12:30.317
7232
question
php api
PHPでログイン用APIの作成
パスワードが生で保存されているっぽいのは、おいといて。

個人的には、このコードから見て考えつくのは  
同一メールアドレスのパスワード違いが複数行あって、  
`while($row = $st->fetch(PDO::FETCH_ASSOC)){}`  
のところでorderもかかっていない最終行のパスワードの何かが  
入力されたものと一致していない可能性がある。というところが疑わしいです。

それから「401 Unauthorixed」(Unauthorixed...?)が発生するのは、  
if($password==$in['password'])のでの判定だけですよね。  
この直前で両方の変数を、出力でもして確認するのが最適だと思います。

……ところで気になったのですが。  
DBへの問い合わせクエリの中で、メールアドレスとパスワードの両方を渡せばいいのでは？  
なぜwhileで回しているのでしょうか。
CC BY-SA 3.0
2015-06-02T04:43:20.010
10874
2015-06-02T04:43:20.010
8335
10846
answer
16行目でwhile開始して、そのブロックが18行目で閉じているのが原因ではないでしょうか。
CC BY-SA 3.0
2015-06-02T22:12:58.797
10914
2015-06-02T22:12:58.797
8136
10846
answer
認証機構を正しく安全に実装するのは意外と難しいので、十分な知識が無いなら自前で実装するのは避けた方が良いです。

認証情報を自前で持つ場合、認証情報の安全な保管、パスワードリカバリ、アカウントに対するブルートフォース攻撃対策等まで含めて自分で考える必要があります。それよりもGoogleなどの外部IDプロバイダを利用して認証情報を自分では持たないのがベストです。

自前でで実装する場合でも既製のライブラリやフレームワーク等がないか検討しましょう。

また、HTTP認証で十分なところに複雑な認証機構を使うのはかえってリスクが高くなります。HTTP認証で十分ならそれを使えば良いです。

質問については、

```

    $st = $pdo->prepare("SELECT count(*) FROM user WHERE mail_address = :mail_address AND password = :password");
    $st->bindParam(':mail_address', $in['mail_address'], PDO::PARAM_STR);
    $st->bindParam(':passowrd',     $in['password'],     PDO::PARAM_STR);
    $st->execute();
    if($st->fetchColumn() > 1){ 
      //match
    }else{
      //don't match
    }
    
```

やりたいことはおそらくこれで十分だと思います。
CC BY-SA 3.0
2015-06-03T02:35:59.130
10924
2015-06-03T02:35:59.130
5793
10846
answer
最終的に以下のようになりました。回答をしてくださった方々に感謝します。パスワードのセキュリティの問題など勉強になりました。パスワードをハッシュ化して保存したり、サードパーティの認証ライブラリの使用も検討したいと思います。

```

    <?php
    header("Content-Type: application/json; charset=UTF-8");
    header("X-Content-Type-Option: nosniff");
    header("Access-Control-Allow-Origin: *");
    header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
    require_once 'functions.php';
    $pdo = initDB();
    switch($_SERVER['REQUEST_METHOD']){
    case 'POST':
      $in = json_decode(file_get_contents('php://input'), true);
      $error = '';
      if(isset($in['mail_address'])&&isset($in['password'])){
        $st = $pdo->prepare("SELECT * FROM user WHERE mail_address=:mail_address");
        $st->bindParam(':mail_address', $in['mail_address'], PDO::PARAM_STR);
        $st->execute();
        $row = $st->fetch(PDO::FETCH_ASSOC);
        $password = $row['password'];
        if($password!=null){
          if($password==$in['password']){
            onPostSuccess($pdo, $in['mail_address']);
          }else{
            header("HTTP/1.0 401 Unauthorixed");
            $error = "パスワードが違います。";
            sendError($error);
          } 
        }else{
          header("HTTP/1.0 401 Unauthorixed");
          $error = "メールアドレスが登録されていません。";
          sendError($error);
        }
      }else{
        header("HTTP/1.0 400 Bad Request");
      }
      break;
    }
    
    function onPostSuccess($pdo, $mailAddress){
      $accessToken = makeAccessToken();
      $st1 = $pdo->prepare("UPDATE user SET accessToken=:accessToken WHERE mail_address=:mail_address");
      $st1->bindParam(':accessToken', $accessToken, PDO::PARAM_STR);
      $st1->bindParam(':mail_address', $mailAddress, PDO::PARAM_STR);
      $st1->execute();
      header("HTTP/1.0 200 OK");
      echo json_encode(array('accessToken' => $accessToken));
    }
    
```
CC BY-SA 3.0
2015-06-04T01:12:30.317
10960
2015-06-04T01:12:30.317
7232
10846
answer