WordPress security: plugins de seguridad en WordPress

A la hora de dotar de más seguridad a un WordPress, además de la que trae de serie, nos encontramos con una selva de opciones. Podemos querer dotar a nuestra web de control de accesos (vigilancia del formulario de login para evitar ataques de fuerza bruta), o bloquear  peticiones sospechosas (XSS, SqlInjection). Quizás también queramos vigilar los ficheros que nuestros usuarios suben a la web (exploits, shells, virus, etc), querer interactuar con la base de datos (copia de seguridad de la misma, consultas, etc) o controlar las versiones de nuestros plugins.

Si buscamos en el directorio de plugins de WordPress  con los términos “wordpress firewall” o “wordpress security” nos salen bastantes candidatos (algunos repetidos en ambas búsquedas), todos ellos luchando por ser la solución definitiva para la Seguridad de WordPress. Personalmente, suelo huir de las soluciones “todo en uno” porque, como dice un refrán español, “el que mucho abarca, poco aprieta”. Pero dejémonos de charlas y vamos a lo que interesa.

Análisis

Vamos a realizar el análisis de algunos de los plugins más descargados, o de los que aparecen en los primeros puestos de las búsquedas, en los apartados de firewall o security. De ellos, vamos a intentar averiguar cómo se comportan respecto al análisis y control de las peticiones que llegan desde el usuario. Es decir, nos centraremos en ver cómo se compartan actuando como Firewall de la aplicación o como IDS (Intrusion Detection System).

Para el test, realizaremos estas sencillas peticiones, tanto GET como POST, para ver cómo se comportan:

  1. GET – http://www.wordpress.exa/?p=91+order+by+23;
  2. GET – http://www.wordpress.exa/?p=91+union+all+select+1;
  3. GET – http://www.wordpress.exa/?p=91+or+(1=(select+count(*)+from+mysql.user));
  4. GET – http://www.wordpress.exa/?p=91+or+(1=(select+count(*)+from+information_schema.tables));
  5. GET – http://www.wordpress.exa/?p=91“><img src=”” onerror=”alert(1)”>;
  6. POST – http://www.wordpress.exa/ – data = {txtBuscar : post”><!—}
  7. GET http://www.wordpress.exa/?p=91+or+(1%3D(select%2Bcount(*)+from+information_schema.tables))%3B
  8. GET http://www.wordpress.exa/?p=91%20%6F%72%20%28%31%3D%28%73%65%6C%65%63%74%2B%63%6F%75%6E%74%28%2A%29%20%66%72%6F%6D%20%69%6E%66%6F%72%6D%61%74%69%6F%6E%5F%73%63%68%65%6D%61%2E%74%61%62%6C%65%73%29%29%3B

Los plugins escogidos, y que instalaremos y configuraremos su apartado de Firewall, son los siguientes.

Tras proceder a realizar todas las peticiones para cada uno de ellos, obtenemos la siguiente lista en la que  se indica si el plugin fue capaz de detectar el ataque (alucinad como yo).

Plugin Petición
(1) (2) (3) (4) (5) (6) (7) (8)
Simple Security Firewall NO SI NO NO NO NO NO NO
BulletProof Security NO SI SI SI SI NO SI NO
Block Bad Queries NO NO NO NO NO NO NO NO
Wordfence Security MA MA MA MA MA MA MA MA
Sucuri Security ? ? ? ? ? ? ? ?
All in One WP Security & Firewall NO NO NO NO SI NO NO NO
Mute Screamer NO NO SI SI SI SI SI SI

 

Resumen

Ninguno de los plugins analizados, salvo NinjaFirewall (quizás Sucuri) y Mute Screamer, analizan las variables que no vengan por GET (que son muchas). Muchos optan por usar el fichero .htaccess como elemento de bloqueo, que si bien detiene el ataque en las primeras fases de la petición (sólo entra en juego Apache, no el motor de base de datos) no tiene en cuenta la posible codificación de las variables ni aquellas enviadas por POST. Esta dependencia del fichero .htaccess hace que no funcionen en servidores que no sean Apache (si esto no es cierto, y alguno lo sabe, agradecería que me lo comentase). Por otro lado, los que no usan .htaccess implementan sus propios códigos de control pero siguen limitándose a las variables GET.

La sorpresa que me he llevado no ha sido pequeña, pensaba que el código que protege a más de 400.000 de instalaciones (no cuento a Wordfence, con sus 800.000, pero debiera hacerlo porque el bloqueo de IPs es manual y no hay un trabajo real de IDS) estaba mejor hecho. Si yo que nada sé sobre seguridad he visto esto, qué habrán encontrado los profesionales del tema.

Si tuviera que poner nota a los plugins en cuanto a la detección y bloqueo de ataques podría ser así:

  • Mute Screamer: notable
  • WP_Expose: notable
  • Wordfence Security: suspenso
  • BulletProof Security: suspenso
  • All in One WP Security & Firewall: suspenso
  • Block Bad Queries: suspenso
  • Simple Security Firewall: suspenso

A la vista de estos resultados, tened cuidado con qué instaláis en vuestro WordPress y cuanto confiáis en ello.

Algo de código

Antes de acabar veamos algo de las tripas de alguno de los plugin. En el caso de NinjaFirewall trabaja usando la directiva PHP de auto_prepend_file, lo que le da un acceso temprano a las variables, y permite analizar tanto GET como POST, REQUEST, COOKIE, HTTP_USER_AGENT, HTTP_REFERER, PATH_INFO, PATH_TRANSLATED y PHP_SELF. Pero el cómo lo hace podría decirse que es un poco ligero porque sólo se centra en escapar caracteres como ( “,’, <, >, %00,`  ) mediante real_escape_string() o con expresiones regulares.

if (is_string($str) ) {
	if ($how == 1) {
		$str2 = $nfw_['mysqli']->real_escape_string($str);
		$str2 = str_replace('`', '\`', $str2);
	} elseif ($how == 2) {
		$str2 = str_replace(	array('\\', "'", '"', "\x0d", "\x0a", "\x00", "\x1a", '`', '<', '>'),
			array('\\\\', "\\'", '\\"', 'X', 'X', 'X', 'X', '\\`', '\\<', '\\>'),	$str);
	} else {
		$str2 = str_replace(	array('\\', "'", "\x00", "\x1a", '`'), array('\\\\', "\\'", 'X', 'X', '\\`'),	$str);
	}
}
else {

	if ($how == 3) {
		$key2 = str_replace(	array('\\', "'", "\x00", "\x1a", '`', '<', '>'),
			array('\\\\', "\\'", 'X', 'X', '\\`', '<', '>'),	$key, $occ);
	} else {
		// We sanitise variables **name** using :
		// -str_replace to escape [\], ['] and ["]
		// -str_replace to replace [\n], [\r], [\x1a] and [\x00] with [X]
		//	-str_replace to replace [`], [<] and [>] with their HTML entities (` < >)
		$key2 = str_replace(	array('\\', "'", '"', "\x0d", "\x0a", "\x00", "\x1a", '`', '<', '>'),
			array('\\\\', "\\'", '\\"', 'X', 'X', 'X', 'X', '`', '<', '>'),	$key, $occ);
	}
}

BulletProof Security trabaja con .htaccess lo que imposibilita la detección de ataques codificados vía GET y no gestiona los que son enviados por POST.

Sucuri Security:  trabaja, creo, enviando la petición a los servidores de Sucuri lo que, si es cierto, me hace pensar en la posible penalización en el rendimiento.

Block Bad Queries: Usa código propio para bloquear las peticiones pero es tan pobre que casi es como si no lo tuviera. Además, olvida que los caracteres que llegan pueden estar codificados y hace una comparación literal.

$request_uri_array  = apply_filters('request_uri_items',  array('eval\(', 'UNION\+SELECT', '\(null\)', 'base64_', '\/localhost', '\%2Flocalhost', '\/pingserver', '\/config\.', '\/wwwroot', '\/makefile', 'crossdomain\.', 'proc\/self\/environ', 'etc\/passwd', '\/https\:', '\/http\:', '\/ftp\:', '\/cgi\/', '\.cgi', '\.exe', '\.sql', '\.ini', '\.dll', '\.asp', '\.jsp', '\/\.bash', '\/\.git', '\/\.svn', '\/\.tar', ' ', '\<', '\>', '\/\=', '\.\.\.', '\+\+\+', '\:\/\/', '\/&&', '\/Nt\.', '\;Nt\.', '\=Nt\.', '\,Nt\.', '\.exec\(', '\)\.html\(', '\{x\.html\(', '\(function\('));
	$query_string_array = apply_filters('query_string_items', array('\.\.\/', '127\.0\.0\.1', 'localhost', 'loopback', '\%0A', '\%0D', '\%00', '\%2e\%2e', 'input_file', 'execute', 'mosconfig', 'path\=\.', 'mod\=\.'));
	$user_agent_array   = apply_filters('user_agent_items',   array('binlar', 'casper', 'cmswor', 'diavol', 'dotbot', 'finder', 'flicky', 'nutch', 'planet', 'purebot', 'pycurl', 'skygrid', 'sucker', 'turnit', 'vikspi', 'zmeu'));
// more code
preg_match( '/' . implode( '|', $request_uri_array )  . '/i', $request_uri_string )

Simple Security Firewall: con código de la aplicación, del estilo de BBQ, que ni comentaré. Este es el código de src/processors/firewall.php que cómo controla las peticiones…miedo que da!

protected function doPassCheckBlockSqlQueries() {
	$aTerms = array(
		'/concat\s*\(/i',
		'/group_concat/i',
		'/union.*select/i'
	);
	$fPass = $this->doPassCheck( $this->getParamsToCheck(), $aTerms, true );
	if ( !$fPass ) {
		$sAuditMessage = sprintf( _wpsf__('Firewall Trigger: %s.'), _wpsf__('SQL Queries') );
		$this->addToAuditEntry( $sAuditMessage, 3, 'firewall_block' );
		$this->doStatIncrement( 'firewall.blocked.sqlqueries' );
	}
	return $fPass;
}
private function doPassCheck( $aParamValues, $aMatchTerms, $fRegex = false ) {

	$fFAIL = false;
	// code
	if ( $fRegex && preg_match( $sTerm, $mValue ) ) { //dodgy term pattern found in a parameter value
		$fFAIL = true;
	}
	else if ( strpos( $mValue, $sTerm ) !== false ) { //dodgy term found in a parameter value
		$fFAIL = true;
	}
	// code

	return true;
}

Wordfence Security: No tiene un Firewall como tal, tiene un monitor en tiempo real de las peticiones sobre el que permite bannear IP’s. Es un trabajo manual con lo que lo voy a considerar como IDS.

All in One WP Security & Firewall trabaja con .htaccess, al igual que Bulletproof Secutiry pero con menos controles que este, e ignora todo lo que venga por POST.