SQL Injection, PHP y Bases de Datos

Written on May 22nd, 2008
.

SQL Injection, la temida dirían algunos y la verdad es que si no tienes cuidado al programar suelen ser verdaderos quebraderos de cabeza, que ciertamente despues de retomar código es un tanto dificil de depurar si no sabes lo que ha sucedido.
Aquí voy a relatar modos de tratar ataques SQL Injection, que harán nuestras apps más seguras y a nosotros estár más tranquilos.
Las SQL Injections son consultas sql que son generadas por scripts/programas que concatenan entradas del usuario con consultas a bases de datos, y ahí es donde está el problema ya que no toda la entrada que proporciona el usuario es segura.
Por ejemplo consideremos la siguiente instrucción:

SELECT * FROM users WHERE user = 'Pepito'

Si quieres en este momento recoger una fila de la base de datos con información del usuario insertada debes reemplazar “Pepito” con la cadena proporcionada por el usuario.
En PHP se vé tal que así:

SELECT * FROM users WHERE user = '$_GET[nombreUsuario]'

El problema es simple: si el usuario inserta una cadena formateada especialmente como el nombre de usuario el puede modificar tu consulta para ejecutar lo que el desee.

Por ejemplo si inserta esto:

';DROP TABLE users;--

Ahora la consulta se verá así:

SELECT * FROM users WHERE user = '';DROP TABLE users;--'

Hay varias formas con las que alguien puede atacar nuestra base de datos mediante sql injection. Las más rudimentarias son escapar los caracteres ‘, pero no siempre funcionan correctamente ya que hay otras formas de alterar la consulta. Echemos un vistazo a esto.

Hay una solucion muy simple a este problema. No concatenes texto proporcionado por el usuario en una consulta, vinculalo. La mayoría de las bases de datos soportan bindings, mysql es una de ellas, postgresql, oracle y la lista crece. La idea es simple: proporcionas un marcador en tu consulta que será reemplazado con la variable actual, por lo que no es posible ninguna modificación posible.

Nuestra anterior consulta se puede escribir tal que así:
Mysql (en php con la extensión mysqli)

SELECT * FROM users WHERE user = ?

Oracle

SELECT * FROM users WHERE user = :user

Ejemplo de la extensión mysqli PHP (para más información lee la función de mysqli_stmt bind_param

/* recoger los datos del usuario */
$nombreUsuario = $_GET['nombreUsuario'];
/* preparar la consulta */
$stmt = $mysqli->prepare('SELECT * FROM users WHERE user = ?');
/* vincular el valor */
$stmt->bind_param('s', $nombreUsuario);
/* ejecutar la declaración preparada */
$stmt->execute();

$stmt->bind_result($name, $surname);
/* recoger los datos de usuario*/
while ($stmt->fetch())
{
  //imprimir los datos del usuario
  echo $nombre, ' =>  ', $apellido , "\n";
}
/* cerrar la operación */
$stmt->close();

Ejemplo con la extensión PHP PDO (para más información lee la función prepare de PDO

/* recoger los datos del usuario */
$nombreUsuario = $_GET['nombreUsuario'];
/* preparar la consulta */
$sth = $dbh->prepare('SELECT * FROM users WHERE user = ?');
/* vincultar y ejecutar la consulta */
$sth->execute($userName);
$row = $sth->fetchAll();

Por lo tanto es muy facil para nosotros evitar la reescritura evitando que concatenar los datos proporcionados por el usuario con nuestra consulta, pero hay escenarios donde debes generar una consulta con los datos del usuario, no solo enviando una consulta. Por ejemplo, quieres mostrar diferentes tipos de libros basados en lo que inserta el usuario. El usuario puede elegir ver libros de ciencia ficción, literatura o libros de cocina.

Podrías estar tentado a escribir algo como esto:

SELECT * FROM $_GET[categoriaLibro]

¿Pero, que pasa si el usuario quiere modificar tu script e inserta ‘users’ como una categoría? En ese caso podrá ver el contenido de la tabla de usuarios. La solución para este problema es comprobar la categoría proporcionada por el usuario en vez de la cadena directamente.

En php deberías hacer algo como esto:

switch ($_GET['bookCategory'])
{
        case 'sf_books':
                $table = 'sf_books';
                break;
        case 'literature_books':
                $table = 'literature_books';
                break;
        default:
                $table = 'cooking_books';
}
$query = "SELECT * FROM $table";

Si escribes tu script de esta forma, cuando el usuario inserte ‘users’ como una categoría, el verá los libros de cocina, o puedes elegir mostrar un error, esa es tu elección.
Para resumir este post, quiero decir que deberías utilizar el binding de variables cuando quieras enviar datos a la base de datos que proporciona el usuario. Y si formas tus consultas según la entrada de usuario, simpre compara lo que inserta con algun valor predefinido y usa el valor por defecto.
Nunca concatenes entrada de usuario en tus consultas, sin excepciones

Configuración de VirtualHosts en Nginx (Nginx II)

Written on May 8th, 2008
.

Echa la compilación de nuestro nginx ahora voy a explicar mas o menos mi sistema para gestionar virtualhosts con nginx. Primero de todo, suelo meter los virtualhosts en /opt en el que crearemos el esqueleto para cada slice.

Configuramos nginx para virtualhosts utilizando ficheros separados para cada uno de ellos:

mkdir -p /usr/local/nginx/conf/{sites-available,sites-enabled}

Substituímos con lo siguiente en el fichero /usr/local/nginx/conf/nginx.conf, para que lea todos los ficheros en sites-enabled y de paso lo limpiamos:

user www-data;
worker_processes  1; #Número de workers configurable según necesidades

error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    access_log  /var/log/nginx/access.log;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay        on;

    gzip  on;

    include /etc/nginx/sites-enabled/*;

}

LLegados a este punto ya tenemos nuestro nginx preparado para servir cualquiera de los distintos tipos de servidores que soporta (web, proxy web inverso, mail, balanceo de carga). Como aquí nos centraremos en configurar un servidor web con virtualhosts, sigo relatando las operaciones.

Read the rest of this entry »

Tips de seguridad en PHP

Written on November 30th, 2007
.
  1. Utiliza los parámetros de PDO o mysql_real_escape_string sobre valores SQL para evitar la inyección del SQL.
  2. Utiliza htmlspecialchars/htmlentities/strip_tags para escapar HTML y Javascript así evitaras ataques XSS.
  3. 3. Utiliza sesiones y asegura los sockets para prevenir que roben la sesión además almacena un símbolo especial md5 ej: md5(uniqueid(rand(),time)) en la sesión y comprobarla con un campo oculto en método POST y así prevenir que otro usuario ingrese.
    Ejemplo:

    if($_SESSION [’seguridadMd5′]==$_POST[’campoculto’]) {
    /*Sesión segura*/
    }
  4. Usa escapeshellarg/escapeshellcmd cuando llames comandos exec para así evitar injección de comandos.
  5. Quita los linebreaks (BR, P y BLOCKQUOTE tags) de headers entrantes para prevenir la terminación y la inyección de códigos. Fixed >PHP5.1
  6. Utiliza comprobación md5 en valores y sessionid serializados para validar su integridad.
  7. 7. Utiliza el === para verificar valores de entrada (identidad de datos y de tipos).

  8. Utiliza está configuración,
    * ini_set(”display_errors”,false);
    * ini_set(”log_errors”,true);
    * ini_set(”error_log”,”ruta/php.log”);
    * ini_set(”session.save_path”,”ruta/www”); o “mm” session module o store en sqllite db
    * php.ini expose_php=off
    * php.ini register_globals=off
    * Apache servertokens=prod
  9. Para cambios de sesión usa session_regenerate_id.
  10. Usa secure sockets SSL para trasacciones comerciales.