2011-10-21 49 views
5

Francamente, está causando demasiada molestia en v1.0 para tener una funcionalidad que requiere tres presentaciones de formularios, con $_SESSION datos de sesión que contienen todos los elementos intermedios, solo para que un usuario inicie una operación, luego abra una segunda pestaña y realice una segunda operación que realiza un pisoteo sobre los datos de la sesión.¿Cómo restringir mi aplicación a una sola pestaña del navegador?

Dudo que esto sea malicioso (pero no puedo descontarlo). Lo más probable es que el usuario inicie una operación, se interrumpa, olvide que comenzó o no puede encontrar la pestaña original, así que comienza de nuevo (luego encuentra la pestaña original e intenta completar la operación por segunda vez).

Como estoy programando en PHP, puedo detectar la existencia de datos de sesión en el envío del formulario (¿cómo podría hacerlo con JS si el usuario abre otra pestaña? Supongo que necesitaría Ajax, ¿no?) .

Por lo tanto, cada vez que inicio una operación, busco un indicador en los datos de la sesión y si lo configuro lo recargo a "Lo siento, Dave. Me temo que no puedo hacer esa página, de lo contrario configuro la bandera y continúo (recordando borrarla al final de la operación).

Supongo que eso funcionaría, pero:
1) ¿Es aceptable restringir las aplicaciones del navegador a una única pestaña/instancia?
2) ¿Debo intentar permitir varias instancias en v2.0?

¿Algún otro comentario, ayuda o consejo?

+0

Si tiene este problema, probablemente también tenga problemas para que el usuario haga clic en el botón Atrás del navegador y luego interactúe con una página anterior, incluso sin varias pestañas. No puede suponer que '$ _SESSION' siempre está sincronizado con el navegador. – Wyzard

Respuesta

5

Un mejor diseño sería evitar almacenar el estado de interacción del usuario en la sesión. Póngalo en campos de formulario ocultos o algo así para que cada solicitud del cliente lleve consigo su estado asociado. Si le preocupa que el usuario lo altere, use un HMAC para evitarlo, y posiblemente encripte si contiene cosas que el usuario no debería poder ver.

Solo indique que debe ser compartido entre pestañas, como la identidad de inicio de sesión del usuario, o algo así como un carrito de compras, debe almacenarse en la sesión.

2

Con cada navegador que admita la navegación con pestañas, sería una experiencia de usuario deficiente intentar restringir la navegación a una única pestaña (también podría hacer una aplicación de escritorio).

Una forma de resolver esto es agregando un token CSRF a sus formularios (como una variable oculta), que se enviaría con la solicitud.

CSRF reference

Hay muchas maneras de generar el token, pero esencialmente que:

  1. crear el token tienda
  2. en su salida $_SESSION
  3. la forma con <input type="hidden" name="{token name}" value="{token value}" />

Luego, cuando el formulario te envíe c diablos $_REQUEST['{token name}'] == $_SESSION[ {token name}] `.

Si ese token es diferente, usted sabe que no fue el formulario que generó originalmente y, por lo tanto, puede ignorar la solicitud hasta que el formulario real entre con el token correcto.

Una cosa: si un atacante puede descubrir cómo se generan los tokens CSRF, entonces pueden falsificar solicitudes.

+0

Parece que formulé la pregunta incorrectamente. El navegador puede tener tantas pestañas abiertas como quiera, pero solo una de ellas puede ser la mía – Mawg

4

Lo máximo que puede hacer es mantener una lista de "última página solicitada" en el archivo de sesión, con indicadores que indiquen que no se le puede permitir al usuario moverse si es uno de estos indicadores de forma crítica. Por lo tanto, si está en form.php y es no-move-off, cualquier página nueva cargada debe presentar una opción de "cancelar o cerrar ventana".

No puede evitar que un usuario abra otra pestaña/ventana, pero puede evitar que se muevan a otra parte de su sitio en esas otras ventanas/pestañas.

Sin embargo, tenga en cuenta que esta es una experiencia de usuario muy pobre. Imagínese si Amazon lo atrapó en la página del carrito de compras y nunca le deja pasar a otra página sin tener que comprar algo realmente. Considere actualizar su código para permitir que varias ventanas diferentes usen el mismo formulario.

+0

+1 Me gusta su solución. Y estoy de acuerdo en que debería arreglarse en 2.0, pero ... – Mawg

+0

por cierto, ¿cómo otras personas abordan esto? Como los datos de sesión son pan-browser, ¿qué ocurre si el usuario realiza la misma operación de varias etapas en paralelo en dos pestañas o ventanas del mismo navegador, que comparten los mismos datos de sesión? – Mawg

+0

@Mawg: Simplemente almacene los datos intermedios en campos ocultos en el formulario. (ver [respuesta de Wyzard] (http://stackoverflow.com/questions/7844415/x/7844704#7844704)) – icktoofay

2

Agregado el guión a continuación después de login (dashboard.php digamos)

<script> 
$(document).ready(function() 
{ 
    $("a").attr("target", ""); 
    if(typeof(Storage)    !== "undefined") 
    { 
     sessionStorage.pagecount = 1; 
     var randomVal    = Math.floor((Math.random() * 10000000) + 1); 
     window.name     = randomVal; 
     var url      = "url to update the value in db(say random_value)"; 
     $.post(url, function (data, url) 
     { 
     }); 
    } 
    else 
    { 
     var url      = "url to remove random_value";   
     $.post(url, function (data, url) 
     { 
      sessionStorage.removeItem('pagecount'); 
      sessionStorage.clear(); 
      window.location   = 'logout.php'; 
     }); 
    }  
}); 
</script> 

Agregado el guión a continuación en la cabecera de resto de mis páginas - 'random_value' es de db para ese usuario

<script> 
$(document).ready(function() 
{  
    $("a").attr("target", "_self"); 

    if(typeof(Storage)      !== "undefined") 
    { 
     if (sessionStorage.pagecount) 
     { 
      if('<?=$random_value?>'   == window.name) 
      { 
       sessionStorage.pagecount = Number(sessionStorage.pagecount) + 1; 
      } 
      else 
      { 
       var url      = "url to remove random_value";   
       $.post(url, function (data, url) 
       { 
        sessionStorage.removeItem('pagecount'); 
        sessionStorage.clear(); 
        window.location   = 'logout.php'; 
       }); 

      }    
     } 
     else 
     {   
      var url       = "url to remove random_value";   
      $.post(url, function (data, url) 
      { 
       sessionStorage.removeItem('pagecount'); 
       sessionStorage.clear(); 
       window.location    = 'logout.php'; 
      }); 
     } 
    } 
    else 
    { 
     var url        = "url to remove random_value";     
     $.post(url, function (data, url) 
     { 
      sessionStorage.removeItem('pagecount'); 
      sessionStorage.clear(); 
      window.location     = 'logout.php'; 
     }); 
    } 
}); 
</script> 
0

Si estuviera haciendo esto ahora, probablemente codificaría una sola página de la aplicación AngularJs (aunque cualquier forma de J lo haría).

En la puesta en marcha, busque en el almacenamiento local una bandera. Si está configurado, rehúsese a iniciar, con el mensaje adecuado, en caso contrario, establezca el indicador & ejecute la aplicación.

Claro, un usuario malicioso podría evitarlo, ya que no es una verificación del lado del servidor, pero me negaría a admitir tal.

Cuestiones relacionadas