Use this skill for input validation, output escaping, passwords, authentication, authorization, sessions, CSRF, secrets, error disclosure, dangerous functions, serialization, and file/path safety.
Treat all external data as untrusted.
Untrusted data includes:
$_GET$_POST$_REQUEST$_COOKIE$_SERVERValidate on input. Use Core\Validator for field validation — it is a fluent chain that collects all errors before returning.
| Method | Description |
|---|---|
required(field, value, message?) |
Fails if value is null or blank |
maxLength(field, value, max, message?) |
Fails if string length exceeds max |
minLength(field, value, min, message?) |
Fails if string length is below min |
numeric(field, value, message?) |
Fails if value is not numeric |
min(field, value, min, message?) |
Fails if numeric value is below min |
max(field, value, max, message?) |
Fails if numeric value exceeds max |
email(field, value, message?) |
Fails if non-empty value is not a valid email |
date(field, value, format?, message?) |
Fails if non-empty value does not match the date format (default Y-m-d) |
in(field, value, allowed[], message?) |
Fails if value is not in the allowed list (strict comparison) |
passes() |
Returns true when no errors were collected |
fails() |
Returns true when any errors were collected |
errors() |
Returns array<string, list<string>> of field errors |
email(), date(), min(), and max() skip empty or non-numeric values respectively — pair them with required() or numeric() when the field is mandatory.
Example:
$validator = new Validator();
$validator
->required('email', $form['email'], 'Email is required.')
->maxLength('email', $form['email'], 255)
->email('email', $form['email'], 'Enter a valid email address.')
->required('start_date', $form['start_date'], 'Start date is required.')
->date('start_date', $form['start_date'], 'Y-m-d', 'Enter a valid start date.');
if ($validator->fails()) {
// $validator->errors() returns field => [messages] map
}
Rules:
Escape on output based on context.
For HTML output:
function e(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
Usage:
<p><?= e($user->name()) ?></p>
Rules:
escapeshellarg() when passing controlled values to shell commands, and avoid shell execution when possible.../, /, \, and null bytes when user-provided paths are not allowed.Never store plain-text passwords.
Use PHP’s password API:
$hash = password_hash($plainPassword, PASSWORD_DEFAULT);
if (! password_verify($plainPassword, $hash)) {
throw new RuntimeException('Invalid credentials.');
}
Rules:
password_hash() for new password hashes.password_verify() for login checks.password_needs_rehash() when algorithm/cost settings change.md5, sha1, or raw sha256 for passwords.Use CSRF protection for state-changing forms and unsafe HTTP methods.
State-changing actions include:
When using _method override to tunnel PUT, PATCH, or DELETE through a POST form, always include a CSRF token. The override is only honoured for POST requests, and only for the values PUT, PATCH, and DELETE — all other values are rejected by the framework.
In MindVisionCode PHP, use the built-in helpers from core/helpers.php:
| Helper | Purpose |
|---|---|
csrf_token() |
Generates and persists the token in the session |
csrf_field() |
Outputs a hidden <input> carrying the token — use in every state-changing form |
verify_csrf_token(string $token) |
Returns bool — call before any business logic in POST actions |
Always verify CSRF before field validation and business logic. A token failure is a security event, not a form validation error. Use a dedicated private method that returns ?Response and short-circuits the action:
private function verifyCsrf(Request $request): ?Response
{
if (!verify_csrf_token((string) $request->input('_token', ''))) {
return new Response('Your session has expired. Please go back and try again.', 419);
}
return null;
}
Call it as the first thing in the action:
public function store(): Response
{
$request = Request::capture();
if ($guard = $this->verifyCsrf($request)) {
return $guard;
}
// field validation and business logic follow
}
Do not call unserialize() on untrusted data.
Prefer JSON for data exchange:
$data = json_decode($json, true, flags: JSON_THROW_ON_ERROR);
$json = json_encode($data, JSON_THROW_ON_ERROR);
Rules:
JSON_THROW_ON_ERROR for new code.Rules:
.env.example.Example .gitignore entries:
.env
.env.local
/config/local.php
/var/cache/
/var/log/
/vendor/
Development:
Production:
Do not leak:
Example:
try {
$service->handle($request);
} catch (Throwable $e) {
$logger->error('Order processing failed.', [
'exception' => $e,
'requestId' => $requestId,
]);
http_response_code(500);
echo 'An unexpected error occurred.';
}
Before completing any feature, verify:
password_hash() and password_verify().eval, exec, shell_exec, system, passthru, unserialize.composer audit.Powered by TurnKey Linux.