entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

2votos

Evitar que un archivo php sea llamado directamente desde el navegador

Un saludo a toda la comunidad, esta es una interrogante que nació a partir de esta otra pregunta (resuelta) en otro hilo:
http://entredesarrolladores.com/5256/es-posible-transmitir-un-video-de-esta-forma-con-php

A modo de resumen es lo siguiente:

Cuento con un reproductor web (jwplayer) el cual se me encargó proteger para evitar que el video que se va a transmitir pueda ser descargado desde el servidor (o al menos hacerlo lo más difícil posible). La solución pasó en transmitir el flujo de datos multimedia a través de un archivo .php (video.php) y no desde un .mp4 o una extensión similar, así en en index.php que es donde se llama jwplayer se crea un token de verificación y este luego se valida y desecha en video.php, y de este modo el link del video es de un solo uso hasta que se vuelva a recargar la página.

El problema está en que el link a video.php junto a su token son visibles desde el código fuente de index.php y entonces únicamente basta con no darle play al video y luego copiar el link en el navegador para poder descargar el contenido. Entonces lo que busco es una forma de evitar que video.php pueda ser llamado directamente desde el navegador.

En el otro hilo, Leonardo me brindó la siguiente solución:

// Verifica que el llamador sea únicamente $referer
$refererArray = explode('?', basename($_SERVER['HTTP_REFERER']));
$referer = $refererArray[0];
if($referer != 'tu_script_que_llama_al_video.php')
{ die("Llamada incorrecta (X)"); }

El problema está en que con esa condición por alguna razón el video solo logra transmitirse en Chrome y Firefox pero no en ningún otro navegador, ya sea de escritorio o móvil.

Entonces yo intenté relajar más la restricción y al menos evitar que el archivo php pudiera ser llamado directamente (aunque no se verificara desde dónde era llamado) de la siguiente forma:

$referer = $_SERVER['HTTP_REFERER'];
if(!is_null($referer)) {
  ...El código
}

Y así al menos logro que el video se muestre en los navegadores de escritorio, pero no en los navegadores de dispositivos móviles.

Pensé entonces en intentar validar "video.php" para que solo pudiera ser llamado por el mismo servidor de la siguiente forma:

if ($_SERVER['SERVER_ADDR'] == $_SERVER['REMOTE_ADDR']) {
   ... El código para transmitir el video.
}

Y así evitar que pudiera ser llamado directamente, pero al parecer entendí mal el funcionamiento porque la condición no se cumple aunque "video.php" sea llamado desde index.php por medio del reproductor.

Y la única forma que logro que el reproductor trabaje bien en todo tipo de navegador, es eliminando por completo cualquier restricción en video.php.

El contenido html de index.php es el siguiente:

<html>
<head>
<script src="jwplayer-6/jwplayer.js"></script>
<script>jwplayer.key="N6msPwo6K+7NU4s2Ucw1GamiGTGyRF45QdYWnw==";</script>
</head>
<body>
<div id="secureplayer">Cargando el reproductor...</div>
<script type="text/javascript">
  jwplayer("secureplayer").setup({
      width: '100%',
      aspectratio: '16:9',
      playlist: [{
          sources: [{ 
            file: "video.php?token=04238c2d5daecb34bb481114b20ea368",
            type: "video/mp4"
          }],
      }]
  });
  </script></body>
</html>

¿Alguna idea para lograr proteger video.php de las llamadas directas? Gracias de antemano.

0voto

carlossevi comentado

Creo que la composición de la petición GET hecha desde JW Player es la misma que la que hace cualquier navegador web, por lo que será complicado distinguir las peticiones desde video.php para rechazar las que no deseas.

De todas formas, deberías asegurarte utilizando un analizador de red (el mismo de Firefox por ejemplo debería servirte) para ver todos los detalles y cabeceras de las 2 peticiones GET para ver si encuentras alguna diferencia. En el caso de encontrarla podrías filtrar por el header que varíe antes de servir el contenido.

0voto

HoberMallow comentado

Gracias por responder. He buscado con el analizador de red de Firefox, Chrome y hasta con Wireshark pero no encontré la petición GET que hacer el JW Player. De todas maneras he notado que si tampoco funciona cuando pongo la condición para detectar si el referer está vació, entonces supongo que igual no iba a funcionar filtrando cabeceras.

Al parecer los dispositivos móviles no hacen un referer y por eso no se cumple ninguna restricción de las que he probado (?)

0voto

carlossevi comentado

No veo muy sólo utilizar este criterio, pero haciendo pruebas con el reproductor de ejemplo de http://www.jwplayer.com/ veo la siguiente diferencia en la petición del JW Player:

Accept: "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5"

1 Respuesta

1voto

Leonardo-Tadei Puntos227320

Buenas!

El filtro por llamador sí debería funcionar. Implementé esto como prueba:

index.php:

<!DOCTYPE html>
<html>
<head>
<title>Qui&eacute;n me llama?</title>
<meta name="generator" content="Bluefish 2.2.3" />
<meta name="author" content="leo" />
<meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8"/>
</head>
<body>
<h1>P&aacute;gina que llama a otra llamada <a href="otra.php">otra.php</a> en un iframe</h1>
<iframe src="otra.php" width="400px" height="300px" ></iframe>
</body>
</html>

y otra.php que es la que se fija quién la llama:

<?php
// Muestra todas las variables de entorno
//var_dump($_SERVER);

$referer = '';
if( isset($_SERVER["HTTP_REFERER"]) ) {
    $referer = $_SERVER["HTTP_REFERER"];
}

print("Llamado desde:<br />$referer");

$aceptar[] = "http://tadei.com.ar/llamador/";
$aceptar[] = "http://www.tadei.com.ar/llamador/";
$aceptar[] = "http://tadei.com.ar/llamador/index.php";
$aceptar[] = "http://www.tadei.com.ar/llamador/index.php";

if (  in_array($referer, $aceptar) ){
    print("<br /><br />Acepto esta petici&oacute;n");   
} else {
    print("<br /><br />Access denied (como en las pel&iacute;culas)");  
}

Loas subí como ejemplo en http://tadei.com.ar/llamador

Toda petición tiene un referer si es llamada por otra página, independientemente del dispositivo del que se la llame, ya que esa variable de entorno la carga el servidor en base a las peticiones que recibe. Probé esto desde una PC con Firefox, desde un tablet con Android y desde un smartphone con FirefoxOS.

Por esto mismo la página otra.php se ve tanto en el iframe como desde el enlace en el título.

Fijate que el llamador está filtrado desde varios valores posibles. Esto es por el funionamiento de los servidores web que si una página se llama index, el valor puede omitirse y el servidor la devuelve igual. También aparecen CNAMEs del dominio, como el típico www. Esa página tiene además como CNAME leonardo, que no está puesto entre las reglas de filtrado, por lo que si hacés la llamada desde http://leonardo.tadei.com.ar/llamador dirá que no es válida, aunque sea del mismo servidor... son cosas a tener en cuenta.

Tal vez haya que analizar un poco como JW Player hace la llamada, pero en este universo existe solamente GET y POST como mecanismo y será siempre el cliente el que la dispare.

Espero te sirvan los ejemplos.

Saludos!

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta