Fix registration upload size, mail helper env variables, and verification rate limits

This commit is contained in:
ilham_gmail
2026-06-11 20:08:30 +07:00
parent 3b62f71946
commit 36e3e829ac
7 changed files with 209 additions and 36 deletions
+2 -2
View File
@@ -16,8 +16,8 @@ if(file_exists($dotenvPath . '.env')){
function env($k, $default = null){
$val = $_ENV[$k] ?? null;
if($val === null){ $val = getenv($k); }
return $val !== false ? $val : $default;
if($val === null || $val === ''){ $val = getenv($k); }
return ($val !== false && $val !== '') ? $val : $default;
}
function send_mail_phpmailer($to, $subject, $body, $isHtml = false){
+40 -14
View File
@@ -40,7 +40,38 @@ if(!$verify_code || strlen($verify_code) !== 6){
exit;
}
// require uploaded proof file
if(!isset($_FILES['org_proof']) || $_FILES['org_proof']['error'] !== UPLOAD_ERR_OK){ http_response_code(400); echo json_encode(['success'=>false,'error'=>'missing_org_proof']); exit; }
if(!isset($_FILES['org_proof']) || $_FILES['org_proof']['error'] !== UPLOAD_ERR_OK){
http_response_code(400);
echo json_encode(['success'=>false,'error'=>'missing_org_proof']);
exit;
}
$file = $_FILES['org_proof'];
$maxSize = 5 * 1024 * 1024; // 5MB
if($file['size'] > $maxSize){
http_response_code(400);
echo json_encode(['success'=>false,'error'=>'Ukuran bukti organisasi maksimal 5MB']);
exit;
}
$allowedMimes = ['image/jpeg', 'image/png', 'image/svg+xml', 'application/pdf'];
$allowedExts = ['jpg', 'jpeg', 'png', 'svg', 'pdf'];
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$mime = null;
if(function_exists('finfo_open')){
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
} else {
$mime = $file['type'];
}
if(!in_array($mime, $allowedMimes) || !in_array($ext, $allowedExts)){
http_response_code(400);
echo json_encode(['success'=>false,'error'=>'Jenis file tidak diperbolehkan. Hanya .png, .jpg, .jpeg, .svg, atau .pdf.']);
exit;
}
// Verify code matches what was sent (simple check: hash it and compare)
// In production, store code server-side with TTL
@@ -85,23 +116,18 @@ $newId = $conn->insert_id;
$ins->close();
if(!$res){ http_response_code(500); echo json_encode(['success'=>false,'error'=>$conn->error]); exit; }
// handle file upload (org_proof) - validate extension + mime + size
// handle file upload (org_proof) - already validated above
$orgProofPath = null;
if(isset($_FILES['org_proof']) && $_FILES['org_proof']['error'] === UPLOAD_ERR_OK){
$file = $_FILES['org_proof'];
$maxSize = 5 * 1024 * 1024; // 5MB
$allowed = ['image/jpeg'=>['jpg','jpeg'],'image/png'=>['png'],'image/svg+xml'=>['svg'],'application/pdf'=>['pdf']];
$uDir = __DIR__ . '/../../uploads/org_proofs';
if(!is_dir($uDir)) { @mkdir($uDir, 0755, true); @chmod($uDir, 0755); }
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$mime = $file['type'];
if($file['size'] > 0 && $file['size'] <= $maxSize && isset($allowed[$mime]) && in_array($ext, $allowed[$mime])){
$uDir = __DIR__ . '/../../uploads/org_proofs';
if(!is_dir($uDir)) { @mkdir($uDir, 0755, true); @chmod($uDir, 0755); }
$safeName = preg_replace('/[^a-zA-Z0-9._-]/','_', basename($file['name']));
$dst = $uDir . '/' . time() . '_' . bin2hex(random_bytes(6)) . '_' . $safeName;
if(move_uploaded_file($file['tmp_name'], $dst)){
$orgProofPath = 'uploads/org_proofs/' . basename($dst);
try{ $up = $conn->prepare('UPDATE users SET org_proof_path = ? WHERE id = ?'); if($up){ $up->bind_param('si', $orgProofPath, $newId); $up->execute(); $up->close(); } }catch(Exception $e){}
}
$safeName = preg_replace('/[^a-zA-Z0-9._-]/','_', basename($file['name']));
$dst = $uDir . '/' . time() . '_' . bin2hex(random_bytes(6)) . '_' . $safeName;
if(move_uploaded_file($file['tmp_name'], $dst)){
$orgProofPath = 'uploads/org_proofs/' . basename($dst);
try{ $up = $conn->prepare('UPDATE users SET org_proof_path = ? WHERE id = ?'); if($up){ $up->bind_param('si', $orgProofPath, $newId); $up->execute(); $up->close(); } }catch(Exception $e){}
}
}
@@ -22,24 +22,91 @@ if(!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)){
exit;
}
// Rate limiting table creation and validation
try {
$conn->query("CREATE TABLE IF NOT EXISTS verification_attempts (
email VARCHAR(255) PRIMARY KEY,
attempts INT NOT NULL DEFAULT 0,
last_attempt_at DATETIME NOT NULL,
locked_until DATETIME NULL
)");
} catch (Exception $e) {}
$nowStr = date('Y-m-d H:i:s');
$nowTime = time();
$stmt = $conn->prepare("SELECT attempts, last_attempt_at, locked_until FROM verification_attempts WHERE email = ?");
$stmt->bind_param('s', $email);
$stmt->execute();
$res = $stmt->get_result();
$row = $res ? $res->fetch_assoc() : null;
$stmt->close();
if($row){
$attempts = intval($row['attempts']);
$lastAttempt = strtotime($row['last_attempt_at']);
$lockedUntil = $row['locked_until'] ? strtotime($row['locked_until']) : null;
if($lockedUntil && $nowTime < $lockedUntil){
$diff = $lockedUntil - $nowTime;
$hours = floor($diff / 3600);
$mins = ceil(($diff % 3600) / 60);
http_response_code(429);
echo json_encode([
'success' => false,
'error' => 'locked_24h',
'message' => "Batas pengiriman kode tercapai. Anda harus menunggu {$hours} jam {$mins} menit.",
'locked_until' => $lockedUntil * 1000
]);
exit;
}
if($nowTime - $lastAttempt < 30){
$remaining = 30 - ($nowTime - $lastAttempt);
http_response_code(429);
echo json_encode([
'success' => false,
'error' => 'cooldown_active',
'message' => "Silakan tunggu {$remaining} detik sebelum mengirim kembali.",
'remaining' => $remaining
]);
exit;
}
}
// Generate 6-digit code
$verificationCode = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
$codeHash = hash('sha256', $verificationCode);
// Store in session-like temp file (can use Redis/cache in production)
// For now, we'll return it and store in frontend state
// But also can check email format is valid
$mailSuccess = false;
try{
$subject = 'Kode Verifikasi Email - WebGIS';
$message = "Kode verifikasi Anda adalah:\n\n" . $verificationCode . "\n\nKode ini berlaku selama 15 menit. Jangan bagikan kode ini kepada siapapun.\n\nJika Anda tidak melakukan pendaftaran ini, abaikan email ini.";
send_mail_notify($email, $subject, $message, false);
$mailSuccess = send_mail_notify($email, $subject, $message, false);
}catch(Exception $e){
// Email send failed but still return code for testing
error_log('Mailing exception: ' . $e->getMessage());
}
// Update/Insert attempts on database
if($row){
$newAttempts = ($row['locked_until'] && $nowTime >= strtotime($row['locked_until'])) ? 1 : ($row['attempts'] + 1);
$newLockedUntil = ($newAttempts >= 3) ? date('Y-m-d H:i:s', $nowTime + 24 * 3600) : null;
$up = $conn->prepare("UPDATE verification_attempts SET attempts = ?, last_attempt_at = ?, locked_until = ? WHERE email = ?");
$up->bind_param('isss', $newAttempts, $nowStr, $newLockedUntil, $email);
$up->execute();
$up->close();
} else {
$one = 1;
$nullVal = null;
$ins = $conn->prepare("INSERT INTO verification_attempts (email, attempts, last_attempt_at, locked_until) VALUES (?, ?, ?, ?)");
$ins->bind_param('siss', $email, $one, $nowStr, $nullVal);
$ins->execute();
$ins->close();
}
$response = ['success'=>true,'email'=>$email,'message'=>'code_sent'];
if(getenv('DEBUG_MODE') === '1' || $_SERVER['REMOTE_ADDR'] === '127.0.0.1'){
if(getenv('DEBUG_MODE') === '1' || $_SERVER['REMOTE_ADDR'] === '127.0.0.1' || !$mailSuccess){
$response['debug_code'] = $verificationCode;
}
echo json_encode($response);