FileClass (subir y descargar archivos)

Iniciado por alexander1712, Octubre 10, 2013, 01:00:09 AM

Tema anterior - Siguiente tema

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

Como no es ninguna novedad, y los que siguen mis aportes en esta comunidad tan querida de underc0de saben, estoy centrando todo mi esfuerzo, mientras programo No tienes permitido ver los links. Registrarse o Entrar a mi cuenta en lograr una serie de "componentes" reusables, y que cumplan con dicho criterio, para lo cual estoy programando de manera que cada componente y parte de mi sistema pueda ser separado, y lo pueda usar en otro código fácilmente, de ésta forma me ahorro todo lo que más pueda escribir código, y así no tengo que escribir una y otra vez el mismo código, ya tengo la lista de clases a mi medida para poder usarlas. (supongo que también sabrán que soy muy reticente a la hora de usar código ageno y que para usar algún código si no es de la documentación oficial de php y que de todos modos reviso varias veces antes de incorporarlo, en códigos de otros autores lo paso por varios criterios de calidad antes de tomarlo como ejemplo para crear mi propio código, pasa que como siempre digo, prefiero hacer las cosas yo y conocer los defectos que tienen perfectamente a usar cosas de otros sin conocer dichos defectos, por eso hasta creé mi propio framework xD)
en fin, traigo una clase para manejar archivos, tiene dos modalidades de uso, para subir un archivo al server, y para obligar a descargar un archivo del server (lo que nos permite descargar archivos como .jpg o lo que fuese)...

para descargar archivos tienen que instanciar la clase pasando como parametros al constructor, la ubicacion del archivo que manejará el objeto, y el nombre del mismo, luego llamar a la función download_file pasando como parametro el nombre que verá la persona que esté descargando el archivo (obviamente con extension incluido)

y la segunda manera para subir archivos es crear el objeto sin pasar parámetros al constructor y llamara a la función upload_file (en la interface explica los parámetros).

como siempre solo incluyen la interfaz y ésta incluirá la clase por si sola, además requiere de mi Trait Property que lo pueden descargar de No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

bueno como me extendí demaciado el código php no entrará en la publicación sin que SMF me lo corte, asique dejo en una respuesta el código del archivo interface y el archivo de la clase principal.

Un Saludo! :)

Octubre 10, 2013, 01:00:53 AM #1 Ultima modificación: Marzo 27, 2014, 05:21:16 PM por Expermicid
FileClassInterface.php

Código: php
<?php 

// se requiere Trait Property

Interface IFileClass
{

/*
* @ $location = carpeta donde se guarda el archivo
* @ $name = nombre del archivo con extensión incluida
*/
public function __construct($location = null, $name = null);

/*
* @ $name = nombre que verá el usuario cuando se le descargue el archivo
*/
public function download_file($name);

/*
* @ $nombre_campo = nombre del campo file del formulario para cargar archivo
* @ $carpeta = carpeta donde se subirá el archivo
* @ $uniqid = agregar o no un id único para que no se repitan los archivos
* @ $namecrypt = encriptar nombre del archivo para imposibilitar descarga (se encrypta en md5)
* @ $extension_valida = arreglo con extensiones válidas
* @ $file_type = MIME/TYPE del archivo
* @ $kbmax = Máximos kb que se permiten en la subida (dejar en 0 si no se quiere poner limite)
* @ $dir = cuando se devuelva la ruta del archivo devolverla con la carpeta o sin la carpeta de su ubicación (osea solo el nombre o toda la ruta completa)
* ésta función devuelve falso si ubo algún error, o la dirección o nombre del archivo (según lo especificado) de lo que se subió.
*/
public function upload_file($nombre_campo, $carpeta, $uniqid = false, $namecrypt = false, $extension_valida = array('rar'), $file_type = null, $kbmax = 0, $dir = true);

// devuelve si un nombre de archivo tiene o no una extensión X.
public function validate_extension($filename, $extension);

public function get_name();
public function set_name($value);

public function get_peso();
public function set_peso($value);

public function get_type();
public function set_type($value);

public function get_location();
public function set_location($value);

public function get_error();
public function set_error($value);

public function get_error_msg();
public function set_error_msg($value);

}

include('FileClass.php');


FileClass.php

Código: php
<?php

Class FileClass implements IFileClass
{
use Property;

protected $kb = 1024;
protected $mb = 1024;
protected $gb = 1024;
private $name = null;
private $peso = null;
private $type = null;
private $location = null;

private $error = false;
private $error_msg = null;

public function __construct($location = null, $name = null)
{
try
{
if(!empty($location))
{
if(empty($name)) throw new exception('El nombre del fichero es requerido');
if(!file_exists($location.$name)) throw new exception('El fichero no existe');
$this->name = $name;
$this->location = $location;
$this->peso = filesize($location.$name);
}
} catch(Exception $e)
{
$this->error = true;
$this->error_msg = $e->getMessage();
}
}

public function download_file($name)
{
header("Content-type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"{$name}\"\n");
$fp=fopen($this->location.$this->name, "r");
fpassthru($fp);
die();
}

public function upload_file($nombre_campo, $carpeta, $uniqid = false, $namecrypt = false, $extension_valida = array('rar'), $file_type = null, $kbmax = 0, $dir = true)
{
try
{
$this->name = $_FILES[$nombre_campo]['name'];
$this->size = $_FILES[$nombre_campo]['size'];
$this->type = $_FILES[$nombre_campo]['type'];
$this->location = $carpeta;
if(empty($_FILES[$nombre_campo])) throw new exception('Archivo no seleccionado');
$id = null;
if($uniqid) $id = uniqid(microtime());
$this->name = $id.$this->name;
// verificamos si la extensión es válida
$valido = false;
for($i = 0; $i<count($extension_valida); $i++)
if($this->validate_extension($this->name, $extension_valida[$i]))
$valido = true;
if(!$valido) throw new exception('El archivo es de una extensión inválida');
// verificamos si el tipo de archivo es valido
if(!empty($file_type) && $this->type!=$file_type) throw new exception('El tipo de archivo es inválido');
// verificamos si tiene el tamaño correcto
if($kbmax>0 && $this->size > $kbmax*$this->kb) throw new exception('El archivo pesa demaciado');
// lo encryptamos si es preciso
if($namecrypt)
$this->name = 'file_'.md5($this->name).'.cab';
// verificamos que el directorio tenga los permisos
//------------ TODO
// ahora lo vamos a mover
if (!move_uploaded_file($_FILES[$nombre_campo]['tmp_name'], $this->location.$this->name)) throw new exception('No se puede mover fichero subido a carpeta especificada');
// retornamos el directorio donde se guardó
if($dir)
return $this->location.$this->name;
else
return $this->name;
} catch (Exception $e)
{
$this->error = true;
$this->error_msg = $e->getMessage();
return false;
}
}

public Function validate_extension($filename, $extension)
{
$ext_init = strlen($filename) - strlen($extension);
$last_ext = null;
for($i=$ext_init; $i<strlen($filename); $i++)
$last_ext.=$filename[$i];
if($last_ext == $extension)
return true;
else
return false;
}

public function get_name() { return $this->name; }
public function set_name($value) { $this->name = $value; }

public function get_peso() { return $this->peso; }
public function set_peso($value) { ; }

public function get_type() { return $this->type; }
public function set_type($value) { ; }

public function get_location() { return $this->location; }
public function set_location($value) { $this->location = $value; }

public function get_error() { return $this->error; }
public function set_error($value) { ; }

public function get_error_msg() { return $this->error_msg; }
public function set_error_msg($value) { ; }

}

Hola, eso es vulnerable, recuerda que en php == no es lo mismo que ===,

if($last_ext == $extension)

en este punto si extension es true o 1 entonces la validación se resuelve en true independiente que extensión tengas filtrado ya que en php == resuelve primero tipos integers por defecto y despues strings y esas cosas.

Además tampoco filtra mayusculas o minusculas, tampoco verifica que venga con un punto de la extensión, o sea que te podría subir un archivo test.php%00rar y dependiendo del servidor web debería dejarlo subir (creo que las verisones nuevas de apache ya no se puede).

Te recomiendo que le des una vuelta a este ejemplo:
$extension = pathinfo($archivo, PATHINFO_EXTENSION);

El $file_type da lo mismo porque se puede modificar desde una petición post porque eso lo da el navegador, modificable por cualquier persona con complementos como el tamper data o el modify headers de firefox o usando sockets con netcat.

Y cuidado con la función download_file() porque te permite bajar cualquier archivo incluyendo los no permitidos tales como ../../../../../../../etc/passwd o .htaccess o index.php o ../config.php etc.

Dale una vuelta mas, no está mala la clase pero aun le falta.

Saludos.

- No tienes permitido ver los links. Registrarse o Entrar a mi cuenta - No tienes permitido ver los links. Registrarse o Entrar a mi cuenta

Noviembre 04, 2013, 02:16:30 PM #3 Ultima modificación: Noviembre 04, 2013, 02:18:52 PM por Harkonnen
No tienes permitido ver los links. Registrarse o Entrar a mi cuenta
Hola, eso es vulnerable, recuerda que en php == no es lo mismo que ===,

if($last_ext == $extension)

en este punto si extension es true o 1 entonces la validación se resuelve en true independiente que extensión tengas filtrado ya que en php == resuelve primero tipos integers por defecto y despues strings y esas cosas.

Además tampoco filtra mayusculas o minusculas, tampoco verifica que venga con un punto de la extensión, o sea que te podría subir un archivo test.php%00rar y dependiendo del servidor web debería dejarlo subir (creo que las verisones nuevas de apache ya no se puede).

Te recomiendo que le des una vuelta a este ejemplo:
$extension = pathinfo($archivo, PATHINFO_EXTENSION);

El $file_type da lo mismo porque se puede modificar desde una petición post porque eso lo da el navegador, modificable por cualquier persona con complementos como el tamper data o el modify headers de firefox o usando sockets con netcat.

Y cuidado con la función download_file() porque te permite bajar cualquier archivo incluyendo los no permitidos tales como ../../../../../../../etc/passwd o .htaccess o index.php o ../config.php etc.

Dale una vuelta mas, no está mala la clase pero aun le falta.

Saludos.

claro, interesante lo que propones, muchisimas gracias, le pegaré las retocadas según la recomendación, como le decía el otro día a antrax, yo se programar razonable, pero no se mucho de seguridad, por eso siempre trato de juntarme con alguien que la tiene clara, dejando eso de lado, es cierto que el === es diferente a == ya que el === agrega una comparación de tipos, no obstante los tipos dinámicos de php son muy impredecibles, por lo que muchas veces da problemas serios con los tipos de datos, por eso trato de usar el === solo cuando realmente es necesario, como en el ejemplo que tu das, es verdaderamente útil, solo quiero aclarar eso porque encontré algunos errores a la hora de hacer comprobaciones de tipo, sobretodo cuando estaba experimentando con NCurses.

Saludos y desde ya muchas gracias por el comentario, realmente es muy útil, si tienes alguna otra sugerencia por favor hazmela saber, no siempre encuentro a personas que me digan en lo que me equivoco o lo que puedo mejorar :)

pd: si tienes alguna sugerencia de algún otro método más eficaz para comprobar la estructura de un archivo además de por su extención sería también de mucha utilidad.