Actualizar PHPost 1.3 a 1.5.1

Después de los hackeos que sufrió el script en su versión A1.3 se ha dado a conocer una actualización que además de corregir los problemas de seguridad también se le agregó una nueva característica.

Para los sitios nuevos y sin modificaciones pueden descargar el paquete desde el siguiente enlace http://www.phpost.net/downloads/, para los sitios con antigüedad y que han sido modificados demasiadas veces se ha creado esta guía, que muestra paso a paso que deben modificar para tener su sitio actualizado.

Antes de comenzar es muy importante que haga un respaldo los archivos que serán modificados en esta guía, por si surge algún error, usted pueda restaurar su web a como estaba antes, también es muy importante que haga un respaldo de su base de datos.

Nota: Al dar doble clic sobre los códigos estos se seleccionan automáticamente, así solo tienes que dar Ctrl+c para copiar todo.

Importante: Esta guía es para actualizaciones nuevas es decir que aún tienen la A1.3, para los que actualizaron de la A1.3 a la A1.5 entre los días 06 y 07 de diciembre de 2011 deben seguir esta otra guía para actualizar a la A1.5.1

Sesiones

Importante: Antes de continuar haga un respaldo de su base de datos, una vez que la tenga en su PC continúe con esta guía.

Primero vamos a agregar la nueva característica de PHPost que nos permite manejar con mayor seguridad las sesiones de los usuarios y además registrar cuando un usuario no registrado nos visita.

Paso 1: Tabla u_sessions

Para esta nueva característica necesitamos agregar una nueva tabla a nuestra base de datos, para hacerlo vamos a nuestro administrador de bases de datos (por lo general phpMyAdmin), elegimos nuestra base de datos y damos clic en la pestaña de SQL ingresamos la siguiente consulta y damos clic en contirnuar.

CREATE TABLE IF NOT EXISTS `u_sessions` (
  `session_id` varchar(32) NOT NULL,
  `session_user_id` int(11) unsigned NOT NULL DEFAULT '0',
  `session_ip` varchar(40) NOT NULL,
  `session_time` int(10) unsigned NOT NULL DEFAULT '0',
  `session_autologin` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`session_id`),
  KEY `session_user_id` (`session_user_id`),
  KEY `session_time` (`session_time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
            

Paso 2: Modificando el archivo c.user.php

Se ha creado una nueva clase que debe ser agregada al archivo c.user.php.

Comenzamos abriendo nuestro archivo localizado en /inc/class/c.user.php

Buscamos aproximadamente en la línea 26 el siguiente código:

            var $cookieName;
            

Lo reemplazamos por:

            var $session;
            

Vamos al final del archivo y justo antes del ?> agregamos el siguiente código:

// --------------------------------------------------------------------

class tsSession {
    
    var $ID                 = '';
    var $sess_expiration    = 7200; 
    var $sess_match_ip      = FALSE;
    var $sess_time_online   = 300; 
    var $cookie_prefix      = 'pp_';
    var $cookie_name        = '';
    var $cookie_path        = '/';
    var $cookie_domain      = '';
    var $userdata;
    var $ip_address;
    var $time_now;
    var $db;
    
    function __construct()
    {
        global $tsdb, $tsCore;
        // Database
        $this->db =& $tsdb;
        // Tiempo
        $this->time_now = time();
        // Obtener el dominio o subdominio para la cookie
        $host = parse_url($tsCore->settings['url']); 
        $host = str_replace('www.', '' , strtolower($host['host']));
        // Establecer variables
        $this->cookie_domain = ($host == 'localhost') ? '' : '.' . $host;
        $this->cookie_name = $this->cookie_prefix . substr(md5($host), 0, 6); 
        // IP
        $this->ip_address = $tsCore->getIP();
        // Cada que un usuario cambie de IP, requerir nueva session?
        $this->sess_match_ip = empty($tsCore->settings['c_allow_sess_ip']) ? FALSE : TRUE;
        // Cada cuanto actualizar la sesión? && Expires
        $this->sess_time_online = empty($tsCore->settings['c_last_active']) ? $this->sess_time_online : ($tsCore->settings['c_last_active'] * 60);
    }
    /**
     * Leer session activa
     * 
	 * @access	public
	 * @return	bool
	 */
     function read() 
     {
        $this->ID = $_COOKIE[$this->cookie_name . '_sid'];
        
        // Es un ID válido?
        if(!$this->ID || strlen($this->ID) != 32)
        {
            return FALSE;
        }
        
        // ** Obtener session desde la base de datos
        $query = $this->db->select("u_sessions","*","session_id = '{$this->ID}'");
        $session = $this->db->fetch_assoc($query);
        $this->db->free($query);
        
        // Existe en la DB?
        if(!isset($session['session_id']))
        {
            $this->destroy();
            return FALSE;
        }
        
        // Is the session current?
		if (($session['session_time'] + $this->sess_expiration) < $this->time_now AND empty($session['session_autologin']))
		{
			$this->destroy();
			return FALSE;
		}
        
        // Si cambió de IP creamos una nueva session
        if($this->sess_match_ip == TRUE && $session['session_ip'] != $this->ip_address)
        {
            $this->destroy();
            return FALSE;
        }
        
        // Listo guardamos y retornamos
        $this->userdata = $session;
        unset($session);
        
        return TRUE;
     }
	/**
	 * Create a new session
	 *
	 * @access	public
	 * @return	void
	 */
	function create()
	{
        // Generar ID de sesión
        $this->ID = $this->gen_session_id();
                
        // Guardar en la base de datos, session_user_id siemrpe será 0 aquí

        // si inicia sesión se "actualiza"
        $this->db->insert('u_sessions', 'session_id, session_user_id, session_ip, session_time', "'{$this->ID}', 0, '{$this->ip_address}', {$this->time_now}");
       
        // Establecemos la cookie
        $this->set_cookie('sid', $this->ID, $this->sess_expiration);
    }
	
	/**
	 * Update an existing session
	 *
	 * @access	public
	 * @return	void
	 */
	function update($user_id = 0, $autologin = FALSE, $force_update = FALSE)
	{
	   // Actualizar la sesión cada x tiempo, esto es configurado en el panel de Admin
       if(($this->userdata['session_time'] + $this->sess_time_online) >= $this->time_now AND $force_update == FALSE)
       {
            return;
       }
       
       // Datos para actualizar
       $this->userdata['session_user_id'] = empty($user_id) ? $this->userdata['session_user_id'] : $user_id;
       $this->userdata['session_ip'] = $this->ip_address;
       $this->userdata['session_time'] = $this->time_now;
       // Autologin requiere una comprovación doble
       $autologin = ($autologin == FALSE) ? 0 : 1;
       $this->userdata['session_autologin'] = empty($this->userdata['session_autologin']) ? $autologin : $this->userdata['session_autologin']; 
    
       // Actualizar en la DB
       $this->db->update("u_sessions","session_user_id = {$this->userdata['session_user_id']}, session_ip = '{$this->userdata['session_ip']}', session_time = {$this->userdata['session_time']}, session_autologin = {$this->userdata['session_autologin']}","session_id = '{$this->ID}'");
       
       // Limpiar sesiones
       $this->sess_gc();
       
       // Actualizar cookie
       if(!empty($this->userdata['session_autologin']))
       {
            // Si el usuario quiere recordar su sesión, se guardará por 1 año
            $expiration = 31500000;
       }
       else $expiration = $this->sess_expiration;
       //
       $this->set_cookie('sid', $this->ID, $expiration);
    }

	/**
	 * Destroy the current session
	 *
	 * @access	public
	 * @return	void
	 */
	function destroy()
	{
	   // Elminar de la DB
       $this->db->delete("u_sessions","session_id = '{$this->ID}'");
       // Reset a la cookie
       $this->set_cookie('sid', '', -31500000);
    }
    /**
     * Crear cookie
     * @access public
     * @param string
     * @param string
     * @param int
     */
	function set_cookie($name, $cookiedata, $cookietime)
	{
        $cookiename = rawurlencode($this->cookie_name . '_' . $name);
        $cookiedata = rawurlencode($cookiedata);
		// Establecer la cookie
        setcookie($cookiename, $cookiedata, ($this->time_now + $cookietime), '/', $this->cookie_domain);
	}
    /**
     * Generar un ID de sesión
     * 
     * @access public
     * @param void
     */
    function gen_session_id() 
    {
		$sessid = '';
		while (strlen($sessid) < 32)
		{
			$sessid .= mt_rand(0, mt_getrandmax());
		}
        
		// To make the session ID even more secure we'll combine it with the user's IP
		$sessid .= $this->ip_address;
        
        return md5(uniqid($sessid, TRUE));
    }
	/**
	 * Eliminar sesiones expiradas
	 *
	 * @access	public
	 * @return	void
	 */
	function sess_gc()
	{
        // Esto es para no eliminar con cada llamada a esta función
        // sólo si se cumple la siguiente sentencia se eliminan las sesiones
		if ((rand() % 100) < 30)
		{
            // Usuario sin actividad
    		$expire = $this->time_now - $this->sess_time_online;
            
            $this->db->delete("u_sessions","session_time < {$expire} AND session_autologin = 0");
        }
	}
} 
            

La clase anterior va a manejar todas las sesiones del usuario, esta clase es auxiliar a la clase tsUser por tanto también debemos modificarla, vamos a seguir editando algunas de las funciones en nuestro archivo c.user.php.

Buscamos y seleccionamos la siguiente función:

	function tsUser(){
		...
	}
            

Vamos a reemplazar toda la función y nos tiene que quedar así:

	function tsUser(){
		global $tsCore;
		/* CARGAR SESSION */
        $this->session = new tsSession();
        $this->setSession();
		// ACTUALIZAR PUNTOS POR DIA :D SOLO A REGISTRADOS
		if($this->is_member) $this->actualizarPuntos();
	}
            

Ahora buscamos la función:

	function setSession(){
	   ...
	}
            

Igual reemplazamos toda la función para quedar así:

	function setSession()
    {
        // Si no existe una sessión la creamos
        // si existe la actualizamos...
		if ( ! $this->session->read())
		{
			$this->session->create();
		}
		else
		{
            // Actualizamos sesión
			$this->session->update();
            // Cargamos información
            $this->loadUser();
		}
	}
            

Seguimos reemplazando funciones ahora buscamos:

            function loadUser($tsUserID, $session = false){
                ...
            }
            

reemplazamos por:

	function loadUser($login = FALSE)
    {
        // Cargar datos
        $sql = "SELECT u.*, s.* FROM u_sessions s, u_miembros u WHERE s.session_id = '{$this->session->ID}' AND u.user_id = s.session_user_id ";
        $query = $this->query($sql);
        $this->info = $this->fetch_assoc($query);
        $this->free($result);
        // Existe el usuario?
        if(!isset($this->info['user_id']))
        {
            return FALSE;
        }
        // PERMISOS SEGUN RANGO
        $query = $this->select("u_rangos","r_name, r_color, r_image, r_special, r_allows","rango_id = {$this->info['user_id']}","",1);
        $this->info['rango'] = $this->fetch_assoc($query);
        $this->free($query);
		/* ES MIEMBRO */
		$this->is_member = 1;
        $this->is_admod = ($this->info['user_rango'] <= 2) ? $this->info['user_rango'] : 0;
		// NOMBRE
		$this->nick = $this->info['user_name'];
		$this->uid = $this->info['user_id'];
        $this->is_banned = $this->info['user_baneado'];
		// ULTIMA ACCION 
		$this->update("u_miembros","user_lastactive = unix_timestamp()","user_id = {$this->uid}");
        // Si ha iniciado sesión cargamos estos datos.
        if($login)
        {
            // Last login
            $this->update("u_miembros","user_lastlogin = {$this->session->time_now}","user_id = {$this->uid}");
            /* REGISTAR IP */
            $this->update("u_miembros","user_last_ip = '{$this->session->ip_address}'","user_id = {$this->uid}");
        }
        // Borrar variable session
        unset($this->session);
        
	}
            

Buscamos y seleccionamos la función:

            function loginUser($username, $password, $remember = false, $redirectTo = NULL){
                ...
            }
            

reemplazamos la función por:

	function loginUser($username, $password, $remember = FALSE, $redirectTo = NULL){
		global $tsCore;
		
		/* ARMAR VARIABLES */
		$username = strtolower($username);	// ARMAR VARIABLES
        $pp_password = md5(md5($password) . $username);
		/* CONSULTA */
		$query = $this->select("u_miembros","user_id, user_password, user_pwtype, user_activo, user_baneado","LOWER(user_name) = '{$username}'","","1");
        if(!$query) $query = $this->select("u_miembros","user_id, user_password, user_activo, user_baneado","LOWER(user_name) = '{$username}'","","1");
        //
        $data = $this->fetch_assoc($query);
        $this->free($query);
        if(empty($data)) return '0: El usuario no existe.';
        //
       	if($data['user_pwtype']){
       	    $other_passwords = array();
       	    $other_passwords[] = sha1($username . $password); // SMF 1.1.x, SMF 2.0.x
            $other_passwords[] = md5($password); // Zinfinal
            //
            if(in_array($data['user_password'], $other_passwords)){
                // UPDATE
                $this->update("u_miembros","user_password = '{$pp_password}', user_pwtype = 0","user_id = {$data['user_id']}");
                //
                $data['user_password'] = $pp_password;
            }
       	}
        // CHECAMOS
        if($data['user_password'] != $pp_password){
			return '0: Tu contrase&ntilde;a es incorrecta.';
		} else {
            if($data['user_activo'] == 1){
                // Actualizamos la session
                $this->session->update($data['user_id'], $remember, TRUE);
                // Cargamos la información del usuario
                $this->loadUser(true);
				/* REDERIGIR */
				if($redirectTo != NULL) $tsCore->redirectTo($redirectTo);	// REDIRIGIR
				else return TRUE;
			} else return '0: Debes activar tu cuenta';
		}
	}
            

Por último buscamos la función:

            function logoutUser($user_id, $redirectTo = '/'){
                ...
            }
            

y reemplazamos por:

	function logoutUser($user_id, $redirectTo = '/'){
		global $tsCore;
		/* BORRAR SESSION */
        $this->session = new tsSession();
        $this->session->read();
        $this->session->destroy();
		/* LIMPIAR VARIABLES */
		$this->info = '';
		$this->is_member = 0;
        # UPDATE
        $last_active = (time() - (($tsCore->settings['c_last_active'] * 60) * 3));
        $this->update("u_miembros","user_lastactive = {$last_active}","user_id = {$user_id}");
        $this->update("w_stats","stats_online = stats_online - 1","stats_no = 1");
		/* REDERIGIR */
		if($redirectTo != NULL) $tsCore->redirectTo($redirectTo);	// REDIRIGIR
		else return true;
	}
            

Paso 3: Agregando nueva configuración

Ahora vamos a crear la nueva configuración para esta característica.

Primero hay que agregar un nuevo campo a la tabla w_configuracion, Abramos nuestro administrador de bases de datos como lo hicimos en el Paso 1: Tabla u_sessions, seleccionamos la pestaña SQL, agregamos y ejecutamos el siguiente código:

                ALTER TABLE `w_configuracion` ADD `c_allow_sess_ip` INT( 1 ) NOT NULL DEFAULT '1' AFTER `c_last_active`
            

Ahora abrimos el archivo m.admin_configs.tpl, ubicado en /Temas/default/templates/admin_mods/m.admin_configs.tpl.

Buscamos el siguiente código aproximadamente en la línea 51

            <dl>
                <dt><label for="ai_active">Usuario online:</label><br /><span>Tiempo que debe trascurrir para considerar que un usuario est&aacute; en línea.</span></dt>
                <dd><input type="text" id="ai_active" name="active" style="width:10%" maxlength="2" value="{$tsConfig.c_last_active}" /> min.</dd>

            </dl>
            

agregamos deabajo el siguiente código:

            <dl>
                <dt><label for="ai_reg_active">Login por IP:</label><br /><span>Por seguridad cada que un usuario cambie de IP se le pedir&aacute; loguearse nuevamente.</span></dt>
                <dd>

                    <label><input name="sess_ip" type="radio" id="ai_sess_ip" value="1" {if $tsConfig.c_allow_sess_ip == 1}checked="checked"{/if} class="radio"/>S&iacute;</label>

                    <label><input name="sess_ip" type="radio" id="ai_sess_ip" value="0" {if $tsConfig.c_allow_sess_ip != 1}checked="checked"{/if} class="radio"/>No</label>

                </dd>
            </dl>
            

Ahora requerimos abrir el archivo c.admin.php ubicado en /inc/class/c.admin.php

Buscamos la siguiente línea:

            'active' => $tsCore->setSecure($_POST['active']),
            
agregamos debajo:
            'sess_ip' => empty($_POST['sess_ip']) ? 0 : 1,
            

Buscamos:

            c_last_active = {$c['active']},
            

y reemplazamos por:

            c_last_active = {$c['active']}, c_allow_sess_ip = {$c['sess_ip']},
            

Y eso es todo ya podemos configurar la nueva característica de PHPost A1.5 las sesiones.

Estadísticas y seguridad

La característica mas notable de las sesiones es que ahora podrás saber cuántos usuarios no registrados están visitando tu web, para lograr esto hay q modificar el archivo c.tops.php, ubicado en /inc/class/c.admin.php.

Buscamos la siguiente línea:

            $query = $tsdb->select("u_miembros","user_id","user_lastactive > $is_online","","");
            

la reemplazamos por:

            $query = $tsdb->select("u_sessions","session_user_id","session_time > $is_online","","");
            

Ahora por seguridad vamos a corregir el bug con el cual últimamente estaban hackeando los sitios creados con PHPost, vamos a abrir el archivo ajax.perfil.php, ubicado en /inc/php/ajax/ajax.perfil.php.

Buscamos:

            $user_id = $tsCore->setSecure($_POST['pid']);
            

reemplazamos por:

            $user_id = (int) $tsCore->setSecure($_POST['pid']);
            

Ahora abre el archivo ajax.feed.php, ubicado en /inc/php/ajax/ajax.feed.php.

Reemplaza todo su contenido por el del siguiente archivo ajax.feed.php

Administrar Themes

Se ha creado una guía para los creadores de themes, con la cual ahora deberán integrar un archivo de instalación, de esta manera será mas fácil administrar los themes que tangamos instalados, para activar esta característica hay que seguir los siguientes pasos.

Abrir el archivo c.admin.php, ubicado en /inc/class/c.admin.php.

Buscar la siguiente función:

            function changeTema(){
                ...
            }
            

reemplazar toda la función por:

	function changeTema(){
		global $tsdb, $smarty;
		//
		$tema = $this->getTema();
		//
		if(!empty($tema['tid'])) {
            $tsdb->update("w_configuracion","tema_id = {$tema['tid']}","tscript_id = 1");
            $d = $smarty->compile_dir;
             $h = opendir($d);
            
             while (($o = readdir($h)) !== FALSE)
            
             {
            
              if (($o != ".") and ($o != ".."))
            
              {
            
               unlink($d.DIRECTORY_SEPARATOR.$o);
            
              }
            
             }
            
             closedir($h);
          return true;
        }
		else return false;
	}
            

Ahora abrimos el archivo c.core.php ubicado en /inc/class/c.core.php

Buscar la siguiente función:

            function getTema(){
                ...
            }
            

reemplazar toda la función por:

	function getTema(){
		global $tsdb;
		//
		$query = $tsdb->select("w_temas","*","tid = {$this->settings['tema_id']}","",1);
		//
		$data = $tsdb->fetch_assoc($query);
        $data['t_url'] = $this->settings['url'] . '/Temas/' . $data['t_path'];
		$tsdb->free($query);
		//
		return $data;
	}
            

Vamos a activar la sección Temas y apariencia de nuestro Panel de Administración, para esto necesitamos abrir el archivo m.admin_temas.tpl, ubicado en /Temas/default/templates/admin_mods/m.admin_temas.tpl.

Seleccionamos todo el contenido de m.admin_temas.tpl y lo reemplazamos por el del siguiente archivo.

upgrade-phpost-1.5-admin_temas.txt

Eso es todo ahora, podremos administrar los themes que tengamos en nuestro servidor.

Característica Extra (MI)

Opcionalmente puedes activar algo que el script A1.5 trae por defecto, colocar los posts como página de inicio y el portal como el "MI" de Taringa.

Abrimos el archivo index.php, ubicado en /index.php

Buscamos el siguiente código:

                $_GET['do'] != 'posts'
            

lo reemplazamos por:

                $_GET['do'] == 'portal'
            

Abrimos la plantilla head_menu.tpl, ubicado en /Temas/default/templates/sections/head_menu.tpl.

Buscamos el siguiente código:

            <a title="Ir a Inicio" onclick="menu('home', this.href); return false;" href="{$tsConfig.url}/"><span>&nbsp;</span></a>
            

lo reemplazamos por:

            <a title="Ir a Inicio" onclick="menu('home', this.href); return false;" href="{$tsConfig.url}/mi/"><span>&nbsp;</span></a>
            

Para finalizar abrimos el archivo .htaccess, ubicado en /.htaccess.

Buscamos:

            RewriteRule ^posts/$ index.php?do=posts [QSA,L]
            

agregar debajo la siguiente línea:

            RewriteRule ^mi/$ index.php?do=portal [QSA,L]
            

Anti flood en Fotos

Esto va a corregir el problema de SPAM que se tienen en la sección Fotos, para esto necesitamos abrir el archivo c.fotos.php, ubicado en /inc/class/c.fotos.php.

Buscamos lo siguiente:

            // UPLOAD
            require('c.upload.php');
            

y agregamos arriba el siguiente código:

            // ANTI FLOOD
            $tsCore->antiFlood(true, 'foto');
            

Conclusión

Esas son las actualizaciones más importantes de la versión A1.5.1, si descargan el script completo traerá un par de mejoras con respecto al "install" pero las demás modificaciones ya se expusieron en esta guía.

La actualización de PHPost Alpha 1.5.1 se hizo para resolver el bug conocido como el “md5” con el cual estaban hackeando los sitios, si surgiera alguna otra vulnerabilidad, que esperemos no suceda, se actualizará nuevamente el script.

↑ Ir arriba