CSRFトークンの追加

This commit is contained in:
2025-12-08 03:18:12 +09:00
parent d810b7155f
commit 4085b77f6a
5 changed files with 61 additions and 44 deletions

View File

@@ -18,10 +18,16 @@ class User {
exit(); exit();
} }
$doLogin = count($_POST) > 0; $doLogin = $_SERVER['REQUEST_METHOD'] === 'POST';
$error = ''; $error = '';
if ($doLogin) { if ($doLogin) {
if (!\verify_csrf_token($_POST['csrf_token'])) {
header('Location: /');
exit();
}
unset($_POST['csrf_token']);
$a = []; $a = [];
if (count($_POST) === 2) { if (count($_POST) === 2) {
$i = 0; $i = 0;
@@ -91,6 +97,12 @@ class User {
$nyuE = ''; $nyuE = '';
if ($doRegister) { if ($doRegister) {
if (!\verify_csrf_token($_POST['csrf_token'])) {
header('Location: /');
exit();
}
unset($_POST['csrf_token']);
$a = []; $a = [];
if (count($_POST) === 4) { if (count($_POST) === 4) {
$i = 0; $i = 0;

View File

@@ -51,17 +51,6 @@ class Auth {
} }
$userData = $this->purgeOldTokens($userData); $userData = $this->purgeOldTokens($userData);
$expire = 0;
$tokenExist = false;
foreach ($userData->tokens as $t) {
if ($t->ip === $ip) {
$tokenExist = true;
break;
}
}
if (!$tokenExist) {
$token = bin2hex(random_bytes(64)); $token = bin2hex(random_bytes(64));
$expire = time() + $this->tokenDuration; $expire = time() + $this->tokenDuration;
$newToken = [ $newToken = [
@@ -74,7 +63,6 @@ class Auth {
]; ];
$userData->tokens[] = $newToken; $userData->tokens[] = $newToken;
}
$path = $this->dataDir.$this->id.'.'.$userData->username.'.json'; $path = $this->dataDir.$this->id.'.'.$userData->username.'.json';
$json = json_encode($userData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $json = json_encode($userData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
@@ -83,21 +71,16 @@ class Auth {
return \Result::error('エラー:ユーザーデータの保存に失敗。'); return \Result::error('エラー:ユーザーデータの保存に失敗。');
} }
if (!$tokenExist) {
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
$domain = $_SERVER['SERVER_NAME'];
if (!setcookie('kerozen', $token, [ if (!setcookie('kerozen', $token, [
'expires' => $expire, 'expires' => $expire,
'path' => '/', 'path' => '/',
'domain' => $domain, 'domain' => $_SERVER['SERVER_NAME'],
'secure' => $secure, 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
'httponly' => true, 'httponly' => true,
'samesite' => 'Strict' 'samesite' => 'Strict'
])) { ])) {
return \Result::error('エラー:クッキーを設定に失敗。'); return \Result::error('エラー:クッキーを設定に失敗。');
} }
}
return \Result::success('ログイン成功'); return \Result::success('ログイン成功');
} }
@@ -111,8 +94,6 @@ class Auth {
public function logout(?string $token = null): \Result { public function logout(?string $token = null): \Result {
if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。'); if (!AUTH_ENABLED) return \Result::Error('エラー:認証システムは無効です。');
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
$domain = $_SERVER['SERVER_NAME'];
$userData = $this->getUserData(); $userData = $this->getUserData();
if (!$token) { if (!$token) {
@@ -124,8 +105,8 @@ class Auth {
setcookie('kerozen', null, [ setcookie('kerozen', null, [
'expires' => 0, 'expires' => 0,
'path' => '/', 'path' => '/',
'domain' => $domain, 'domain' => $_SERVER['SERVER_NAME'],
'secure' => $secure, 'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
'httponly' => true, 'httponly' => true,
'samesite' => 'Strict' 'samesite' => 'Strict'
]); ]);

View File

@@ -160,6 +160,24 @@ if (AUTH_ENABLED) {
return $color.$suffix; return $color.$suffix;
} }
function make_csrf_token(?bool $force = false): string {
if (null !== getcookie('csrf_token') && !$force) return getcookie('csrf_token');
$token = bin2hex(random_bytes(32));
setcookie('csrf_token', $token, [
'expires' => time() + 300, // 5分
'path' => '/',
'domain' => $_SERVER['SERVER_NAME'],
'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
'httponly' => true,
'samesite' => 'Strict'
]);
return $token;
}
function verify_csrf_token(string $token): bool {
return hash_equals(getcookie('csrf_token'), $token);
}
} }
function count_special_chars(string $str): int { function count_special_chars(string $str): int {

View File

@@ -28,7 +28,10 @@
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td><button>サインイン</button></td> <td>
<input type="hidden" id="csrf_token" name="csrf_token" value="{{ make_csrf_token() }}" />
<button>サインイン</button>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -38,7 +38,10 @@
</tr> </tr>
<tr> <tr>
<td></td> <td></td>
<td><button>登録</button></td> <td>
<input type="hidden" id="csrf_token" name="csrf_token" value="{{ make_csrf_token() }}" />
<button>登録</button>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>