Files
zuevav e5a88665cd mailn
2026-04-30 15:14:09 +03:00

259 lines
6.9 KiB
PHP

<?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';
}
}