-
Notifications
You must be signed in to change notification settings - Fork 0
Seguridad
| Riesgo | Mitigación |
|---|---|
| XSS | Usar e() en todas las vistas |
| Inyección SQL | Todos los métodos de Model usan prepared statements; los nombres de columna/tabla se validan contra un patrón estricto de identificador |
| Exposición de credenciales | Las credenciales solo viven en .env (excluido de git) |
| Exposición de errores |
APP_DEBUG=false en producción |
| Exposición del código fuente | Solo public/ es alcanzable por el servidor web; core/, controllers/, models/, config/ quedan fuera de la raíz de documentos |
| Listado de directorios |
Options -Indexes en public/.htaccess
|
| Archivos sensibles (Nginx) |
deny all para .env, .git, .htaccess
|
| IP de cliente falsificada |
Request::ip() confía en X-Forwarded-For, que cualquier cliente puede establecer — solo confía en ella detrás de un proxy inverso de confianza que sobrescriba esa cabecera |
| Almacenamiento de contraseñas |
password_hash()/password_verify() (bcrypt), nunca texto plano — ver Autenticación
|
| Tokens de recuperación | Guardados como hash SHA-256, de un solo uso (se eliminan tras usarse), expiran en 1 hora |
| Fijación de sesión |
session_regenerate_id(true) en login y logout |
| Enumeración de emails |
/forgot-password muestra el mismo mensaje exista o no el email registrado |
Tanuki no fuerza la verificación CSRF por defecto — el esqueleto base evita cualquier cosa que corra silenciosamente en cada petición sin que tú decidas activarla.
csrf_token() // devuelve el token de la sesión actual (lo crea en la primera llamada)
csrf_field() // renderiza un <input> oculto listo para usar
csrf_verify($tok) // comparación segura frente a timing attacks contra el token de sesión1. Añade el campo a cualquier formulario que modifique datos:
<form method="POST" action="/todo">
<?= csrf_field() ?>
<!-- ... -->
</form>2. Verifícalo al principio de la acción del controlador:
public function store(): void
{
if (!csrf_verify($this->request->post('_token'))) {
$this->flash('error', 'Tu sesión expiró. Inténtalo de nuevo.');
$this->redirect('/todo/create');
}
// ...
}Por qué esto no viene conectado por defecto en el router: forzarlo globalmente en cada ruta POST/PUT/DELETE obligaría a cualquier formulario de cualquier proyecto — incluyendo webhooks y endpoints tipo API que no usan sesiones — a llevar un token, lo necesite o no. Mantenerlo explícito por formulario deja el esqueleto base sin opiniones impuestas. El CRUD de TODO de ejemplo y las extensiones tanuki_login/tanuki_admin ya lo usan en cada formulario que muta datos — copia ese patrón para tus propios recursos.
Todo excepto public/ queda fuera de la raíz de documentos del servidor web. core/, controllers/, models/ y config/ nunca pueden pedirse directamente por URL, sin importar cómo esté configurado el servidor. Mismo patrón que usan Laravel, Symfony y la mayoría de frameworks PHP modernos — ver Estructura del proyecto.