PDA

Ver la Versión Completa : Cómo implementar un límite de "intentos de acceso"


dec
10-03-2009, 19:47:47
Hola a todos,

Me encontraba yo esta tarde aburrido, de manera que me he puesto a implementar cierta característica en Gesbit (http://www.gesbit.com) que llevaba tiempo queriendo hacer. La idea es limitar a un número máximo los intentos de acceder al sistema por parte de un usuario. Es decir, existe un formulario para autenticarse, y, el objetivo es que si el usuario intenta "logearse" más de cinco veces, por ejemplo, sin tener éxito, impedirle que continúe intentándolo, cuando menos, no mostrarle más el formulario en cuestión, sino un mensaje de error.

Lo primero que se me ha ocurrido ha sido utilizar una variable de sesión. Craso error. En efecto, funcionaría más o menos bien para un usuario "normal", es decir, alguien llega al formulario de acceso, intenta autenticarse, no lo consigue, y, a la quinta vez, el sistema le retorna un mensaje de error, y no le muestra más el formulario de autenticación hasta que no cierre su sesión de usuario (cierre y vuelva a abrir su navegador). O sea, que sí.

Pero, no. Parece que utililizar una variable de sesión no sirve para el caso de que un usuario intente enviar nuestro formulario de autenticación "desde un script", por ejemplo, escrito en PHP, utilizando la clase Snoopy, haciendo de navegador web. ¡Y precisamente este tipo de usuarios son a los que hay que intentar detener! Puesto que pueden, mediente un "script", hacer tantas peticiones de acceso como les de la gana... intentando así un ataque "por fuerza bruta" al sistema.

Ahora bien, como digo, para este tipo de "scripts", no parecen funcionar las variables de sesión, y es que siempre que el "script" se ejecuta se establece una variable de sesión nueva, de modo que nuestro "contador de intentos de acceso" siempre estaría "a cero". Entonces, mi pregunta es, ¿qué se puede usar aparte de una variable de sesión? ¿No queda más remedio que utilizar la base de datos para guardar allí el número de intentos? ¿Pero dónde se guardaría? ¿En una especie de tabla auxiliar? ¿Acaso no sería esto complicarlo demasiado? ¿Tú sabes de alguna otra solución?

Gracias a todos de antemano por vuestras posibles sugerencias. :)

roman
10-03-2009, 20:16:25
Una pregunta ingenua:

Si algo como Snoopy, genera en el servidor una nueva sesión en cada acceso, ¿qué podría hacer el tal snoopy en nuestro sistema dado que su sesión no se preseva? Esto es, nunca pasaría de la página del login ¿no?

// Saludos

dec
10-03-2009, 20:21:00
Hola,

Bueno. En principio sí que pasaría. El servidor, una vez considerado autenticado al usuario, le serviría la página correspondiente al panel de administración. Pero, en realidad no se prevee que desde Snoopy se hiciera algo, sino que, un "script" podría usar Snoopy para averiguar la respuesta del servidor, podría intentar un "ataque por fuerza bruta".

Se trata de conseguir los datos del usuario Román. Cuando el "script" recibiera "algo" que no fuera "Autenticación fallida", este guardaría el nombre de usuario y contraseña utilizados, puesto que habría conseguido autenticarse. Luego, quien programase el "script" ya haría uso por su cuenta de esos datos... no sé si me explico.

El caso es que, echando un vistazo, no encuentro mucha información en Internet, y, los sistemas que veo que implementan algo así, utilizan la base de datos para guardar el número de intentos de autenticación. Ahora bien, la cuestión es si no hay otra solución a esta (puesto que usar sesiones no da resultado) o si acaso existe otra opción...

roman
10-03-2009, 20:29:18
Pérmíteme insistir. Ya sé que usas cookies para mantener la autentificación del usuario, pero, al menos en mis caso, que uso sesiones, es imposible que el usuario haga nada en el sistema si no se preserva la sesión, por el simple hecho de que toda página lleva una sentencia así (ejemplo):


if (!isset($_SESSION['foo']))
{
die();
}


¿Podrías combinar sesiones con cookies?

// Saludos

dec
10-03-2009, 20:38:42
Hola,

Creo que llevas parte de razón al menos, Román, pero, recuerda que no estamos tratando de impedir que se haga algo en el sistema, sino que se pueda acceder al mismo, ¡con los datos del usuario! Se trata de robar las contraseñas, los datos de acceso. Como he dicho arriba, el "script" de Snoopy no trataría de hacer sino eso.

El "script" envía el formulario de autenticación, probando con ciertos "login y password". El "script" sabe que, por ejemplo, cuando en la respuesta del servidor se incluya un "Autenticación fallida", no puede dar por buenos el "login y password" conque ha intentado ese acceso. Pero, como puede hacer infinitos más... sigue probando.

Entonces, en el momento en que el "script" consigue una respuesta del servidor que no sea "Autenticación fallida", el "script" sabe que el login y la contraseña que acaba de probar garantizan el acceso al sistema. No al "script", el "script" habría terminado su trabajo. Simplemente guardaría esos datos.

Ahora, quien programase el "script" diría, Vamos a ver qué hemos pescado hoy. Y, en un archivo, por ejemplo, se encontraría con un mensaje del tipo, "He intentado entrar a esta URL, y, he podido hacerlo con estos datos". Ahora el programador se dirige a la URL (ya no con el "script", sino con un navegador) y usa esos datos para entrar al sistema...

Ahora bien. ¿Qué se hace ante esto? Pues, foros como este, utilizan un "contador de intentos de autenticación". Para evitar el "robo de contraseñas" por la "fuerza bruta". Porque el "script", a la que intentara autenticarse un número determinado de veces, se encontraría con un "Alto ahí, amigo, ya no puedes intentarlo más".

Y eso evitaría (en buena medida) que nadie se planteara robar las contraseñas de un sitio, pues ya no podría hacer, qué sé yo, miles de intentos en una hora, sino que podría hacer no más de unos cuantos al cabo del día... y así podría tardar años en conseguir una contraseña y usuario válidos.

Más o menos ese es el asunto Román. ;)

PD. Usando una base de datos... la cosa es bastante sencilla, como se echará de ver. Yo quisiera evitarlo, porque, no existe una tabla apropiada para tal cosa en Gesbit, de modo que, o bien incluyo una, o bien "encajo" este asunto en una tabla existente... podría preparar un plugin para Gesbit, que se encargara de este asunto, creando la tabla por su cuenta. Pero, considero el asunto lo suficientemente importante como para fuera Gesbit mismo quien diese una solución y no que lo hiciera un plugin.

roman
10-03-2009, 20:44:36
Ya, ya. Veo que estaba malentendiendo. Es que estaba comentando en cierta bitácora (http://www.bitacora.davidesperalta.com/) y no estab poniendo cien por ciento de atención :o

¿Has probado ese Snoopy vs tu cuenta del Club?

Digo, por curiosidad para saber si el vBulletin resiste.

// Saludos

dec
10-03-2009, 20:52:01
Hola,

Ya lo he visto, ya (http://www.bitacora.davidesperalta.com/rapidshare-television-a-la-carta/#comment-1325). :)

No he probado contra vBulletin, pero, estoy seguro de que resiste, porque, a buen seguro usa la base de datos para este asunto. Ya te digo, Román, en realidad es algo bastante sencillo. El escollo está, básicamente, en "persistir" el contador de intentos de autenticación. Como sé que vBulletin usa la base de datos, estoy seguro de que resistirá también a Snoopy, porque Snoopy no tiene nada que hacer ahí, puede cambiar la sesión de PHP, pero, no puede hacer nada en lo que toca a la base de datos. Igual es que no hay otra forma... usar la base de datos. Dita sea... :D

pcicom
10-03-2009, 22:05:16
Saludos DEC

Yo lo que hago es manejar el numero de intentos con SESSIONES y en caso de sobrepasar los intentos GRABO la IP y tambien el id del USUARIO para darle un lapso de 15,20,30 minutos para su nuevo REINTENTO..

La logica que utilizo es la siguiente.

1)
El Usuario LLENA el formulario de LOGIN PASSWORD
cuando le da SUBMIT se reenvia a un archivo de VERIFICACION
este archivo CHECA si la IP del NAVEGANTE esta en la BASE de BLOQUEOS
Si esta BLOQUEADO
Muestro MENSAJE de REINENTE mas TARDE
Redirecciono a OTRA PAGINA
No
CHECO LOGIN y PASSWORD en MI BASE
Acumula Numero de INTENTOS
Si Numero de INTENTOS es MAYOR al PERMITIDO
Grabo la IP en la TABLA de BLOQUEO
Si FALLA LOGIN o PASSWORD
Va a (1)
No
ACCESA AL SITIO.



SALUDOS..

dec
10-03-2009, 22:16:44
Hola,

Gracias por la respuesta pcicom. ¿Tú usas la base de datos, verdad? El asunto estaría en no tener que usarla, pero, me temo que es necesaria. El caso es que no funcionaría usando sólo la variable de sesión. Es menester "persistir" el contador de intentos, y, ahí es donde entra la base de datos.

Se admiten otras ideas. ;)

dec
10-03-2009, 23:35:47
Hola,

No sé qué tal parecerá, pero, estoy por implementar el asunto utilizando la tabla "options" que existe ya en Gesbit. De este modo, se añadiría una nueva opción (un nuevo registro) llamado "gbaccessretries", por ejemplo. Este registro sería con el que trabajaría la clase "GbUser", desde el formulario de autenticación y desde el de recuperación de los datos (nuevas contraseñas). Creo que, estirando un poco, una opción así puede caber en dicha tabla. ¿Y vosotros cómo lo veis esto? :rolleyes:

PD. Sea como sea, el asunto se complica más de lo que en principio me parecía. No hay que guardar sólo el número de intentos, sino también quién (la IP) hace dichos intentos, luego, tampoco bastaría un registro de una tabla... sino una tabla con varios registros, o varios registros, en todo caso... me temo que de momento voy a recomendar usar contraseñas "fuertes" (:D), para limitar los ataques por fuerza bruta. Esto hay que tomárselo con mucha más calma.

pcicom
11-03-2009, 00:49:00
Hola,

Gracias por la respuesta pcicom. ¿Tú usas la base de datos, verdad? El asunto estaría en no tener que usarla, pero, me temo que es necesaria. El caso es que no funcionaría usando sólo la variable de sesión. Es menester "persistir" el contador de intentos, y, ahí es donde entra la base de datos.

Se admiten otras ideas. ;)


Pues tambien puedes grabar los INTENTOS y las IP en un archivo de TEXTO, ya seria tu decicion solo tendrias que hacer los barridos buscando la IP y la FECHA HORA del ultimo INTENTO..

Al final de cuentas como la tabla de boqueos va a ser muy pequeña, no afecta en el performance de nada al servidor web,, SALVO que te ataquen por todos lados a tu SITIO...

dec
11-03-2009, 00:59:08
Hola,

Sí; ya tengo claro que toca usar la base de datos, mejor que un archivo de texto, aunque, en efecto, sería parecido. Pero como la base de datos existe... podría prepararse ahí lo necesario. Ahora es cuestión de ponerme con ello, si es que decido hacerlo. El caso es que este asunto no puede hacerse con sesiones, por lo dicho al principio. Y no es tan sencillo como yo lo había previsto en principio. Pero, sea como sea, dando por supuesto el uso de la base de datos, es cuestión de ponerse a ello. Gracias pcicom por tu interés. ;)

roman
11-03-2009, 20:42:26
A ver, no quiero quitar el dedo del renglón.

El problema para usar sesiones es que herramientas como Snoopy no tienen manera de guardar las cookies que reciben, incluida la PHPSESSID de sesión, y por tanto, en cada petición, el servidor piensa que es la primera y genera una nueva sesión.

Ahora bien, ésta no es la situación de un usuario normal, y si alguien quiere usar Snoopy para algún fin lícito, tendrá que preocuparse por como preservar esa cookie y enviarla al servidor.

Entonces, el problema está con los chicos malos que intentarán confundir al servidor explotando esta particularidad.

Siendo así, ¿por qué no aprovechar esta amnesia de sesión para detectar si alguien no está jugando limpio?

La idea es generar un valor aleatorio o token al momento de presentar el formulario de login. Este valor se guarda en la sesión y se manda junto con el formulario como un campo hidden. El script que recibe los datos del formulario, verifica que exista dicho campo oculto y que su valor coincida con el que tiene guardado en la sesión.

Esto, de entrada, impide usar Snoopy para invocar directamente al script que procesa el formulario -los datos tiene que enviarse forzosamente desde el formulario que presentamos nosotros mismos. Snoopy podría "leer" ese formulario, usar el DOM para leer el campo oculto y enviarlo junto con la petición al script procesador. Esto es lo que haría alguien que quiere utilizar Snoopy u otra herramienta similar con un fin genuino. Pero si no es así, entonces la sesión no se preservará y el script que recibe los datos del formulario no reconocerá el campo escondido pues no lo tendrá guardado en la sesión, y, de esta manera, sabrá que se trata de una petición no válida.

En resumen, que les damos una cucharada de su propia medicina.

¿Cómo ven?

Claro que esto no invalida la opción de usar la base de datos, pero es una alternativa que, de hecho, se usa con frecuencia para dar mayor seguridad a nuestros formularios de login (buscar forms+tokens en google).

// Saludos

roman
11-03-2009, 22:57:30
Hola,

Creo que me enredé demasiado con los tokens.

Basta guardar una variable de sesión cualquiera en la página que muestra el formulario y preguntar por ella en el script que procesa el formulario.

Si no existe la variable, consideramos inválida la petición y abortamos. El atacante podrá hacerlo cuantas veces quiera que siempre obtendrá la misma respuesta: "petición no válida".

La única forma que tiene para no obtener dicha respuesta es preservando la sesión, y una vez hecho esto ya lo tenemos amarrado y podemos contar los intentos con una variable de sesión, que, de hecho, puede ser la misma que la otra:

login-form.php

<?php
session_start();
$_SESSION['intentos'] = 0;
?>
<form method='post' action='login.php'>
<div>
<label for='user'>User</label>
<input type='text' name='user' id='user'>
</div>

<div>
<label for='password'>Pwd</label>
<input type='text' name='password' id='password'>
</div>

<div>
<input type='submit'>
</div>
</form>



login.php

<?php
session_start();

if (!isset($_SESSION['intentos']))
{
die('Petición no válida');
}

/*
Verificamos los datos de inicio
*/

...

/*
Si son incorrectos, marcamos un intento más
*/

$_SESSION['intentos']++;

/*
Y lo mandamos a volar si ya rebasó el máximo permitido
*/
if ($_SESSION['intentos'] > 3)
{
die('Demasiados intentos');
}
?>


// Saludos

dec
12-03-2009, 00:27:03
Hola,

Antes de nada, te agradezco el interés Román, doblemente, por la posible solución. Digo posible, porque, aunque todavía no lo he probado tal como dices, me entran dudas sobre el uso de las sesiones, puesto que es lo que había intentado anteriormente. ¿No se supone que las variables de sesión son únicas para cada sesión? Pero, si el "script" cambia dicha variable de sesión... ¿no estamos como al principio? Pero, igual es que no he leído bien tus mensajes Román.

Echaré un vistazo, haré las pruebas oportunas, y comentaré aquí los resultados. Gracias de nuevo Román. ;)

roman
12-03-2009, 00:37:06
Pero, si el "script" cambia dicha variable de sesión... ¿no estamos como al principio?

No podemos evitar que nos cambien la sesión. El punto aquí es que si lo hacen, nosotros lo detectamos y los mandamos a volar. Dicho de otra forma, la única manera de que puedan autenticarse en nuestro sistema es no cambiando la sesión, con lo cual tenemos un ambiente controlado.

Olvídate un poco de los tokens (son útiles pero no se requiere tanto) y fíjate en el ejemplo que puse:

1. Es imposible llamar directamente a login.php pues no habrá ninguna sesión y por tanto ninguna variable $_SESSION['intentos']

2. Esta variable se establece en login-form.php

3. Si en el paso de login-form.php a login.php cambia el ID de sesión, entonces login.php ya no reconocerá la variable intentos.

1 y 3 vienen a ser lo mismo. El caso es que ni siquiera se llega a verificar usuario y contraseña, y por tanto el atacante no tiene forma de decidir si eran datos correctos o no.

// Saludos

dec
12-03-2009, 00:42:23
Hola,

Bueno. Ahora mismo lo que más me llama la atención, es que, en el caso de Gesbit, existe un "user-login.php", donde se realiza la autenticación. No hay dos "scripts", sino uno solamente. Pero, perdona que insista Román (porque igual lo de los "tokens" resulte más conveniente), precisamente, porque no contamos con una sesión "estable", es que no podemos basarnos en ella para este caso.

1º Establezco la variable de sesión "contador"

2º Reviso si existe la variable contador (para incrementarla, si es preciso) ¡Error!

No existe la variable "contador"... no se ha establecido para la sesión "actual"... porque el "script" (Snoopy) siempre juega con una nueva sesión, la variable "contador" ya no existe..., dicho de otro modo, siempre estaríamos estableciendo una variable "contador", pero, no podríamos incrementar esa misma variable contador: de ahí la necesidad de usar una base de datos... para hacer persistir sí o sí esa información, para desligarla de la sesión de usuario.

¿No? :rolleyes: :D

roman
12-03-2009, 00:55:08
Por un sólo script, me imagino que te refieres a que el mismo presenta el formulario y el mismo procesa los datos. Pero aún así son dos scripts distintos: el mismo a dos tiempos.

Sería algo así:

user-login.php

<?php
session_start();

if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
// Aquí el "segundo" script

if (!isset($_SESSION['intentos']))
{
die('Petición no válida');
}

/*
Verificamos los datos de inicio
*/

...

/*
Si son incorrectos, marcamos un intento más
*/

$_SESSION['intentos']++;

/*
Y lo mandamos a volar si ya rebasó el máximo permitido
*/
if ($_SESSION['intentos'] > 3)
{
die('Demasiados intentos');
}

exit;
}

$_SESSION['intentos'] = 0;
?>
<form method='post' action='login.php'>
<div>
<label for='user'>User</label>
<input type='text' name='user' id='user'>
</div>

<div>
<label for='password'>Pwd</label>
<input type='text' name='password' id='password'>
</div>

<div>
<input type='submit'>
</div>
</form>



2º Reviso si existe la variable contador (para incrementarla, si es preciso) ¡Error!


¡Exacto! Ése es el punto. Si no existe la variable contador, es un error. No es que haya que volverla a inicializar, es un error y le mandamos el


die('Petición no válida');


La única forma en que no se dé ese error es teniendo definida esa variable de sesión y eso lo hacemos al presentar el formulario. Si el listillo nos cambia la sesión pues el afectado es él porqué recibira el error.

// Saludos

dec
12-03-2009, 01:03:47
Hola,

A ver, que, quiero cogerle el punto (atentos argentinos queridos, cogerle el punto aquí significa lo que significa, ¡y nada más!). :D


La única forma en que no se dé ese error es teniendo definida esa variable de sesión y eso lo hacemos al presentar el formulario. Si el listillo nos cambia la sesión pues el afectado es él porqué recibira el error.


Hum... o sea, tratar de aprovechar, precisamente, que no se puede mantener la sesión en condiciones. Pero, ¿esto cómo lo hacemos? Pongámonos en el caso.

El "script" (Snoopy) enviará, directamente, el formulario. Luego siempre estaremos "como en un principio". Siempre será "la primera vez" que se trata de envíar nuestro formulario. Si establecemos una variable de sesión ahí, de nada nos serviría, porque, no la tendríamos a nuestra disposición la siguiente vez. Pero, no sabríamos si es que es la siguiente vez (después de haberla establecido) o no, porque, como el "script" inicia una sesión nueva cada vez... siempre será como la primera vez (argentinos, que os veo con el cachondeíto).

No termino de verlo claro Román. El "script" (Snoopy) no quiere entrar en nuestro sistema, sólo quiere saber si el formulario se procesa bien o mal, si consigue autenticarse con unos datos de usuario, o no lo consigue. No es posible que mantenga (de hecho, ni querrá hacerlo) una sesión de usuario, luego nosotros no podemos basarnos en sesiones de usuario... simplemente es que no podemos... yo lo ví tan claro (que podía hacerse) que hasta me precipité en implementarlo, pero, en cuanto probé con Snoopy... caí en la cuenta de que no podía basarse algo así en variables de sesión: porque no podemos contar con una sesión de usuario.

dec
12-03-2009, 01:14:34
¿Te convenciste? :D :D :D

roman
12-03-2009, 01:20:06
Haz la prueba David, no me digas nada más que no se puede. Sigue el flujo de lo que pasaría con el tal snoopy intentando acceder (#$@&%! perro, ¡cómo da lata! :D)

Caso A) Snoopy intenta acceder directamente a login.php. No importa si es la primera vez o la chorrocientas. Como el condenado perro cambia la sesión, para login.php será como si fuera la primera vez. Por tanto le da una patada.

Caso B) Snoopy intenta acceder primero a login-form.php. Ahí se establece la sesión, pero, como el condenado perro la cambia, al acceder a login.php estamos como en el caso (a) y le damos una patada.

La única posibilidad que tiene el animal, es preservando la sesión.

pd: Las patadas al perro son simuladas. En el desarrollo de este código no se ha lastimado a ningún cachorrito :D.

// Saludos

dec
12-03-2009, 01:36:15
Hola,

Jodó con el perrito. :D

¡Pero no digas que no lo probé! Inicié este mismo hilo porque lo había probado y, para mi sorpresa, no funcionaba...


Caso A) Snoopy intenta acceder directamente a login.php. No importa si es la primera vez o la chorrocientas. Como el condenado perro cambia la sesión, para login.php será como si fuera la primera vez. Por tanto le da una patada.


El punto está en que "Como el condenado perro cambia la sesión, para login.php será como si fuera la primera vez". No es que el condenado chucho cambie la sesión, sino que no la inicia nunca, o, dicho de otra forma (pero igual resultado), siempre inicia una nueva. Y no podemos darle la patada a alguien que llega por primera vez a nuestro sitio... ¡ni aunque sea un chucho, que los hay podencos! :D


Caso B) Snoopy intenta acceder primero a login-form.php. Ahí se establece la sesión, pero, como el condenado perro la cambia, al acceder a login.php estamos como en el caso (a) y le damos una patada.


En el caso de Gesbit existe un formulario, un "script", que, junto con la clase GbUser (y GbDb, entre otras, pero, es otra historia) se encargan de la autenticación del usuario. Pero, a todos los efectos, es como si fuera un solo "script". Creo que esto puede confundirte Román.

En efecto, si hubiese que pasar por dos "script", incluso si hubiera que pasar por el mismo, dos veces, el asunto pintaría de otra forma, podríamos usar la sesión de usuario, y, en caso de no encontrarla en el segundo "script", denegar el acceso al can. Esto sería una solución ideal, acaso, pero, recuerda que el perrito rellena y envía el formulario en un mismo punto.

No ha de pasar por dos lugares, por tanto, no puede usarse el primero para establecer la variable de sesión que comprobar en el segundo. En realidad sólo ha de pasar por un lugar. Y podemos establecer la variable de sesión que queramos, porque, como cuando vuelva a pasar, lo hará con otra sesión de usuario diferente, dicha variable no existirá, no podremos usarla, no nos servirá de nada.

Ya digo. vBulletin lo implementa apoyándose en la base de datos. PhpBB ni siquiera lo implementa... si pudiera hacerse usando sesiones de usuario, de alguna manera, sería bastante sencillo de implementar, y, sin embargo, no se hace, porque no funciona. Funciona para personas, pero, con los cánidos parece que no pueden usarse sesiones de usuario, al menos no para lo que nos ocupa ahora. :(

dec
12-03-2009, 01:51:00
Hola,

Hum... ¿y si forzáramos tanto a cuadrúpedos como a personas a pasar dos veces por el "script"? Dicho de otro modo, mirar si existe la variable de sesión cuando se envía el formulario, y, si no existe, mandar a quien sea a freír espárragos. Huy, huy, huy, que me parece que tú ibas por aquí y no lo he sabido captar hasta ahora...

Claro que habrá que pensar en los posibles inconvenientes... pero, da que pensar. :rolleyes:

roman
12-03-2009, 01:54:28
¡Pero no digas que no lo probé! Inicié este mismo hilo porque lo había probado y, para mi sorpresa, no funcionaba...


Lo que tú probaste no es lo mismo que yo propongo, ojo.


Y no podemos darle la patada a alguien que llega por primera vez a nuestro sitio...


Claro que podemos, y lo haremos, si no llega por la puerta adecuada.


En el caso de Gesbit existe un formulario, un "script", que, junto con la clase GbUser (y GbDb, entre otras, pero, es otra historia) se encargan de la autenticación del usuario. Pero, a todos los efectos, es como si fuera un solo "script". Creo que esto puede confundirte Román.


Al contrario, si hablo de dos scripts es sólo para facilitar la charla y no decir: el script en una petición GET y el script en una petición POST, por ejemplo :)


En efecto, si hubiese que pasar por dos "script", incluso si hubiera que pasar por el mismo, dos veces, el asunto pintaría de otra forma, podríamos usar la sesión de usuario, y, en caso de no encontrarla en el segundo "script", denegar el acceso al can. Esto sería una solución ideal, acaso, pero, recuerda que el perrito rellena y envía el formulario en un mismo punto.


No es el mismo punto. Aunque uses un mismo archivo user-login.php, se trata de momentos distintos:

1. Cuando se despliega el formulario (y se establece la variable de sesión)
2. Cuando se procesa el formulario (y se checa la existencia de la variable)

Ya también te puse el ejemplo de ese caso (un sólo script)


No ha de pasar por dos lugares


Que sí, hombre, que sí.

Esto es como el espacio-tiempo (¿recuerdas a Carl Sagan?). La puerta de tu casa hoy no es el mismo lugar que la puerta de tu casa mañana. En términos del espacio-tiempo son dos ubicaciones distintas. No es lo mismo el script en una petición GET que el script en una petición POST, son dos ubicaciones distintas en el HTTP-tiempo :D


vBulletin lo implementa apoyándose en la base de datos.


// Saludos

dec
12-03-2009, 01:59:08
Hola,

Que sí, que sí, ahora lo veo un poco más claro. No sé si es que me he ofuscado en contra del uso de variables de sesión, pero, en efecto, como has explicado, se trataría de no aceptar en nuestra casa a nadie que entrara a trompicones. ¿No te limpiaste los pies en el felpudo de la entrada? Pues no entras. ¿Te los limpiaste? Deja que lo vea... vale, pasa pa'dentro.

Tiene cierta lógica y, en efecto, le encuentro sentido. Ahora, lo que me llama la atención es que sistemas populares no implementan algo así, si, como parece, es bastante sencillo... (aunque no evidente) ¿acaso habrá alguna razón que se nos escapa? Pero, sea como sea, es cierto, tengo que probarlo de nuevo teniendo todo lo que has explicado en cuenta. ¡Gracias Román! :)

dec
12-03-2009, 02:12:24
Hola,

Estoy haciendo pruebas, no me quiero ilusionar, pero, ¡parece que podría valer! :D

roman
12-03-2009, 02:36:22
Mmmm.

Se me ocurre que podemos amaestrar un perro y hacer que se comporte mordiendo sólo tres veces. Pero, podemos amaestrar más de un perro ¿no?

:(

// Saludos

dec
12-03-2009, 02:42:16
Hola,

Yo ahora mismo estoy probando, y, con alguna que otra "dificultad inesperada" parece que podría valer, ¿por qué dices ahora esto último? ¿Has averiguado algo? :rolleyes:

roman
12-03-2009, 02:48:08
Pues que a final de cuentas, tienes razón. A usar la base coño :D

Es cierto, con el mecanismo que propongo, el perro tiene que preservar la sesión para siquiera poder intentar el login. Pero ¿y eso qué? En cuanto reciba el ok o not ok de la autenticación, el condenado cánido cambia la sesión. Ya es otro perro y vueve a intentar, una y otra vez. Mientras no se amarre la IP (debo repetirme esto cien veces), no tiene ningún caso.

// Saludos

dec
12-03-2009, 02:55:04
Hola,

¡Pero cómo que no! ¡¡Si acabas de convencerme de lo contrario!! :D :D :D

Lo estoy ahora mismo probando Román, y, parece que funciona... Este es el código que de momento está ejecutándose al "entrar" al formulario:


public function AssertAccessRetries(){
global $gbInput;
if(isset($_SESSION[GBUSER_SESSION_ACCESS_RETRIES])){
if($_SESSION[GBUSER_SESSION_ACCESS_RETRIES]
>= GBUSER_MAX_ACCESS_RETRIES_NUM){
return new GbError(array(
ra('Maximum user access retries detected.'),
ra('Please, close your session and try again.')
));
}
}else{
if($gbInput->IsHttpGetRequest()){
$_SESSION[GBUSER_SESSION_ACCESS_RETRIES] = 0;
}
}
}


Y, este otro, se ejecutaría sólo cuando se enviase el formulario:


if(!isset($_SESSION[GBUSER_SESSION_ACCESS_RETRIES])){
return new GbError(ra('Invalid user session. Please, try again.'));
}else{
$_SESSION[GBUSER_SESSION_ACCESS_RETRIES]++;
}


El primero me parece más curioso que el segundo: fíjate que si no existe la variable de sesión, no la establece sin más: sólo lo hace si la petición es HTTP GET. Como pulgoso manda el formulario vía HTTP POST... se va a encontrar (y de hecho se encuentra) con el segundo código...

Ya digo, el asunto parece funcionar, aunque, no dudo de que pudiera "complicarse" lo que quisiese, guardando IPs, fechas, etc. Sin embargo, creo que podría valer tal cual, sin tantas complicaciones, pues, los problemas no los tendría el usuario normal y corriente, sino los caniches "roba contraseñas"... y de eso se trata, ¿no?

¡¡¡Ahora vas a decirme que no!!! :D :D :D

Lo que hay que hacer es pruebas y más pruebas... suponiendo varias posibilidades, cuantas más mejor, y ver si el invento aguanta o qué. :)

roman
12-03-2009, 03:09:56
http://img27.imageshack.us/img27/9467/snoopyr.png (http://img27.imageshack.us/my.php?image=snoopyr.png)

No sirve.

A ver, tú mismo dijiste desde el principio, que si el usuario accede como gente bien desde un navegador, sólo puede hacer tres intentos y no podrá volver hasta que no cierre su sesión. Ahora el perro hace lo mismo: manda la cookie de sesión (que lee en el primer acceso al formulario) y queda como perro bueno y tu script intenta la autenticación. Ni siquiera vuelve a intentarlo, simplemente cambia -ahora sí- la sesión e intenta de nuevo, bien portado, y es como si fuera la primera vez.

Tiene que amarrarse la IP.

// Saludos

dec
12-03-2009, 03:17:34
Hola,

Bueno. Mientras hacía las pruebas, algunas mejoras he añadido, de todas formas. Y sigo haciendo pruebas... porque chico, ¡ahora parece que sí que sirve! Estoy intentándome poner en la piel del pitbull, a ver qué tendría que hacer, según tú dices... Estoy aquí con Snoopy y ya no puede enviar, directamente, el formulario, eso está claro. Se me había saltado antes comprobar si se enviaba el formulario mediante HTTP POST, esto ahora se comprueba. De modo que no puede hacer una petición HTTP GET, porque, aun con datos buenos, no funcionaría.

Ahora tú dices que podría hacer primero una petición, y luego otra, manteniendo la sesión, enviando la cookie correspondiente, pero, si lo hace así, ¿no estaríamos consiguendo lo que queremos? Tengo un lío de espanto, ahora mismo. De todas formas, lo siento sólo por ti, pues, como digo, entre pitos y flautas he hecho algunos cambios y mejoras que ya no me las quita nadie. Y sigo probando a ver... :)

dec
12-03-2009, 03:23:51
Hola,

Sigo probando, Román, y, enviando la cookie de sesión, lo que obtengo con el "script" es que al cabo supero el número de intentos... ¿no se cumple ya con esto el objetivo? El pitbul puede pasarse por caniche, pero, entonces jugará con las reglas, y sólo tendrá un número de intentos posibles, dificultando el ataque por fuerza bruta. O sea, que, bien. ¿O tengo que probar de otra manera? :rolleyes:

roman
12-03-2009, 03:26:55
Ya te caerá el veinte, ya te caerá :p

A ver. Estás de acuerdo que con un navegador normalito puedo hacer tres intentos y ya, ¿no es cierto? Aún con el mecanismo de ahora.

Pero también estás de acuerdo que si cierro el navegador puedo realizar otros tres intentos ¿no?

Pues esto es lo que hace el perro. Luego de sus tres intentos, "cierra" el navegador cambiando la cookie de sesión (de hecho, simplemente no mandándola), y va de vuelta la mula al trigo...

// Saludos

dec
12-03-2009, 03:29:00
Hola,

Sí; acabo de caer del guindo, como suele decirse. Je je je... ¿ves como no era tan sencillo?

¡Te lo he dicho desde un principio! Y tú erre que erre... :D :D :D

¡Fail! :D

PD. De todas maneras, insisto, he hecho algunos cambios por el camino bastante útiles. ;)


1.3 - 12/3/2009

[?] Better recognition of HTTP POST form submitted in admin forms
[?] Change GBCACHE_EXPIRE_SESSION_CHECK for more descriptive value
[?] GbError class send now an HTTP 404 error code to clients
[+] GbInput IsPostFormSubmit, IsHttpGetRequest, IsHttpPostRequest


No está mal para empezar el día, digo la noche, bueno, allí el día, o la tarde, aquí la noche, madrugada. ¡Eh! :D

roman
12-03-2009, 03:29:53
Vamos a ver si cuando te convenza de esto no ya me convenciste tú a mi de lo contrario, y así ad infinitum, o, como se dice, ad nauseum :D

// Saludos

roman
12-03-2009, 03:31:39
Tengo que volver a leer acerca de los token, porque es seguro que se usan mucho. Incluso el vBulletin los usa (mira el formulario de inicio de sesión y verás un campo con un valor bastante rarito).

Bueno, creo que es mejor irse a descansar :)

// Saludos