85 lines
2.7 KiB
PHP
85 lines
2.7 KiB
PHP
<?php
|
|
// JWT auth with secret "secret" (top-100 brute list) AND alg:none accepted.
|
|
header("Content-Type: application/json");
|
|
|
|
// CORS wide open
|
|
if (!empty($_SERVER['HTTP_ORIGIN'])) {
|
|
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
|
|
header("Access-Control-Allow-Credentials: true");
|
|
}
|
|
|
|
$SECRET = getenv('JWT_SECRET') ?: 'secret';
|
|
|
|
function b64url_encode($s) { return rtrim(strtr(base64_encode($s), '+/', '-_'), '='); }
|
|
function b64url_decode($s) { return base64_decode(strtr($s, '-_', '+/')); }
|
|
|
|
function sign($header_json, $payload_json, $secret) {
|
|
$h = b64url_encode($header_json);
|
|
$p = b64url_encode($payload_json);
|
|
$sig = hash_hmac('sha256', "$h.$p", $secret, true);
|
|
return "$h.$p." . b64url_encode($sig);
|
|
}
|
|
|
|
function verify($jwt, $secret) {
|
|
$parts = explode('.', $jwt);
|
|
if (count($parts) < 2) return null;
|
|
[$h, $p] = $parts;
|
|
$header = json_decode(b64url_decode($h), true);
|
|
$payload = json_decode(b64url_decode($p), true);
|
|
if (!$header || !$payload) return null;
|
|
|
|
$alg = strtolower($header['alg'] ?? '');
|
|
|
|
// VULN 1 — accept alg:none
|
|
if ($alg === 'none' || $alg === '') return $payload;
|
|
|
|
// VULN 2 — weak HS256 secret
|
|
if ($alg === 'hs256') {
|
|
if (count($parts) !== 3) return null;
|
|
$sig = b64url_decode($parts[2]);
|
|
$want = hash_hmac('sha256', "$h.$p", $secret, true);
|
|
if (hash_equals($want, $sig)) return $payload;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// POST creds -> issue token
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
$body = json_decode(file_get_contents('php://input'), true) ?: [];
|
|
|
|
if ($method === 'POST' && isset($body['action']) && $body['action'] === 'login') {
|
|
$u = $body['username'] ?? '';
|
|
$p = $body['password'] ?? '';
|
|
// plaintext check — admin / admin123
|
|
if ($u === 'admin' && $p === 'admin123') {
|
|
$jwt = sign(
|
|
json_encode(['alg' => 'HS256', 'typ' => 'JWT']),
|
|
json_encode(['sub' => 'admin', 'role' => 'admin', 'iat' => time(), 'exp' => time() + 3600]),
|
|
$SECRET
|
|
);
|
|
echo json_encode(['token' => $jwt, 'expires_in' => 3600]);
|
|
exit;
|
|
}
|
|
http_response_code(401);
|
|
echo json_encode(['error' => 'bad credentials']);
|
|
exit;
|
|
}
|
|
|
|
// GET — show current session from Authorization: Bearer ...
|
|
$hdr = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
|
|
if (preg_match('/Bearer\s+(\S+)/i', $hdr, $m)) {
|
|
$claims = verify($m[1], $SECRET);
|
|
if ($claims) {
|
|
echo json_encode(['authenticated' => true, 'claims' => $claims]);
|
|
exit;
|
|
}
|
|
http_response_code(401);
|
|
echo json_encode(['error' => 'invalid token']);
|
|
exit;
|
|
}
|
|
|
|
echo json_encode([
|
|
'message' => 'POST {action:"login",username,password} to obtain token',
|
|
'example' => ['username' => 'admin', 'password' => 'admin123'],
|
|
]);
|