たぶんCSRFの対策なんだろうけど、対策になっていないコードをみつけた。
こういうことはしてはいけませんよ!というメモ。
記述はCakePHPを対象にしています。
[php]
<?php
class BonusController extends AppController {
public function index() {
if ($this->gotBonus()) {
return $this->showError('Bonus is already received.');
}
$user = $this->getUser();
if (!$user) {
return $this->showError('Not found user.');
}
$token = $this->getToken($user);
$this->set('token', $token);
// viewでhttp://hoge.com/bonus/receive/?user_id=xxxx&token=$token なアンカーリンクが表示される
// ユーザはこのリンクを踏むことでボーナスを得ることができる
}
public function receive() {
$user = $this->getUser();
if (!$user) {
return $this->showError('Not found user.');
}
$_token = $this->getToken($user);
$token = isset($_GET['token']) ? $_GET['token'] : null;
if ($_token !== $token) {
return $this->showError('Not found token.');
}
// ボーナスをあげる
$this->redirect('bonus/finish');
}
public function finish() {
}
private function gotBonus() {
// すでにbonusをもらっていればtrue、そうでなければfalseを返す。
}
private function getUser() {
$user_id = isset($_GET['user_id']) ? $_GET['user_id'] : null;
if ($user_id === null) {
return false;
}
$user = $this->User->findById($user_id);
if (!$user) {
return false;
}
return $user;
}
private function getToken($user) {
$session_id = $user['User']['session_id']; // session_idはログアウトして、再度ログインすると変わる値
$salt = 'hgqpvnpqhpwgpwqnpwj';
return Security::hash($session_id, null, $salt); // sha1($session_id + $salt); が行われる
}
} [/php] ※実際にみたコードではuser_idをGETで送ったりはしてません。
$tokenの作り方がまずい。 $tokenの元になっている - $session_idは再ログイン(もしくはセッションが時間で切れる)しない限り永遠に同じ値。 - $saltは固定。 というものになっている。 つまり、$session_idが変わらない限り$tokenも変わらないということになる。
確かにindexで2回以上ボーナスがもらえないようにgotBonus()で確認しているが、receiveでは確認していないという・・・。 つまり、 >http://hoge.com/bonus/receive/?user_id=xxxx&token=$token このURLさえつかんでしまえば、このURLをブラウザでたたきまくれば何回でもボーナスをゲットできてしまうという。 そしてこのURLをつかむのなんて全く簡単な話だということ。
ちゃんとワンタイムトークンなものを使うようにしましょう。