mailn
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
/**
|
||||
* Simple but secure authentication system
|
||||
* Compatible with PHP 5.6+ (maximum compatibility)
|
||||
*/
|
||||
|
||||
class Auth
|
||||
{
|
||||
private $configFile;
|
||||
private $config;
|
||||
private $maxAttempts = 5;
|
||||
private $lockoutTime = 900;
|
||||
private $passwordAlgo;
|
||||
|
||||
public function __construct($configFile = null)
|
||||
{
|
||||
if ($configFile === null) {
|
||||
$this->configFile = __DIR__ . '/../auth_config.php';
|
||||
} else {
|
||||
$this->configFile = $configFile;
|
||||
}
|
||||
|
||||
// Use Argon2ID if available, fallback to bcrypt
|
||||
if (defined('PASSWORD_ARGON2ID')) {
|
||||
$this->passwordAlgo = PASSWORD_ARGON2ID;
|
||||
} else {
|
||||
$this->passwordAlgo = PASSWORD_BCRYPT;
|
||||
}
|
||||
|
||||
$this->loadConfig();
|
||||
}
|
||||
|
||||
private function loadConfig()
|
||||
{
|
||||
if (file_exists($this->configFile)) {
|
||||
$this->config = require $this->configFile;
|
||||
} else {
|
||||
$this->config = array(
|
||||
'users' => array(),
|
||||
'failed_attempts' => array(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function saveConfig()
|
||||
{
|
||||
$content = "<?php\nreturn " . var_export($this->config, true) . ";\n";
|
||||
file_put_contents($this->configFile, $content);
|
||||
@chmod($this->configFile, 0600);
|
||||
}
|
||||
|
||||
public function createUser($username, $password)
|
||||
{
|
||||
if (isset($this->config['users'][$username])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->config['users'][$username] = array(
|
||||
'password_hash' => $this->hashPassword($password),
|
||||
'created_at' => time(),
|
||||
);
|
||||
|
||||
$this->saveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function changePassword($username, $oldPassword, $newPassword)
|
||||
{
|
||||
if (!$this->verifyPassword($username, $oldPassword)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->config['users'][$username]['password_hash'] = $this->hashPassword($newPassword);
|
||||
$this->saveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function hashPassword($password)
|
||||
{
|
||||
if ($this->passwordAlgo === PASSWORD_ARGON2ID) {
|
||||
return password_hash($password, PASSWORD_ARGON2ID, array(
|
||||
'memory_cost' => 65536,
|
||||
'time_cost' => 4,
|
||||
'threads' => 1,
|
||||
));
|
||||
}
|
||||
return password_hash($password, PASSWORD_BCRYPT, array('cost' => 12));
|
||||
}
|
||||
|
||||
private function verifyPassword($username, $password)
|
||||
{
|
||||
if (!isset($this->config['users'][$username])) {
|
||||
$this->hashPassword($password);
|
||||
return false;
|
||||
}
|
||||
return password_verify($password, $this->config['users'][$username]['password_hash']);
|
||||
}
|
||||
|
||||
private function isLockedOut($ip)
|
||||
{
|
||||
if (!isset($this->config['failed_attempts'][$ip])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$attempts = $this->config['failed_attempts'][$ip];
|
||||
$lockoutTime = $this->lockoutTime;
|
||||
|
||||
$filtered = array();
|
||||
foreach ($attempts as $time) {
|
||||
if ($time > time() - $lockoutTime) {
|
||||
$filtered[] = $time;
|
||||
}
|
||||
}
|
||||
$this->config['failed_attempts'][$ip] = $filtered;
|
||||
|
||||
return count($filtered) >= $this->maxAttempts;
|
||||
}
|
||||
|
||||
private function recordFailedAttempt($ip)
|
||||
{
|
||||
if (!isset($this->config['failed_attempts'][$ip])) {
|
||||
$this->config['failed_attempts'][$ip] = array();
|
||||
}
|
||||
$this->config['failed_attempts'][$ip][] = time();
|
||||
$this->saveConfig();
|
||||
}
|
||||
|
||||
private function clearFailedAttempts($ip)
|
||||
{
|
||||
unset($this->config['failed_attempts'][$ip]);
|
||||
$this->saveConfig();
|
||||
}
|
||||
|
||||
public function login($username, $password, $ip)
|
||||
{
|
||||
if ($this->isLockedOut($ip)) {
|
||||
return array(
|
||||
'success' => false,
|
||||
'message' => 'Too many failed attempts. Please try again later.',
|
||||
'locked' => true,
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->verifyPassword($username, $password)) {
|
||||
$this->clearFailedAttempts($ip);
|
||||
$token = $this->generateSessionToken();
|
||||
|
||||
return array(
|
||||
'success' => true,
|
||||
'message' => 'Login successful',
|
||||
'token' => $token,
|
||||
'username' => $username,
|
||||
);
|
||||
}
|
||||
|
||||
$this->recordFailedAttempt($ip);
|
||||
|
||||
$attempts = isset($this->config['failed_attempts'][$ip]) ? $this->config['failed_attempts'][$ip] : array();
|
||||
$remaining = $this->maxAttempts - count($attempts);
|
||||
|
||||
return array(
|
||||
'success' => false,
|
||||
'message' => "Invalid username or password. {$remaining} attempts remaining.",
|
||||
'locked' => false,
|
||||
);
|
||||
}
|
||||
|
||||
private function generateSessionToken()
|
||||
{
|
||||
if (function_exists('random_bytes')) {
|
||||
return bin2hex(random_bytes(32));
|
||||
}
|
||||
return bin2hex(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
|
||||
public function startSession($username, $token)
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
session_regenerate_id(true);
|
||||
|
||||
$_SESSION['authenticated'] = true;
|
||||
$_SESSION['username'] = $username;
|
||||
$_SESSION['token'] = $token;
|
||||
$_SESSION['login_time'] = time();
|
||||
$_SESSION['last_activity'] = time();
|
||||
}
|
||||
|
||||
public function isAuthenticated($timeout = 3600)
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (empty($_SESSION['authenticated'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$lastActivity = isset($_SESSION['last_activity']) ? $_SESSION['last_activity'] : 0;
|
||||
if (time() - $lastActivity > $timeout) {
|
||||
$this->logout();
|
||||
return false;
|
||||
}
|
||||
|
||||
$_SESSION['last_activity'] = time();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function logout()
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
$_SESSION = array();
|
||||
|
||||
if (isset($_COOKIE[session_name()])) {
|
||||
setcookie(session_name(), '', time() - 3600, '/');
|
||||
}
|
||||
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
public function getCurrentUser()
|
||||
{
|
||||
if (!$this->isAuthenticated()) {
|
||||
return null;
|
||||
}
|
||||
return isset($_SESSION['username']) ? $_SESSION['username'] : null;
|
||||
}
|
||||
|
||||
public function hasUsers()
|
||||
{
|
||||
return !empty($this->config['users']);
|
||||
}
|
||||
|
||||
public static function getClientIP()
|
||||
{
|
||||
$headers = array('HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR');
|
||||
|
||||
foreach ($headers as $header) {
|
||||
if (!empty($_SERVER[$header])) {
|
||||
$ip = $_SERVER[$header];
|
||||
if (strpos($ip, ',') !== false) {
|
||||
$parts = explode(',', $ip);
|
||||
$ip = trim($parts[0]);
|
||||
}
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP)) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '0.0.0.0';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user