miércoles, 31 de julio de 2013

PHP orientado a objetos - Introducción

Definición de clase

Una clase se podría definir como una plantilla de código para generar objetos.

En PHP una clase la definiremos con la palabra clave class seguida del nombre que queramos que tenga dicha clase. El nombre puede ser cualquier combinación de letras, guiones 'bajos' y números. Pero no puede empezar por un número. Ni el nombre puede ser igual al de alguna palabra reservada de PHP. Finalmente una par de llaves limitarán el contenido de la clase.

 class Producto{  
   //contenido de la clase  
 }  

Un objeto se compone  de datos que han sido estructurados de acuerdo con la plantilla definida en una clase. Por lo tanto un objeto es una instancia (representación) o un tipo definido por una clase.
Entonces vamos a ver como generar una instancia de una clase. Para ello utilizaremos el operador new de la siguiente forma:

 p1 = new Producto();  
 p2 = new Producto();  
 p3 = new Producto();  

Como vemos podemos generar tantas instancias (objetos) como queramos de un mismo tipo (clase).

lunes, 29 de julio de 2013

Patrón PHP POST/Redirect/GET

Todas las aplicaciones web tienen dos objetivos básicos:
- Poder obtener información enviada por el usuario desde un navegador.
- Mostrar resultados obtenidos por el procesado de la información anterior.
Esto se consigue mediante los métodos HTTP POST y GET.
En anteriores tutoriales  ya describimos las peculiaridades de utilizar cada método (POST/GET) para el traslado de información en la comunicación cliente-servidor. Y concluimos que para el envío de información importante desde el navegador al servidor era necesario utilizar el método POST. Debido a que los datos no viajaban en la URL, sino que lo hacían junto con las cabeceras.
Además también comentamos que los datos enviados mediante POST son almacenados en la memoria caché de los navegadores. Y aunque a priori este hecho no pueda parecer problemático, vamos a presentar un problema por el cual se puede ocurrir un doble envío de datos.

Para evitar tener que solicitar varias veces los mismos recursos (datos o imágenes) el navegador almacena en el disco duro dichos recursos. De esta forma, si alguien entra  en una página web, el navegador intentará cargar recursos que tenga almacenados. Ya que si no es la primera vez que se visita la página, el navegador habrá guardado recursos en caché.

lunes, 22 de julio de 2013

Seguridad PHP - Cross Site Request Forgery (CSRF)

Los ataques XSS se centraban en explotar la confianza de un usuario en un sitio web, ya que confiaba en la seguridad del servidor para que los atacantes no pudieran apoderarse de información de los usuarios, mediante la inserción de código 'malicioso'.
Pues existen otro tipo de ataques cuya idea es parecida. Este tipo de ataques ocurren cuando un sitio web permite que un usuario autenticado realice acciones sin verificar si realmente es él quien las está realizando. El ataque CSRF puede hacer que el navegador de la víctima envíe una petición HTTP 'fraudulenta'.  Y la aplicación (servidor) vulnerable piensa que es una petición de la víctima. Esto permitiría al atacante forzar al navegador de la víctima a hacer peticiones que la aplicación vulnerable pensaría que son peticiones legítimas.

sábado, 20 de julio de 2013

Seguridad PHP - Robo de sesiones

Introducción

Antes de nada conviene recordar el funcionamiento de las sesiones y las cookies de sesión explicados en anteriores tutoriales.
PHP tiene soporte nativo para trabajar con sesiones. Cada sesión viene identificada  un id aleatorio (32 caracters hexadecimales), que se crea la inicializar dicha sesión con la función session_start(), y que se almacena en una variable llamada PHPSESSID. Este id es esencial ya que cuando creamos una sesión, se crea un array superglobal que almacenará la información de la sesión. Y éste se almacena en el servidor, en un archivo temporal cuyo nombre está formado por 'sess' + dicho ID de sesión (depende de la configuración de php.ini).
Ahora necesitamos que el cliente tenga una forma de hacer peticiones al servidor y que
este sepa que se está comunicando en un contexto de sesión determinado. O dicho de otra forma el servidor tiene que saber que las peticiones del cliente pertenecen a la sesión.

Esquema intercambio cliente-servidor de cookie de sesión

En una comunicación bidireccional hay dos posibilidades para la comunicación de identificador de sesión:

viernes, 19 de julio de 2013

Seguridad PHP - Cross Site Scripting (XSS)

Mientras que un ataque 'SQL injection' pretendía insertar código 'malicioso' en las consultas SQL de una base de datos, XSS pretende que la aplicación web ejecute código JavaScript o similar. Es una vulnerabilidad que aprovecha la falta de mecanismos de filtrado y validación en los campos de entrada. Permitiendo así el envío de scripts completos (como Visual Basic Scripts o JavaScripts) con secuencias de comandos maliciosos que podrían impactar directamente en el sitio web o en el equipo de un usuario. Este código malicioso intenta aprovecharse de la confianza de un usuario en un sitio web, para engañarlo en la realización de alguna acción o forzarlo a enviar algún tipo de información a otro sitio que no es de confianza.

Por lo tanto, la diferencia en la ideología del ataque es que con 'SQL injection' se pretende aprovecharse de la poca seguridad del servidor para obtener acceso o alterar recursos del mismo mediante inserción de código SQL en el mismo. Mientras que con XSS se pretende introducir código JavaScript con el  propósito de que dicho código  al ser ejecutado por un cliente confiado lleve a cabo una acción maliciosa. Por ejemplo, un atacante colocar un enlace en un foro que al ser pinchado revele la información de identificación del usuario y la envíe a una dirección.

jueves, 18 de julio de 2013

Seguridad PHP - SQL injection

Las aplicaciones web permiten que a través de datos introducidos por los usuarios se realicen consultas y operaciones sobre las bases de datos que integran.
Al intento de  insertar o inyectar código dentro del código SQL para alterar el funcionamiento normal y hacer que se ejecute el código 'malicioso' dentro del sistema se le llama 'inyección de sql' o 'sql injection' (en ingles).
La solución para no permitir la inyección de este código es el correcto tratamiento de los datos que llegan a la aplicación de manera externa. Ya que como hemos dicho anteriormente, nunca hay que fiarse de lo que va hará o no un usuario.

Ejemplos de ataques

A) Acceso de administrador

- La siguiente consulta podría ser una consulta real para averiguar la existencia del usuario introducido en el formulario:

 $usuario = $_POST['usuario'];  
 $pass = $_POST['password'];  
 SELECT * FROM usuarios WHERE usuario = '$usuario' AND password = '$pass';  

Supongamos que dichas variables provienen de los inputs usuario y password del formulario de login y que existe un usuario llamado admin. Que pasaría si el usuario introdujese la cadena admin como login y  'or '1'='1 en la contraseña. Pues que la consulta se evaluaría de la siguiente manera:

 SELECT * FROM usuarios WHERE usuario = 'admin' AND password = '' OR '1'='1';  

Pues sí, podríamos ganarnos el pase como admin!!!. Evidentemente pueden haber variantes en el ataque ya que a parte de que admin puede no existir, puede que en la consulta las variables no estén entre comillas.

Seguridad PHP - Validación y filtrado 2

FILTER_CALLBACK


Como habréis visto en el anterior tutorial la cantidad de filtros es bastante decente pero evidentemente no cubre todas las necesidades que puede tener un desarrollador a la hora de filtrara datos. Por lo tanto tenemos que recurrir a funciones creadas por nosotros o a un tipo de filtro especial con llamada a funciones.

El tipo de filtro FILTER_CALLBACK nos permite hacer lo que nos indica su nombre. Llamar a una función nuestra para utilizarla como filtro o como función de filtrado. Y la única opción que permite es el nombre de la función que queremos aplicar.

Vamos a ver un sencillo ejemplo:

 function convertirEspacio($cadena) {  
      return str_replace(" ", "_", $cadena);  
 }  
 $string = "Esto es un ejemplo";  
 echo filter_var($string, FILTER_CALLBACK, array("options"=>"convertirEspacio"));  

Seguridad PHP - Validación y filtrado 1

La primera regla cuando tratamos con datos, ya sean provenientes de peticiones POST o GET, es que nunca nos podemos fiar de lo que pueda o no haber enviado el usuario. Por lo tanto debemos que validar cualquier dato ya que solo podemos permitir la entrada esperada. Y por supuesto no confiar únicamente en la posible validación en el lado cliente del sistema.

PHP nos presenta un conjunto de funciones que nos permitirán la validación y saneamiento de datos de una forma sencilla. La funcion filter_var (PHP >=5.2) nos permite filtrar una variable según el filtro especificado.

La sintaxis de la función es simple:

 mixed filter_var ( mixed $AFiltrar , int $tipo_filtro , mixed $opciones_filtro [opcional] );  

La salida será los datos filtrados o false en caso de fallo. Importante es el último parámetro  en el que podemos añadir opciones, en formato array asociativo o banderas. Existen diferentes formas de especificar tanto opciones como banderas:

Seguridad PHP - register_globals

El primer paso antes de validar cualquier datos de entrada, es comprobar que la directiva register_globals este desactivada. Desde PHP 5.3 fue declarada obsoleta y a partir de la versión 5.4 fue eliminada. Pero hay que tener cuidado si se usa una versión de PHP anterior. Por lo que  podemos usar la función phpinfo() para que nos diga si esta a activada o no.

El tener activa esta directiva ocasiona problemas graves de seguridad. Cuando register_globals está definida a “On” dentro del php.ini, se permitiría que un usuario cualquiera pudiera inicializar una variable remotamente. Y esto es un peligro en combinación con  de la naturaleza de PHP que no obliga a inicializar las variables.

Seguridad PHP - Autenticación HTTP vs PHP (formularios)

Aunque puedan haber ventajas con el uso de autenticación HTTP, sus desventajas hacen que en la mayoría de los caso se opte por una identificación basada en formularios. En vez de confiar en la identificación a nivel de protocolo. Aquí tenemos algunos inconvenientes:

- La nula posibilidad de 'customizar' que proporciona las ventanas emergentes de identificación HTTP. Las ventanas donde se introducen los datos en este tipo de identificación no permiten modificar su estilo. Lo que hace que tengan un estilo completamente diferente al del resto de la aplicación. Y eso quita atractivo a la aplicación/página web.

- En la autenticación HTTP la información de identificación es recordada hasta que se cierra el navegador. Si no se cierra el navegador seguirá recordando tus datos por lo que no los pedirá. Ya que no proporciona un método sencillo para que el usuario finalice la sesión. Por lo que hay que utilizar algún que otro 'truco'.
La consecuencia de esto es que si otra persona intenta entrar en el sitio web podrá entrar haciéndose pasar por ti, ya que no se le solicitaría ningún tipo de credenciales.

miércoles, 17 de julio de 2013

Seguridad PHP - Autenticación HTTP digest

Si la autenticación HTTP básica anterior no se combina con HTTPS, estamos trasmitiendo la información codificada en base-64. Pero no encriptada o usando algoritmos 'hash'. Lo que es muy fácil de decodificar y es prácticamente como si la enviásemos en texto plano.
Por eso el mecanismo que se presenta a continuación, mejora al anterior, transmitiendo la contraseña como un valor resultante de un algoritmo 'hash'. Lo que lo hace algo más seguro. O por lo menos era más seguro hace unos años. Ya que el algoritmo usado es MD5.  Y es bien sabido, que este algoritmo es vulnerable (por fuerza bruta) desde hace tiempo.
El propósito del esquema de autenticación HTTP digest es permitir que los usuarios prueben que ellos conocen una contraseña pero sin revelar dicha contraseña. Al contrarío que la identificación HTTP básica, en esta se crea un hash de diferentes datos usando la contraseña. El hash implica la creación de una cadena basada en datos que tanto el usuario (y por lo tanto el cliente) y el servidor conocen. Pero en definitiva, se envía el hash y no la contraseña. Lo que en caso que alguien consiga interceptar la conexión y suplantar al usuario, no disponga de la contraseña. Que normalmente es requerida para cambios de configuraciones en lo sitios web.

Seguridad PHP - Autenticación HTTP básica

La autenticación básica HTTP es un sencillo mecanismo con el que un servidor puede solicitar información de autenticación (un ID de usuario y contraseña) de un cliente. El cliente pasa la información de autenticación al servidor en una cabecera de autorización. Dicha información se encuentra codificada en base-64. Dada su poca seguridad, es útil para sistemas con un número muy limitado de usuarios y donde la protección no es muy importante. Por ejemplo para sistemas donde solo se quiere evitar usuarios anónimos para acceder al algún recurso.

Proceso:

1. El navegador (cliente) iniciará la comunicación realizando la petición al servidor. Y enviará las siguientes cabeceras (similar) al servidor:

 GET /download/report.doc HTTP/1.1   
 Accept: application/msword, */*   
 Accept-Language: en-ES  
 Accept-Encoding: gzip, deflate   
 User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)   
 Host: ejemplo.com  
 Connection: Keep-Alive  

martes, 16 de julio de 2013

PDO - Parte 2

Otras funciones útiles de PDO 

Existen tres funciones importantes para obtener información de la consulta ejecutada sin tener que recorrer los resultados:

A) Obtener el id de la última fila o secuencia insertada en la base de datos.

 public string PDO::lastInsertId ([ string $name = NULL ] )  

El parámetro $name opcional, sirve para indicar el nombre de columna. Como PDO depende de los drivers para cada gestor de base de datos soportado, en alguno de estos quizás sea necesario especificar el nombre de columna que almacena los identificadores. Como ocurre en PostgreSQL.

 $conn = new PDO('mysql:dbname=test;host=127.0.0.1', 'user', 'password');  
 $stmt = $conn->prepare('INSERT INTO test (name) VALUES (:name)');  
 $stmt->execute([':name' => 'foo']);  
 var_dump($conn->lastInsertId());  

Recuerda que la función es de la clase PDO y no de PDOStatement.

PDO - Parte 1

La extensión PDO nos facilitará mucho la vida a la hora de trabajar con bases de datos, ya que nos permite abstraernos del tipo de gestor de base de datos que estemos utilizando. Esto quiere decir que sin el uso de PDP, si por ejemplo decides migrar tu aplicación de MySQL a PostgreSQL tendrías que cambiar todos los métodos del conector de MySQL por los métodos de PostgreSQL. Con PDO bastaría con que se cambie únicamente la creación del objeto PDO, donde se especifica el tipo de base de datos a conectar, y el resto de la aplicación seguiría funcionando igual. Por lo esta abstracción del acceso de datos convierte a esta extensión en un elemento muy importante para la portabilidad de la aplicación/web.
PDO además te permitirá usar consultas preparadas. Y como vimos en el caso de la extensión MySQLi nos proporciona una herramienta muy importante en seguridad y rendimiento.
- Se encargan de 'desinfectar' los datos automáticamente.
- La separación de lógica y datos hace que la consulta sea analizada/preparada una única vez y se pueda ejecutar múltiples veces con los mismos datos o similares. Evitando cada vez el ciclo de análisis, compilación y optimización que ocurriría en cada consulta no preparada.
- Al igual que Mysqli, proporciona el uso de transacciones.

miércoles, 10 de julio de 2013

Cookies en PHP

Básicamente una cookie es un fichero de texto almacenado en el navegador de un cliente por orden de un sitio web. En estos ficheros, de tamaño máx. 4 KB se almacena información útil para el servidor como podría ser información del último acceso o información para la personalización de la web de un usuario concreto. Una cookie solo puede ser leída por el sitio web (dominio) que la creó.
Hay un limite de cookies que el navegador puede almacenar:
- 20 cookies para un servidor o dominio
- 300 cookies en total.
Ya que estas se almacenan en el disco duro del usuario, hay que tener cuidado con la posibilidad de que dicho usuario desactive su uso. En dicho caso no se almacenarán. Por lo que hay que tener previsto otro tipo de almacenamiento si la persistencia de información en cookies es indispensable para el sitio web.

Una cookie se envía junto a las demás cabeceras HTTP. Por lo tanto se exige lo mismo que exigíamos cuando queríamos enviar dichas cabeceras. Que no haya ninguna salida (elemento html, espacio en blanco o salida del script) anterior al envío. http://programandolo.blogspot.com.es/2013/07/redireccionamiento-php.html

domingo, 7 de julio de 2013

Sesiones en PHP

PHP en cada petición ejecuta el script correspondiente de principio a final. Y hay ocasiones en que sin necesitar la persistencia de datos que ofrece una base de datos, necesitamos almacenar de alguna forma ciertos datos mientras el usuario este utilizando la aplicación/web.
Y al igual que los arrays superglobales asociativos $_GET  y  $_POST existe otro  de especial importancia para dicho almacenamiento de datos: $_SESSION.  Cada vez que un usuario entre en el sitio web, podremos iniciar una sesión con un identificador exclusivo para dicho usuario y se podrán almacenar datos por medio del array $_SESSION hasta que decidamos finalizar una sesión. Dicho ciclo de inicio y fin suele coincidir con login y desconexión del sistema por parte de un usuario.
Internamente, el servidor, almacena los datos del array superglobal de la sesión en un archivo temporal cuyo nombre está formado por 'sess' + dicho ID de sesión (depende de la configuración de php.ini).

Uso de sesiones

1. Inicializando

 session_start();  

Con esta función no solo iniciaremos una nueva sesión particular en el servidor, sino que reanudaremos una posible sesión existente. Ya que al haber iniciado una sesión nueva, el identificador único que se crea es pasado por medio de una cookie al cliente. De esta manera el servidor sabrá si existe una sesión que tiene que recuperar o no. Dicha cookie por defecto se llamará PHPSESSID aunque podremos modificar el nombre en el el archivo php.ini (etc/php5/apach2/php.ini).

miércoles, 3 de julio de 2013

Redireccionamiento en PHP

En PHP existe una función llamada header() que se encarga de especificar una cabecera HTTP.
Dicha función debe de ser llamada antes de mostrar nada por pantalla. De otra manera se producirá un error.
Cada vez que enviamos una salida desde el servidor al cliente (o viceversa) dicha información será enviada junto a un conjunto de cabeceras HTTP. PHP enviará cabeceras aunque no las especifiquemos. Estas cabeceras serán por defecto si no se han sobreecrito con header(). Por lo tanto si existe alguna salida anterior a la llamada de header(), ya se habrá procesado el envió de cabeceras. Y se producirá el error: "headers already sent".

 void header ( string $string [, bool $replace = true [, int $http_response_code ]] )  

- El primer parámetro es la cabecera que indicará la acción a realizar. Las tres cabeceras más habituales, en header() son:
  • "Location ...". Es el uso más típico de header(). Con  este encabezado redirigimos a la dirección especificada y enviará un código 302 (redirect) de estado. Por lo tanto enviará las cabeceras especificas hasta el momento y procederá a realizar una redirección. Podemos introducir una ruta relativa al servidor o una absoluta. 
 header("Content-Type: application/json;charset=utf-8");    
 header("Location: info.php");  
  • "HTTP/...". Especificaremos una cabecera que especificará un código de respuesta HTTP de la respuesta al navegador. 
 header("HTTP/1.1 200 OK");   
  • "Content-Type ..." Con esta cabecera especificaremos el formato de la salida que mostrará el navegador del cliente.
  header("Content-Type: application/json;charset=utf-8");   

martes, 2 de julio de 2013

Formularios y PHP

Cuando un usuario rellena un formulario HTML y presiona el botón (input) de tipo 'submit' toda la información que este haya introducido en los distintos controles será enviado a la dirección (script) y mediante el método indicado.
Ciclo de envío de formulario a un servidor PHP

El método utilizado para el envío de los datos al servidor, indicará como viajan los datos hacia él.

A) GET

- Todos los nombres de los campos y valores viajan por la URL (query strings).
- Como son mostrados por la URL el navegador los almacena en su historial. Lo que no es muy conveniente para el caso de información sensible y más si ese ordenador puede ser usado por más personas. Además que el usuario despistado puede compartir datos al compartir la URL.
- Debido a esta hay una restricción de longitud de los datos. La longitud máxima de la URL es de 2048 caracteres.
- Los datos enviados al servidor, al viajar por la URL, pueden quedar almacenados en los ficheros de 'logs' del mismo.
- Solo se permite enviar datos ASCII.
- Resultados de operaciones GET pueden ser cacheadas. Lo que quiere decir que múltiples llamadas a un servicio pueden resultar una única petición.
- Tipo de codificación (enctype): application/x-www-form-urlencoded. Es el tipo por defecto, además del único, por lo que no es necesario indicarlo.

lunes, 1 de julio de 2013

Extensión MySQLi - Parte 4: Transacciones

Las transacciones con la extensión MySqli son un mecanismo que nos asegura que todas y cada de las consultas que las forman van a ser ejecutadas. En el caso de que falle alguna declaración, la transacción no se llevará a cabo.

Pero tenemos que tener cuidado con el tipo de tabla o tablas sobre las que vamos a realizar la transacción. Como sabemos los tipos más comunes de motores de almacenamiento de tablas en MySQL son MyISAM y InnoDB. Siendo el primero el que se utiliza por defecto si no se dice nada y que excluye la seguridad proporcionada por la integridad referencial. Sin embargo con este tipo no se permiten transacciones. Así que transacciones solo se pueden llevar a cabo sobre tablas InnoDB.

Fases de las transacciones

1. Iniciar transacción - Autocommit

Por defecto, MySQL se ejecuta en modo autocommit. Tener activo el modo de ejecución automática (autocommit) significa que cada consulta se tratará como una transacción individual.  Lo que  conlleva a  que tan pronto como se ejecuta una sentencia se modifica una tabla de MySQL. Haciendo imposible la vuelta atrás (modificación permanente). Pero gracias a InnoDB y a las transacción, las consultas se puede revertir (roll back). Y para ello tenemos que desactivar el citado modo de ejecución automática. Lo que iniciará la transacción.