PHP brinda a los desarrolladores una gran flexibilidad, pero esto también trae riesgos potenciales para los problemas de seguridad. Recientemente necesito resumir los problemas pasados. Aquí, aprovechando la traducción de un artículo y agregando algunas reflexiones de mi propio desarrollo, haré un resumen.

Introducción
Al desarrollar un servicio de Internet, debes tener siempre presente el concepto de seguridad y reflejarlo en el código que desarrollas. El lenguaje de script PHP no se preocupa por los problemas de seguridad, especialmente para la mayoría de los desarrolladores sin experiencia. Cada vez que abordes cualquier problema de transacciones que involucre dinero u otros asuntos, debes prestar especial atención a la consideración de problemas de seguridad, por ejemplo, al desarrollar un foro o un carrito de compras.
Puntos generales de protección de seguridad
No confiar en los formularios
Para la verificación general de front-end con Javascript, dado que no se puede conocer el comportamiento del usuario, por ejemplo, si desactiva el motor de JavaScript del navegador, puede enviar datos maliciosos al servidor mediante POST. Es necesario realizar la verificación en el lado del servidor, validando los datos transmitidos en cada script PHP para prevenir ataques XSS e inyección SQL.
No confiar en los usuarios
Debes asumir que cada dato que recibe tu sitio web contiene código malicioso y amenazas ocultas; debes limpiar cada dato.
Desactivar variables globales
Realiza la siguiente configuración en el archivo php.ini:
register_globals = Off
Si esta opción de configuración está activada, aparecerán grandes riesgos de seguridad. Por ejemplo, hay un archivo de script process.php que inserta los datos recibidos en la base de datos. El formulario que recibe los datos ingresados por el usuario podría ser el siguiente:
<input name="username" type="text" size="15" maxlength="64">
De esta manera, después de enviar los datos a process.php, PHP registrará una variable $username y enviará los datos de esta variable a process.php. Al mismo tiempo, para cualquier parámetro de solicitud POST o GET, se establecerá dicha variable. Si no se inicializa explícitamente, aparecerá el siguiente problema:
<?php
// Define $authorized = true only if user is authenticated
if (authenticated_user()) {
$authorized = true;
}
?>
Aquí, supongamos que la función authenticated_user simplemente evalúa el valor de la variable $authorized. Si la configuración register_globals está activada, cualquier usuario puede enviar una solicitud para establecer el valor de la variable $authorized a cualquier valor, eludiendo así esta verificación.
Todos estos datos enviados deben obtenerse a través de los arrays globales predefinidos incorporados en PHP, incluidos $_POST, $_GET, $_FILES, $_SERVER, $_REQUEST, etc. Entre ellos, $_REQUEST es una variable combinada de los tres arrays $_GET/$_POST/$_COOKIE, con el orden predeterminado de $_COOKIE, $_POST, $_GET.
- Opciones de configuración de seguridad recomendadas
error_reporting establecido en Off: no expongas mensajes de error a los usuarios; durante el desarrollo se puede establecer en ON.
safe_mode establecido en Off
register_globals establecido en Off
Deshabilita las siguientes funciones: system, exec, passthru, shell_exec, proc_open, popen
open_basedir establecido en /tmp, de modo que la información de sesión tenga permiso de almacenamiento, y además establece un directorio raíz separado para el sitio web.
expose_php establecido en Off
allow_url_fopen establecido en Off
allow_url_include establecido en Off
Ataque de inyección SQL
Para las sentencias SQL que operan sobre la base de datos, se debe prestar especial atención a la seguridad, porque el usuario puede ingresar sentencias específicas que cambien la funcionalidad de la sentencia SQL original. Un ejemplo similar es el siguiente:
$sql = "select * from pinfo where product = '$product'";
En este momento, si el parámetro $product ingresado por el usuario es:
39'; DROP pinfo; SELECT 'FOO
Entonces la sentencia SQL final se convierte en lo siguiente:
select product from pinfo where product = '39'; DROP pinfo; SELECT 'FOO'
Esto se convertirá en tres sentencias SQL, lo que provocará la eliminación de la tabla pinfo, causando graves consecuencias.
Este problema se puede resolver fácilmente usando las funciones integradas de PHP:
$sql = 'Select * from pinfo where product = '"'
mysql_real_escape_string($product) . '"';
Para prevenir ataques de inyección SQL, es necesario hacer dos cosas:
Siempre realizar validación de tipos en los parámetros de entrada
Siempre usar la función mysql_real_escape_string para escapar caracteres especiales como comillas simples, comillas dobles, comillas invertidas, etc.
Pero aquí, según la experiencia de desarrollo, no actives Magic Quotes de PHP; esta característica ya fue eliminada en PHP 6. Siempre escapa tú mismo cuando sea necesario.
Prevención de ataques XSS básicos
El ataque XSS no es como otros ataques; este ataque se realiza en el cliente. La herramienta XSS más básica consiste en insertar un script de JavaScript en la página del formulario pendiente de envío del usuario para robar los datos enviados por el usuario y las cookies.
Las herramientas XSS son más difíciles de proteger que la inyección SQL. Los sitios web de las grandes empresas han sido atacados por XSS. Aunque este ataque no está relacionado con el lenguaje PHP, se puede usar PHP para filtrar los datos del usuario y así protegerlos. Aquí se utiliza principalmente el filtrado de los datos del usuario, generalmente eliminando etiquetas HTML, especialmente la etiqueta a. A continuación se muestra un método de filtrado común:
function transform_HTML($string, $length = null) {
// Helps prevent XSS attacks
// Remove dead space.
$string = trim($string);
// Prevent potential Unicode codec problems.
$string = utf8_decode($string);
// HTMLize HTML-specific characters.
$string = htmlentities($string, ENT_NOQUOTES);
$string = str_replace("#", "#", $string);
$string = str_replace("%", "%", $string);
$length = intval($length);
if ($length > 0) {
$string = substr($string, 0, $length);
}
return $string;
}
Esta función convierte los caracteres especiales de HTML en entidades HTML. Cuando el navegador renderiza este texto, lo muestra en forma de texto plano. Por ejemplo, <strong>bold</strong> se mostrará como:
<STRONG>BoldText</STRONG>
El núcleo de la función anterior es la función htmlentities, que convierte las etiquetas HTML especiales en caracteres de entidad HTML, filtrando así la mayoría de los ataques XSS.
Pero para atacantes XSS experimentados, hay formas más ingeniosas de atacar: codificar su código malicioso en hexadecimal o UTF-8 en lugar de texto ASCII normal. Por ejemplo, se puede hacer de la siguiente manera:
<a href="http://host/a.php?variable=%22%3e %3c%53%43%52%49%50%54%3e%44%6f%73%6f%6d%65%74%68%69%6e%67%6d%61%6c%69%63%69%6f%75%73%3c%2f%53%43%52%49%50%54%3e">
De esta manera, el resultado renderizado por el navegador es en realidad:
<a href="http://host/a.php?variable="> <SCRIPT>Dosomethingmalicious</SCRIPT>
De esta manera se logra el propósito del ataque. Para evitar esta situación, es necesario basarse en la función transform_HTML para convertir también # y % en sus correspondientes símbolos de entidad, y agregar el parámetro $length para limitar la longitud máxima de los datos enviados.
Uso de SafeHTML para prevenir ataques XSS
La protección contra ataques XSS mencionada anteriormente es muy simple, pero no cubre todas las marcas del usuario; además, hay cientos de formas de omitir las funciones de filtrado para enviar código JavaScript, y no hay manera de prevenir completamente esta situación.
Actualmente, ningún script único puede garantizar que no sea vulnerado por ataques, pero siempre hay algunos con un nivel de protección relativamente mejor. Hay dos formas de protección de seguridad: lista blanca y lista negra. Entre ellas, la lista blanca es más simple y efectiva.
Una solución de lista blanca es SafeHTML, que es lo suficientemente inteligente como para reconocer HTML válido y luego eliminar cualquier etiqueta peligrosa. Esto requiere el paquete HTMLSax para el análisis.
Método para instalar y usar SafeHTML:
1. Ve ahttp://pixel-apes.com/safehtml/?page=safehtml Descarga el SafeHTML más reciente
2. Coloca el archivo en el directorio classes del servidor; este directorio contiene todas las bibliotecas SafeHTML y HTMLSax.
3. Incluye el archivo de clase SafeHTML en tu propio script
4. Crea un objeto SafeHTML
5. Usa el método parse para filtrar
<?php
/* If you're storing the HTMLSax3.php in the /classes directory, along
with the safehtml.php script, define XML_HTMLSAX3 as a null string. */
define(XML_HTMLSAX3, '');
// Include the class file.
require_once('classes/safehtml.php');
// Define some sample bad code.
$data = "This data would raise an alert <script>alert('XSS Attack')</script>";
// Create a safehtml object.
$safehtml = new safehtml();
// Parse and sanitize the data.
$safe_data = $safehtml->parse($data);
// Display result.
echo 'The sanitized data is <br />' . $safe_data;
?>
SafeHTML no puede prevenir completamente los ataques XSS; es solo una forma relativamente compleja de script para verificar.
Uso de cifrado HASH unidireccional para proteger datos
El cifrado hash unidireccional garantiza que la contraseña de cada usuario sea única y no pueda ser descifrada. Solo el usuario final conoce la contraseña; el sistema tampoco conoce la contraseña original. Una ventaja de esto es que, incluso si el sistema es atacado, los atacantes no podrán conocer los datos de la contraseña original.
El cifrado y el hash son dos procesos diferentes. A diferencia del cifrado, el hash no puede ser descifrado; es unidireccional. Además, dos cadenas diferentes pueden producir el mismo valor hash, por lo que no se garantiza la unicidad del valor hash.
Los valores hash procesados por la función MD5 básicamente no pueden ser descifrados, pero siempre existe la posibilidad, y también hay diccionarios de hash MD5 en Internet.
Uso de mcrypt para cifrar datos
La función hash MD5 puede mostrar datos en formularios legibles, pero al almacenar la información de tarjetas de crédito de los usuarios, es necesario cifrarla antes de almacenarla y luego descifrarla cuando sea necesario.
El mejor método es usar el módulo mcrypt, que incluye más de 30 métodos de cifrado para garantizar que solo el cifrador pueda descifrar los datos.
<?php
$data = "Stuff you want encrypted";
$key = "Secret passphrase used to encrypt your data";
$cipher = "MCRYPT_SERPENT_256";
$mode = "MCRYPT_MODE_CBC";
function encrypt($data, $key, $cipher, $mode) {
// Encrypt data
return (string)
base64_encode
(
mcrypt_encrypt
(
$cipher,
substr(md5($key),0,mcrypt_get_key_size($cipher, $mode)),
$data,
$mode,
substr(md5($key),0,mcrypt_get_block_size($cipher, $mode))
)
);
}
function decrypt($data, $key, $cipher, $mode) {
// Decrypt data
return (string)
mcrypt_decrypt
(
$cipher,
substr(md5($key),0,mcrypt_get_key_size($cipher, $mode)),
base64_decode($data),
$mode,
substr(md5($key),0,mcrypt_get_block_size($cipher, $mode))
);
}
?>
La función mcrypt requiere la siguiente información:
1. Datos a cifrar
2. La clave (key) utilizada para cifrar y descifrar los datos
3. El algoritmo específico elegido por el usuario para cifrar los datos (cipher: comoMCRYPT_TWOFISH192,MCRYPT_SERPENT_256, MCRYPT_RC2, MCRYPT_DES, and MCRYPT_LOKI97)
4. El modo utilizado para el cifrado
5. La semilla de cifrado, los datos utilizados para iniciar el proceso de cifrado; son datos binarios adicionales que se usan para inicializar el algoritmo de cifrado.
6. La longitud de la clave de cifrado y la semilla; se puede obtener usando las funciones mcrypt_get_key_size y mcrypt_get_block_size.
Si tanto los datos como la clave son robados, el atacante puede recorrer los ciphers para encontrar la forma de abrirlos. Por lo tanto, necesitamos aplicar MD5 a la clave de cifrado una vez para garantizar la seguridad. Además, dado que los datos cifrados devueltos por la función mcrypt son datos binarios, guardarlos en un campo de la base de datos puede causar otros errores; por eso se utilizó base64encode para convertir estos datos en números hexadecimales y facilitar su almacenamiento.
Referencia: http://www.codeproject.com/Articles/363897/PHP-Security
Fuente: http://blog.csdn.net/u010487568/article/details/29828757