Middlewares & Security
Middlewares guard routes before main() runs. They execute in the order returned by middlewares(), using an onion-style pipeline — and Lunaris ships the security primitives you need alongside them.
The interface
Every middleware implements one method:
interface IMiddleware {
public function handle($request, $next);
}
A complete middleware
<?php
namespace App\Middlewares;
use System\Routing\IMiddleware;
class LengthGuard implements IMiddleware {
public function handle($request, $next) {
$value = $request->get("length");
if ((int)$value <= 20) {
throw new Exception("Length is less than 21");
}
return $next($request); // pass to the next layer
}
}
$requestis the action instance itself, so you can inspect input, headers,$_FILES, etc.- Call
$next($request)to continue along the pipeline. - To short-circuit a request, throw an exception (e.g.
forbidden(),notfound()), return early, or emit a response directly. - Middleware classes are instantiated with plain
new— there is no DI container, so constructor dependencies can’t be auto-injected.
Applying middlewares to an action
<?php
namespace App\Actions;
use System\Http\Action;
use App\Middlewares\IdsGuard;
use App\Middlewares\LengthGuard;
class AboutAction extends Action {
public function middlewares() {
return [
IdsGuard::class,
LengthGuard::class
];
}
public function main(array $params) {
return response()->json([
"params" => $params,
"all" => $this->all(),
]);
}
}
How the pipeline works
System\Routing\MiddlewarePipeline composes the onion using array_reduce on the reversed middleware list, wrapping the destination closure (which calls main()):
$pipeline = new MiddlewarePipeline($middlewares);
$result = $pipeline->handle($instance, function($request) use ($instance, $params) {
return $instance->main($params);
});
echo $result;
Built-in middleware: CsrfMiddleware
System\Http\Middlewares\CsrfMiddleware protects state-changing requests. On POST / PUT / PATCH / DELETE it validates a CSRF token sent either as $_POST['_token'] or the X-CSRF-TOKEN header, throwing ForbiddenException on failure.
<?php
namespace App\Actions;
use System\Http\Action;
use System\Http\Middlewares\CsrfMiddleware;
class SubmitAction extends Action {
public function middlewares() {
return [CsrfMiddleware::class];
}
public function main() {
return response()->text("Saved!");
}
}
Generate the hidden input for forms with the csrf_field() helper:
<form method="POST" action="<?= route("submit") ?>">
<?= csrf_field() ?>
<input type="text" name="name">
<button>Submit</button>
</form>
Hashing passwords
System\Security\Hash wraps PHP’s password_* functions (bcrypt via PASSWORD_DEFAULT):
use System\Security\Hash;
$hash = Hash::make("secret-pass"); // bcrypt hash
Hash::verify("secret-pass", $hash); // true
Hash::needsRehash($hash); // false (rehash when true)
Encryption
System\Security\Crypt provides authenticated symmetric encryption via defuse/php-encryption (AES-256-GCM), keyed by the APP_KEY in your config:
use System\Security\Crypt;
$ciphertext = Crypt::encrypt("sensitive data");
$plaintext = Crypt::decrypt($ciphertext);
The key is loaded lazily and cached. A missing APP_KEY throws an InvalidArgumentException.
CSRF tokens
Session-backed tokens validated with hash_equals (constant-time comparison):
use System\Routing\Csrf;
Csrf::generate(); // create/return the current token
Csrf::token(); // current token or null
Csrf::validate($t); // true if it matches the session token
Csrf::regenerate(); // rotate the token
Csrf::destroy(); // remove the token
Hardening baked into the framework
| Protection | Where |
|---|---|
| Session cookie hardening | httponly, samesite=Lax, secure on HTTPS — set before session_start() in public/index.php. |
| Server file protection | Root .htaccess denies web access to .env and *.log. |
| Error display | display_errors=0, errors logged to storage/logs. |
| Host validation | The server() helper whitelists hostnames and only trusts a sane X-Forwarded-Proto. |
| Escape by default | Flash::render(..., $escape = true) escapes messages; Str::sanitize() for ad-hoc values. |
| Validation | The Validateable trait — see Validation & Sessions. |
Part of the Lunaris documentation · Source on Codeberg · MIT License