Desbloquea el contenido mixto envolviendo http en https en un túnel SSL

Hoy vamos a hacer que nuestra aplicación web segura HTTPS envuelva en HTTPS las peticiones HTTP inseguras que intercambia con servicios externos. O dicho de otra manera, vamos a aprender a desbloquear el contenido mixto.

Advertencia: recurso no seguro

Esta semana me encontraba trabajando en la GUI de una app web que tenía https y resulta que la parte cliente, la GUI (Interfaz Gráfica de Usuario), tenía que comunicarse por medio de jQuery con un servicio web externo que no respondía en https, sino que respondía en http.

Como consecuencia de este desajuste HTTPS/HTTP que había entre la GUI y el web service, el navegador web bloqueaba la respuesta del servidor, lanzando una advertencia al usuario que le decía que dicho recurso no era seguro.

Recuerda…

Seguro que alguna vez te ha pasado esto que explico hoy, este problema se llama contenido mixto bloqueado.

Si tu website trabaja con HTTPS todos los recursos que sirve bajo HTTP quedan bloqueados de forma predeterminada por el navegador, degradándose así de forma notable la usabilidad de la aplicación. ¡Los usuarios recibirán un mensaje de advertencia comunicándoles que tu web sirve contenido no seguro!

¿Cómo puedo desbloquear los recursos HTTP?

En mi caso concreto, esta aplicación jQuery a la que me refiero en este post, hacía la llamada HTTP insegura para subir un archivo. Para solucionar esto, pues, tuve que cambiar la llamada de mi app jQuery: si antes el código JS llamaba al web service HTTP no seguro, ahora llamaría a una acción de controlador de mi servidor protegido con HTTPS, y desde dicha acción de controlador, ya en el servidor, se haría la llamada al web service de forma transparente para el usuario.

Túnel SSL

Un túnel SSL

La siguiente acción de controlador MVC pertenece al framework PHP Slim y maneja la subida de archivos con el componente Upload de codeguy (Josh Lockhart), el mismo tipo que está detrás de Slim.

$app->post(
    '/action-name/upload-file/:slug/:n/',
    function ($slug,$n) use ($app){
        // constant definition
        $rand_seed = microtime(true);
        define('MULTIPART_BOUNDARY', '---------------------------' . $rand_seed);
        define('FORM_FIELD', 'PDF');
        // try to upload file only to trigger form validation on the server side
        $storage = new \Upload\Storage\FileSystem(APP_PATH . 'uploads');
        $file = new \Upload\File(FORM_FIELD, $storage);            
        $file->addValidations(array(
            new \Upload\Validation\Mimetype('application/pdf'),
            new \Upload\Validation\Size('2M')
        ));
        $file->setName($rand_seed);
        if($file->upload())
        {
            $file_contents = file_get_contents(APP_PATH . 'uploads/' . $rand_seed . '.pdf');
            // http body
            $http_body = '--' . MULTIPART_BOUNDARY ."\r\n".
            'Content-Disposition: form-data; name="' . FORM_FIELD . '"; filename="' . $_FILES[FORM_FIELD]['name'] . '"' . "\r\n".
            'Content-Type: application/pdf'."\r\n\r\n".
            $file_contents."\r\n".
            '--' . MULTIPART_BOUNDARY . "--\r\n";
            // curl options
            $options = array(
                'http' => array(
                    'header'  => 'Content-Type: multipart/form-data; boundary='.MULTIPART_BOUNDARY,
                    'method'  => 'POST',
                    'content' => $http_body,
                )
            );        
            $context  = stream_context_create($options);
            // run request
            $result = file_get_contents("http://iam-insecure.com/action/$slug/$n", false, $context);
            // remove uploaded file
            unlink(APP_PATH . 'uploads/' . $rand_seed . '.pdf');
            // print web service response
            print_r($result);
        }
        exit;
    }
);

En resumidas cuentas, en el servidor tienes que lidiar con el protocolo HTTP de forma completamente manual; debes recoger la petición HTTP y volver a montar las cabeceras y el cuerpo para, justo a continuación, reenviarla de nuevo al servicio web externo y recoger la respuesta.

Y esto es todo por hoy, ¡espero que este truco te resulte práctico! ¿Lo compartes con tus amigos y amigas?

También te puede interesar leer esto