crear enlaces seguros para las paginas php

Iniciado por gAb1, 24 Septiembre 2014, 13:16 PM

0 Miembros y 1 Visitante están viendo este tema.

gAb1

Hola, me gustaría saber cual es la manera mas segura de crear enlaces seguros para las paginas de mi sitio web.

Acaba de leer un pequeño tutorial donde aparece un codigo con el que se supone que no será facil hackear la web, pero es del año 2006 y tengo dudas por si ya no es tan seguro como lo era antes... Aqui esta el link: Safe Dynamic Includes

Tambien he visto otra manera: Safe Dynamic Includes, pero tampoco se si hay otras maneras mas seguras para crear los links de una pagina a otra.

Lo normal es las paginas suele ser

Código (html5) [Seleccionar]
<a href="index.php?x=pagename">Link</a>

Pero al parecer cambiando la variable de sesion por otra, el link puede ser de otra manera:

Código (php) [Seleccionar]
$_GET['x']; -> $_SERVER['QUERY_STRING'];

Código (html5) [Seleccionar]
<a href="index.php?pagename">Link</a>

¿Podriais confirmar que maneras son más seguras?

Gracias!

engel lex

yo uso "tablas de rutas"

es decir, una db que contenga 2 campos, "link" y "ruta", entonces al llamar, busco en la db si algún valor coincide con el recibido, si coincide, se hace un include con la ruta devuelta, eso te sirve incluso al grado que la pagina "producto" puede ser "pagina_de_fondo.php"
El problema con la sociedad actualmente radica en que todos creen que tienen el derecho de tener una opinión, y que esa opinión sea validada por todos, cuando lo correcto es que todos tengan derecho a una opinión, siempre y cuando esa opinión pueda ser ignorada, cuestionada, e incluso ser sujeta a burla, particularmente cuando no tiene sentido alguno.

MinusFour

¿Enlaces seguros? Las rutas son tan seguras como el código que ejecuta esa ruta en especifico. Es importante 'limpiar' todo input humano o te arriegas a un XSS, RFI o algo peor.

WHK

#3
Es facil... solo debes hacer un array con todas las posibles rutas y luego hacer la comparación con tu parámetro get, si existe la incluyes y si no existe das un 404, de esa manera te aseguras que solamente las rutas reales van a ser solicitadas y nada de cosas extrañas con rutas extrañas.

Por ejemplo:

Código (php) [Seleccionar]
<?php

if(in_array('includes/'.(string)$_GET['sección'].'.php'glob('includes/*.php')))
include('includes/'.(string)$_GET['sección'].'.php');
else
include('includes/404.php');


El cast (string) es para que no te de una excepción si te pasan arrays como parámetros.

También puedes aceptar directorios como lo hace wordpress cuando puedes poner un plugin como plugin.php o una carpeta llamada plugin y un index:

Código (php) [Seleccionar]
<?php

/* Es archivo */
if(in_array('includes/'.$_GET['sección'].'.php'glob('includes/*.php')))
include('includes/'.$_GET['sección'].'.php');

/* Es directorio */
elseif(in_array('includes/'.$_GET['sección'].'/index.php'glob('includes/*/index.php')))
include('includes/'.$_GET['sección'].'/index.php');

/* No existe */
else
include('includes/404.php');


Ahora, si usas file_exists() o is_dir() tendrás que tener cuidado con los agujeros de tipo LFI o RFI, usar esas fuinciones para incluir archivos es muy mala práctica.

Según el blog de donde das en enlace ( http://www.jemjabella.co.uk/2006/safe-dynamic-includes/ ) dice que se debe hacer así:

Código (php) [Seleccionar]
if (strpos($_GET['x'], "/")) {
     $dir = substr(str_replace('..', '', $_GET['x']), 0, strpos($_GET['x'], "/")) . "/";


Es una malisima práctica, muchos sistemas han sido programados de esa manera y estan llenos de agujeros de seguridad como unos mods y plugins para phpnuke, joomla y wordpress, por ejemplo esto sería vulnerable en base a ese código:

index.php?x=.htpasswd

a demás strpos() solo te indica la primera coincidencia, o sea que yo podría escribir algo//algo y bypasear el filtro, insertar carácteres nulos para cortar el include con un %00, etc etc.

gAb1

#4
Gracias por las respuestas.

Eso de la tabla de rutas que comenta engel lex es interesante, es algo parecido a lo que comenta WHK, las "whitelist".

Pero en el ejemplo que has puesto, ¿asi tal cual sería seguro? sin hacer un preg_replace o str_replace? Y lo que dice MinusFour, lo de limpiar los input, tiene algo que ver tambien con sanitizar?

Me gustaría algo para no tener que mostrar la ruta, simplemente un nombre, poniendo la ruta en el script.

Por ejemplo si quiero llamar al archivo: register.php dentro de 'views/user/'

Y el link quedase: index.php?register ¿O esto no es seguro?

Pero no se si seria un problema para mostrar el contenido, lo que quiero hacer tambien y no se si hay otra manera mejor, es tener un layout con header (header+menu) y footer, y en medio haciendo un echo content(); para cargar el contenido de los views y no tener que cambiar nada del layout en todas las paginas de contenido. El index.php cargaría el layout y el layout cargaria los views.

¿Hay algún problema para hacer eso? Supongo que hara falta crear una funcion para cargar el contenido.

Gracias!

WHK

#5
Cita de: gAb1 en 24 Septiembre 2014, 21:15 PMPero en el ejemplo que has puesto, ¿asi tal cual sería seguro? sin hacer un preg_replace o str_replace? Y lo que dice MinusFour, lo de limpiar los input, tiene algo que ver tambien con sanitizar?

Así es, es un método muy seguro, no te tienes que estar preocupando de tener que modificar tu código o base de datos cada ves que agregas secciones nuevas porque se verifican automáticamente en cada carga.

no necesitas un replace ni un limpiado de rutas ni nada de eso, es como si estuvieras intentando tapar el sol con un dedo, son técnicas que no siempre funcionan y son por decirlo muy absurdas, generalmente se ven en códigos hechos por menores de edad o personas que recién están aprendiendo a programar. Es similar a lo que antiguamente muchos hacían... ponerle safe mode en on para supuestamente proteger los servidores, muchos lo hacían pero al final no servía de mucho.

Lo de limpiar inputs es una pesima práctica, es como tratar de enumerar todas las posibles maneras que existen de como robar una casa, siempre habrá alguien que encuentre una manera nueva de entrar, en ves de eso haz una casa normal con seguros en las puertas y listo.

Por lo general debido a personas que piensan que limpiando inputs van a solucionar los problemas de seguridad es porque hoy en día hay tantos sitios expuestos a vulnerabilidades. Puede servir para intentar prevenir algunos tipos de ataques basicos pero jamas para detener todo lo que existe, en este caso limpiar inputs para prevenir el lfi o el rfi es un tema demasiado extenso y complejo si no quieres usar listas blancas, ya existen muchos sistemas que llevan años intentando solucionar este problema en base a filtros tales como phpnuke y algunos sistemas en .NET, por esocorta por lo sano y haz una lista blanca, es rápido y ultra seguro, todos los inputs que no contengan una url real serán rechazadas aunque sean rutas validas, es lo mas sano.

MinusFour

Te estas llendo por las ramas WHK.

Filtrar texto si puede ser una mala práctica, pero cuando yo dije 'limpiar' (y lo puse entre comillas por una buena razón, porque no es exactamente limpiar) no me estaba refiriendo a filtrar el texto (que esto bien puede ser una mala forma de 'limpiar' input). Al decir yo limpiar, me estoy refiriendo a someter el input a un proceso que no permita una anomalía en la ejecución del código. Y 'limpiar' si que es una buena práctica.

Tampoco tienes que generalizar, filtrar texto se sigue haciendo. No como medida de seguridad pero si se sigue usando para los inputs.

gAb1

#7
Gracias, acaba de armar algo y parece funcionar correcto, ¿veis algo mal? ¿es seguro?

index.php
Código (php) [Seleccionar]
<?php

include_once 'themes/default/views/layouts/main.php';

$whitelist_home = array("home");
$whitelist_site = array("site/login""site/logout""site/contact""site/about");
$whitelist_user = array("user/register""user/referral""user/friend""user/register_success");

if(
in_array($_SERVER['QUERY_STRING'], $whitelist_home)) {
if(in_array('views/'.(string)$_SERVER['QUERY_STRING'].'.php'glob('views/*.php'))) {
$home = include('views/'.(string)$_SERVER['QUERY_STRING'].'.php');
} else include('views/404.php');
} elseif(
in_array($_SERVER['QUERY_STRING'], $whitelist_site)) {
if(in_array('views/'.(string)$_SERVER['QUERY_STRING'].'.php'glob('views/site/*.php'))) {
$content = include('views/'.(string)$_SERVER['QUERY_STRING'].'.php');
} else include('views/404.php');
} elseif(
in_array($_SERVER['QUERY_STRING'], $whitelist_user)) {
if(in_array('views/'.(string)$_SERVER['QUERY_STRING'].'.php'glob('views/user/*.php'))) {
$content = include('views/'.(string)$_SERVER['QUERY_STRING'].'.php');
} else include('views/404.php');
}

if(!
$_SERVER['QUERY_STRING']) {
echo $home = include('views/home.php'); // Esta es la pagina que se muestra por defecto
}

if(
$_SERVER['QUERY_STRING'] == "home") {
echo $home;
}elseif(
in_array($_SERVER['QUERY_STRING'], $whitelist_site)) {
echo $content;
}elseif(
in_array($_SERVER['QUERY_STRING'], $whitelist_user)) {
echo $content;
}else 
print_r(error_get_last());

?>


¿Hay algún problema si utilizo ese include_once (lo que uso actualmente para incluir paginas como functions.php) sin ningún if ni nada para mostrar una pagina (que es la base de la web) en el index.php?

Por alguna misteriosa razón justo despues del texto de home.php se añade un 1 sin espacio... ¿Que esta imprimiendo ese 1?

Aparte de que ese script de echos hay que mejorarlo, tiene que ir en el archivo main.php y cuando los he puesto han empezado los errores de variable no definida. Todo son problemas :)

Gracias!

MinusFour

Include devuelve 1 si la la inclusión es exitosa, si le haces echo a un include exitosa, este imprime 1. A menos que dentro de tus includes tengas un return, en ese caso el echo toma el valor del return.

El código de WHK está bien para validar todos los archivos de las carpetas. Si vas a trabajar con solo algunos archivos creo que es mejor si tienes una lista para tus includes.

e.g.

Código (php) [Seleccionar]

<?php

$views 
'views/';
$site $views 'site/';
$user $views 'user/';

$includes = array(
'home' => $views 'home.php',
'site/login' => $site 'login.php',
'site/logout' => $site 'logout.php',
'site/contact' => $site 'contact.php',
'site/about' => $site 'about.php',
'user/register' => $user 'register.php',
'user/referral' => $user 'referral.php',
'user/friend' => $user'friend.php',
'user/register_success' => $user 'register_success.php'
);

if(
array_key_exists($_SERVER['QUERY_STRING'], $includes)){
include($includes[$_SERVER['QUERY_STRING']]);
}
?>


gAb1

#9
Necesito saber como darle un valor por defecto a la variable server, si lo hago asi no lee ningun input y siempre se muestra home...
index.php
Código (php) [Seleccionar]
<?php

include_once 'themes/default/views/layouts/main.php';

$views 'views/';
$site $views 'site/';
$user $views 'user/';

$_SERVER['QUERY_STRING'] = 'home'// Alguna otra manera?

$includes = array(
'home' => $views 'home.php',
'site/login' => $site 'login.php',
'site/logout' => $site 'logout.php',
'site/contact' => $site 'contact.php',
'site/about' => $site 'about.php.php',
'user/register' => $user 'register.php',
'user/referral' => $user 'referral.php',
'user/friend' => $user'friend.php',
'user/register_success' => $user 'register_success.php'
);

if(
array_key_exists($_SERVER['QUERY_STRING'], $includes)) {
$content = include($includes[$_SERVER['QUERY_STRING']]);
return true;
} else include(
'views/404.php');

// Esto va en themes/default/views/layouts/main.php
/*
if(array_key_exists($_SERVER['QUERY_STRING'], $includes)) {
echo $content;
}else print_r(error_get_last());
*/
?>


Ahora mismo, sin ningun echo $content se muestra igualmente el include, ¿por que?
¿Como hago para que no se muestre ningún include si no hay un echo?

Gracias.

Edito: Y a todo esto... si necesito hacer inputs en esas paginas, por ejemplo, antes era: login.php?error=1 y ahora como seria? porque ?site/login&error=1 no funciona