Authentication
Cédula-based login with bcrypt password hashing
Session Management
PHP sessions with 10-minute inactivity timeout
Role-Based Access
Administrador vs. Usuario permission model
Audit Trail
All login events and data changes logged with IP
Authentication
Source:Loggin.php
Login credential format
Users log in with their Venezuelan national ID (cédula) and a password. The cédula field accepts only 7–8 numeric digits, validated on both the client and server:Password hashing
Passwords are stored as bcrypt hashes using PHP’sPASSWORD_DEFAULT constant, which maps to bcrypt at the time of writing:
password_verify(), which handles the bcrypt comparison and is resistant to timing attacks:
Account status check
Before password verification, the system checksusuarios.activo. If the account is disabled (activo = 0), login is rejected and the failed attempt is recorded in auditoria:
Session Management
Source:home.php, header.php, conexion.php
Session initialization
Every protected page starts the PHP session withsession_start(). The session is initialized in Loggin.php on successful login, and the $_SESSION['usuario'] array carries the user state:
Session validation guard
header.php is included at the top of every protected page. It performs two checks:
- Logged-in check — redirects to
Loggin.phpif the session is missing orloggeado !== true - Inactivity timeout — destroys the session and redirects with
?inactividad=1if the user has been idle for more than 10 minutes
conexion.php also refreshes the timestamp as part of its inclusion:
Inactivity timeout
| Parameter | Value | Source |
|---|---|---|
$inactividad_maxima | 600 seconds (10 minutes) | home.php:14, header.php:15 |
Timeout redirect (home.php) | Loggin.php?inactividad=1 | home.php:18 |
Timeout redirect (header.php) | Loggin.php?error=Sesión+expirada+por+inactividad | header.php:19 |
| Timestamp key | $_SESSION['usuario']['ultimo_acceso'] | Updated on every page request |
Loggin.php with ?inactividad=1 set, the following message is displayed:
The timeout is checked server-side on every page load, not with a client-side JavaScript timer. The commented-out JS timer in
header.php is not active in the current build.Session destruction
Users can explicitly end their session by navigating tosalir.php. On timeout, session_destroy() is called before the redirect to ensure all session data is cleared.
Role-Based Access Control
Source:home.php, header.php
Roles
The system has exactly two roles, stored inusuarios.rol as an enum:
| Role | Access |
|---|---|
Administrador | Full access including configuracion.php, gestion_usuarios.php, and all write operations |
Usuario | Read and limited write access; cannot access admin-only pages |
Role check pattern
Admin-only pages check the role from the session and redirect unauthorized users toindex.php:
Menu visibility
The sidebar inheader.php and home.php conditionally renders the Configuración menu item:
Hiding the menu item is a UX convenience only. The actual access restriction must always be enforced server-side at the top of each admin page.
SQL Injection Protection
Source:Loggin.php, home.php
All database queries that incorporate user-supplied input use MySQLi prepared statements with bind_param(). Raw string interpolation into SQL is never used for user data.
Login query example
Audit log write example
Date-parameterized query example
Input Validation
Output escaping
All user-originated data rendered to HTML is escaped withhtmlspecialchars() to prevent XSS:
Email validation
When storing or updating email addresses, the system uses PHP’s built-inFILTER_VALIDATE_EMAIL:
Form completeness check
Before any processing, empty-field guards prevent partial submissions:Audit Trail
Source:Loggin.php, auditoria table
Every significant event writes a row to the auditoria table. The table is append-only by convention.
Events recorded at login
| Event | datos_nuevos payload | usuario_cedula |
|---|---|---|
| Successful login | {"accion": "inicio_sesion_exitoso"} | Authenticated cédula |
| Wrong password | {"intento": "sesion_fallida", "motivo": "credenciales_incorrectas"} | Submitted cédula |
| User not found | {"intento": "sesion_fallida", "motivo": "usuario_no_encontrado"} | Submitted cédula |
| Account inactive | {"intento": "sesion_fallida", "motivo": "usuario_inactivo"} | Submitted cédula |
Audit record structure
| Field | Login value | Notes |
|---|---|---|
tabla_afectada | 'usuarios' | Hardcoded for login events |
accion | 'INSERT' | Login events always use INSERT |
usuario_cedula | Submitted cédula | May belong to a non-existent user |
datos_anteriores | NULL | Not used for login events |
datos_nuevos | JSON string | Contains event type and failure reason |
ip_address | $_SERVER['REMOTE_ADDR'] | Supports IPv6 (field is varchar(45)) |
Querying recent login history
home.php retrieves the previous login timestamp for the welcome screen using: