{"id":49746106,"url":"https://github.com/jandres25/encriptacion_php","last_synced_at":"2026-06-15T02:00:58.884Z","repository":{"id":238741487,"uuid":"797416741","full_name":"Jandres25/Encriptacion_PHP","owner":"Jandres25","description":"Custom PHP 8.2 MVC system with Composer, lightweight router, bcrypt auth, account lockout, remember-me, session timeout, admin CRUD, file-based caching, and PHPUnit integration tests with GitHub Actions CI.","archived":false,"fork":false,"pushed_at":"2026-06-11T19:01:36.000Z","size":3740,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-06-11T21:03:22.352Z","etag":null,"topics":["authentication","bcrypt","bootstrap","brute-force-protection","composer","csrf","github-actions","integration-testing","middleware","mvc","mysql","mysqli","password-recovery","php","phpmailer","phpunit","remember-me","security","session-management","sweetalert2"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Jandres25.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-05-07T19:38:54.000Z","updated_at":"2026-06-11T19:01:45.000Z","dependencies_parsed_at":null,"dependency_job_id":"385b736d-e6fd-489d-8f26-6202eb8e2933","html_url":"https://github.com/Jandres25/Encriptacion_PHP","commit_stats":null,"previous_names":["jandres25/encriptacion_php"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/Jandres25/Encriptacion_PHP","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jandres25%2FEncriptacion_PHP","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jandres25%2FEncriptacion_PHP/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jandres25%2FEncriptacion_PHP/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jandres25%2FEncriptacion_PHP/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jandres25","download_url":"https://codeload.github.com/Jandres25/Encriptacion_PHP/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jandres25%2FEncriptacion_PHP/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34344440,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","bcrypt","bootstrap","brute-force-protection","composer","csrf","github-actions","integration-testing","middleware","mvc","mysql","mysqli","password-recovery","php","phpmailer","phpunit","remember-me","security","session-management","sweetalert2"],"created_at":"2026-05-10T04:53:26.684Z","updated_at":"2026-06-15T02:00:58.877Z","avatar_url":"https://github.com/Jandres25.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# SecureAuth — PHP MVC Authentication System\n\n[![Version](https://img.shields.io/badge/version-1.9.0-blue.svg?style=flat-square)](https://github.com/Jandres25/Encriptacion_PHP/releases/tag/1.9.0)\n[![Tests](https://github.com/Jandres25/Encriptacion_PHP/actions/workflows/tests.yml/badge.svg)](https://github.com/Jandres25/Encriptacion_PHP/actions/workflows/tests.yml)\n[![PHP Version](https://img.shields.io/badge/PHP-\u003e=8.2-777BB4.svg?style=flat-square\u0026logo=php)](https://php.net/)\n[![PHPMailer](https://img.shields.io/badge/PHPMailer-^6.9-1F3B5F.svg?style=flat-square)](https://github.com/PHPMailer/PHPMailer)\n[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat-square)](LICENSE)\n\nCustom PHP MVC authentication system built with Composer, a lightweight router, and role-based access control.\n\n\u003c/div\u003e\n\n## Features\n\n- Custom MVC architecture — `App\\Core\\Router`, abstract `Controller` and `Model`, PSR-4 autoloading via Composer\n- Secure login with bcrypt password hashing (`password_hash()` / `password_verify()`)\n- **CSRF protection** on all POST forms via `App\\Core\\Csrf` — `hash_equals()` token comparison\n- **Session fixation prevention** — `session_regenerate_id(true)` on every successful login\n- Persistent login via **Remember Me** — `HttpOnly` / `SameSite=Strict` cookie; token stored as SHA-256 hash in DB\n- Automatic **session timeout** on inactivity with remember cookie cleanup\n- Password recovery via email with expiring single-use tokens stored as SHA-256 hash (PHPMailer + STARTTLS)\n- **User profile** — authenticated users can edit their name, email and username, or change their password, at `/profile`; each action has its own CSRF-protected form\n- Admin user management — full CRUD with role-based access control (`AuthMiddleware`)\n- `App\\Config\\Database` singleton — single `\\mysqli` connection per request\n- File-based cache for the users listing with automatic invalidation on writes\n- **Account lockout** — automatic account lock after N failed login attempts; configurable threshold and duration via `.env`\n- **HTTP Security Headers** — `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `Content-Security-Policy` and more via `mod_headers` in `.htaccess`; HSTS ready for HTTPS\n- **Secure session cookie** — `session_start_secure()` helper enforces `HttpOnly`, `SameSite=Strict` and conditional `Secure` flag on every session start\n- **Custom error pages** — styled 404, 403 and 500 views matching the app's design; standalone (no DB dependency)\n- **Integration test suite** — 40 PHPUnit tests against a real MySQL DB; CI via GitHub Actions\n- SweetAlert2 toast notifications for all CRUD and authentication actions\n- Per-page asset injection — `$pageStyles` / `$pageScripts` arrays in shared layouts\n- Shared layout system — `header.php` / `footer.php` accept `$pageTitle`, `$favicon`, `$bodyClass`, `$useDataTables`\n- App version displayed in footer via `APP_VERSION` env var\n\n## Requirements\n\n- PHP \u003e= 8.2\n- MySQL / MariaDB\n- Apache with `mod_rewrite` (XAMPP recommended)\n- Composer\n- Gmail account with an App Password (or any SMTP provider)\n\n## Installation\n\n1. Clone the repository:\n\n```bash\ngit clone https://github.com/Jandres25/Encriptacion_PHP.git\ncd Encriptacion_PHP\n```\n\n2. Install dependencies:\n\n```bash\ncomposer install\n```\n\n3. Copy and configure the environment file:\n\n```bash\ncp .env.example .env\n```\n\nEdit `.env` with your credentials:\n\n```\nDB_HOST=localhost\nDB_USERNAME=root\nDB_PASSWORD=\nDB_DATABASE=login\n\nSMTP_HOST=smtp.gmail.com\nSMTP_USERNAME=your@gmail.com\nSMTP_PASSWORD=your_app_password\nSMTP_PORT=587\n\nAPP_URL=http://localhost/Encriptacion_PHP/public\nAPP_TIMEZONE=America/Bogota\nAPP_VERSION=1.9.0\n\nCACHE_ENABLED=true\nCACHE_TTL_USERS=60\n\nREMEMBER_ME_ENABLED=true\nREMEMBER_ME_TTL=2592000\n\nSESSION_TIMEOUT=1800\n\nLOGIN_LOCKOUT_ENABLED=true\nLOGIN_MAX_ATTEMPTS=5\nLOGIN_LOCKOUT_MINUTES=15\n```\n\n4. Import the database schema:\n\n```bash\nmysql -u root -p \u003c database/schema.sql\n```\n\n5. (Optional) Load sample data:\n\n```bash\nmysql -u root -p \u003c database/seeds.sql\n```\n\n6. Place the project in your server's web root (e.g. `htdocs/` in XAMPP) and open `APP_URL` in your browser.\n\n## Project Structure\n\n```\n├── app/\n│   ├── Config/\n│   │   ├── autoload.php       # Bootstrap: timezone, cache, DB, session, restoreFromCookie()\n│   │   ├── cache.php          # Cache bootstrap + appCache() helper\n│   │   ├── config.php         # Loads .env via phpdotenv; defines APP_URL + env()\n│   │   └── database.php       # Database singleton — Database::getConnection()\n│   ├── Controller/\n│   │   ├── AuthController.php    # login, logout, forgotPassword, resetPassword\n│   │   ├── HomeController.php    # Dashboard — applies timeout + auth middleware\n│   │   ├── ProfileController.php # profile(), changePassword() — any authenticated user\n│   │   └── UserController.php    # Full user CRUD — guarded by admin middleware\n│   ├── Core/\n│   │   ├── Auth.php            # Credential verify, remember-me tokens, password reset tokens\n│   │   ├── Controller.php      # Abstract base — render(), redirect(), verifyCsrf()\n│   │   ├── Csrf.php            # CSRF token generation and verification\n│   │   ├── Model.php           # Abstract base — holds protected \\mysqli $db\n│   │   └── Router.php          # GET/POST route registration and dispatch\n│   ├── Middleware/\n│   │   └── AuthMiddleware.php  # Static guards: auth(), admin(), timeout()\n│   ├── Model/\n│   │   ├── LoginAttempt.php    # Account lockout — atomic insert/update, lock check, clear\n│   │   └── User.php            # All DB queries via MySQLi prepared statements\n│   └── Service/\n│       └── MailerService.php   # PHPMailer encapsulation — SMTP via STARTTLS\n├── database/\n│   ├── schema.sql              # Table definitions (users, password_resets, login_attempts)\n│   ├── schema_test.sql         # Table-only schema for test DB (no CREATE DATABASE)\n│   └── seeds.sql               # Sample data with bcrypt-hashed passwords\n├── libs/\n│   └── Cache/                  # File-based cache implementation\n├── public/\n│   ├── css/                    # bootstrap.css, estilo.css, all.min.css, layout-protected.css\n│   ├── DataTables/             # DataTables JS bundle + Bootstrap 4 skin\n│   ├── img/                    # Images and icons\n│   ├── js/                     # jQuery, Bootstrap JS, Popper, SweetAlert2, users-*.js\n│   ├── webfonts/               # FontAwesome webfonts\n│   ├── .htaccess               # Apache rewrite rules for clean URLs\n│   └── index.php               # Front controller\n├── routes/\n│   └── web.php                 # All route definitions\n├── storage/\n│   ├── .htaccess               # Require all denied — blocks direct web access to cache files\n│   └── cache/                  # Runtime cache files (*.cache)\n├── views/\n│   ├── auth/                   # login, forgot_password, reset_password (standalone, self-hosted assets)\n│   ├── errors/                 # 404.php, 403.php, 500.php + layout.php (standalone, no DB dependency)\n│   ├── home/                   # index.php — dashboard content (wrapped by shared layout)\n│   ├── layouts/                # header.php, footer.php, messages.php\n│   ├── profile/                # index.php — unified profile + change password view\n│   └── user/                   # index, create, edit (wrapped by shared layout)\n├── tests/\n│   ├── bootstrap.php           # Test bootstrap — loads .env.testing, never starts session\n│   ├── TestCase.php            # Abstract base — DB connection, truncate, createUser()\n│   ├── Unit/\n│   │   └── UserTest.php        # 14 integration tests for App\\Model\\User\n│   └── Integration/\n│       └── AuthTest.php        # 19 integration tests for App\\Core\\Auth\n├── .env.example                # Environment variable template\n├── phpunit.xml                 # PHPUnit 11 configuration\n└── composer.json               # Composer dependencies and PSR-4 autoload\n```\n\n## Usage\n\n1. Open `http://localhost/Encriptacion_PHP/public/` in your browser\n2. Log in with a seeded user (e.g. username `Admin`, password `Admin1234`)\n3. Click your username in the nav to access your **profile** — edit info or change password\n4. Admin users (`is_admin = 1`) see the **Users** link in the nav → full CRUD\n5. To recover a password, click \"Forgot your password?\" on the login page\n\n## URL Routing\n\nAll routes are declared in `routes/web.php` and dispatched by `App\\Core\\Router`:\n\n| URL                         | Controller method                  |\n| --------------------------- | ---------------------------------- |\n| `/`                         | `HomeController::index()`          |\n| `/login`                    | `AuthController::login()`          |\n| `POST /logout`              | `AuthController::logout()`         |\n| `/forgot-password`          | `AuthController::forgotPassword()` |\n| `/reset-password?token=...` | `AuthController::resetPassword()`  |\n| `/profile`                  | `ProfileController::profile()`     |\n| `POST /profile/password`    | `ProfileController::changePassword()` |\n| `/users`                    | `UserController::index()`          |\n| `/users/create`             | `UserController::create()`         |\n| `/users/edit?id=X`          | `UserController::edit()`           |\n| `POST /users/delete`        | `UserController::delete()`         |\n\n## Security\n\n- Passwords hashed with bcrypt (`PASSWORD_DEFAULT`)\n- Session set only after successful `password_verify()`; `session_regenerate_id(true)` called immediately after to prevent session fixation\n- **Secure session cookie** — `session_start_secure()` helper enforces `HttpOnly`, `SameSite=Strict`, and `Secure` (on HTTPS) on every session start — including after logout and session timeout\n- **HTTP Security Headers** — `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy`, `Content-Security-Policy`, `Permissions-Policy` set in `public/.htaccess` via `mod_headers`; HSTS commented out, ready for HTTPS\n- **CSRF tokens** on all POST forms — generated via `App\\Core\\Csrf::token()`, validated with `hash_equals()` in every controller; **token rotated** after each successful verification\n- **Logout is POST-only** — protected by CSRF token; prevents logout CSRF via `\u003cimg\u003e` or link\n- Reset tokens: 256-bit (`bin2hex(random_bytes(32))`), 1-hour expiry, single-use, stored as SHA-256 hash in DB\n- **User enumeration prevention** — `forgot-password` always returns the same generic response regardless of whether the email is registered\n- All DB queries via MySQLi prepared statements\n- **Self-hosted assets** — no external CDN in any view; eliminates supply-chain risk and `Referer` header token leakage\n- Email validated with `filter_var()` before DB lookup\n- SMTP with STARTTLS (port 587)\n- Remember-me: raw token in cookie, SHA-256 hash in DB — cookie is `HttpOnly`, `SameSite=Strict`, `Secure` on HTTPS\n- Session timeout enforced on every protected request; clears remember cookie to prevent silent re-login\n- User delete requires POST — not exploitable via `\u003cimg\u003e` or link prefetch\n- **Admin self-protection** — admins cannot delete their own account or remove their own `is_admin` flag\n- **Account lockout** — 5 consecutive failed logins lock the account for 15 min (configurable); only tracked for existing usernames; lockout cleared on successful login or password reset\n- **Custom error pages** — 404, 403, 500 views are standalone (no DB/session dependency); DB errors logged via `error_log()`, never exposed to the browser\n\n## Cache\n\n- Cached endpoint: `/users` listing (`App\\Model\\User::getAll()`)\n- Cache key: `users.all`\n- Invalidation: on create, edit, delete, and password update\n- Controls: `CACHE_ENABLED=true|false`, `CACHE_TTL_USERS=\u003cseconds\u003e`\n- Storage: `storage/cache/*.cache`\n- If the directory is not writable, cache is disabled for the request and a warning is logged (no HTTP 500)\n\n## Testing\n\nThe project includes an integration test suite (PHPUnit 11) that runs against a real MySQL database.\n\n### Local setup\n\n```bash\n# 1. Create the test database\nmysql -u root -p -e \"CREATE DATABASE login_test;\"\nmysql -u root -p login_test \u003c database/schema_test.sql\n\n# 2. Copy and configure the test environment\ncp .env.testing.example .env.testing   # or create it manually from .env.testing section in docs\n# Set DB_HOST, DB_USERNAME, DB_PASSWORD, DB_DATABASE=login_test\n\n# 3. Run all tests\ncomposer test\n\n# Run by suite\ncomposer test:unit         # App\\Model\\User + App\\Model\\LoginAttempt — 21 tests\ncomposer test:integration  # App\\Core\\Auth — 19 tests\n```\n\n### CI\n\nTests run automatically on every push and PR to `master` via GitHub Actions (`.github/workflows/tests.yml`).\n\n## Contributing\n\n1. Fork the project\n2. Create a feature branch (`git checkout -b feature/my-feature`)\n3. Commit your changes following [Conventional Commits](https://www.conventionalcommits.org/)\n4. Push and open a Pull Request\n\n\u003cdiv align=\"center\"\u003e\n\n## License\n\nMIT License — see the `LICENSE` file for details.\n\n---\n\nJandres25 — jandrespb4@gmail.com\n\n[https://github.com/Jandres25/Encriptacion_PHP](https://github.com/Jandres25/Encriptacion_PHP)\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjandres25%2Fencriptacion_php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjandres25%2Fencriptacion_php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjandres25%2Fencriptacion_php/lists"}