Secure Guestbook is a web application, written in PHP. Data persistence is implemented with MySQL.
This demo was written to demonstrate my approach in the situation "You've got nothing except PHP and MySQL. You need to write a secure app. Show what you can."
This could be also a challenge to you: deploy this app, torture it, hack it, prove there are app-level flaws. I'm 95% sure there are not. Show me I'm wrong.
This app is not about reinventing the wheel. Yes, there are many good opensource frameworks and libs.
This is more about the challenge of understanding the inner nature of the frameworks we use every day, and how it affects the security, extendability and maintainability of the apps we create.
The task is to write an demo app in pure PHP with no third-party libs.
The app is a guestbook with the following opts: user signup, login and logout, message create, edit, delete, add reply.
The task is to write an demo app in pure PHP with no third-party libs to demonstrate that the right design is the best way to create secure applications.
Despite the obvious tempation to write a simple app in an oversimplified way, there is an even more obvious flaw of that approach: you loose control over the app and drown in the limitations of that alusive simplicity.
So yes, it worth to write a simple but functional MVC framework, than to write 10 non-extendable PHP scripts, mixed with HTML.
A boilerplate MVC-like environment was written from scratch for this demo. Interface in pure unstyled ultra-simple HTML.
All processing is done around a lifecycle of a Request object which is being mutated during the following sequence:
- Ingestion of a request
- Authentication
- Routing
- Data preparation and injection into request by controller
- HTML rendering in the View subsystem
- Sending the response
As the challenge was primarily about writing an app, as secure as it even possible, the following list of mesaures taken to enforce the app:
- Single point-of-entry MVC-like architecture
- Isolated runtime scope, nothing is global
- Errors are handled by pass-through exceptions
- Indirect request parameters ingestion after sanity check
- Full request parameters sanitization
- Proactive reaction to overflowing attempts in coordiation with local Fail2Ban
- Completely static view templates
- Semi-transparent CSRF protection for POST requests
- Token-based user authentication
- User-tokens are bind to IP address
- Semi-paranoid built-in Fail2Ban implementation, handling both errors and requests tempo
- MySQL parameters are taken from env vars
- XSS and content restrictions headers are being sent
To limit the number of break-in attempts the local simplified Fail2Ban is buil-in.
The main features are:
- Ban for 1 hour by IP, regardless the user.
- Ban the user for 1 hour, regardless the IP.
Ban thresholds for errors and requests quantities are configurable.
Very basic amount of testing provided for backbone subsystems.
Prerequisites: PHP 7 (pdo_mysql, openssl),composer (for phpunit only), http server
- Set environment variables:
WEBAPP_BASEDIR - should point to src/ folder
WEBAPP_STORAGE_MYSQL_HOST
WEBAPP_STORAGE_MYSQL_USER
WEBAPP_STORAGE_MYSQL_PASSWORD
WEBAPP_STORAGE_MYSQL_DB
- Config your web server's document root to point to
src/public
with default document index.php - Access the application in browser
- Run phpunit in the root project directory, ensure all tests passed
phpunit --bootstrap tests/bootstrap.php tests/ApplicationTest.php
- Full protection (covert mode) is disabled in Configuration.php in getDisableProtection(). Set it to false for full security
- See /src/classes/Configuration.php to learn about current config
- Fail2Ban config is relatively paranoid, adjust
getErrorsTillBan
andgetRequestsTillBan
if necessary