Nueva pregunta

Pregunta:

Fecha: 02-08-2016 15:45:58 (En Español)

Problemas con el doble backslash \\ al insertar en la base de datos o imprimir en pantalla[Resuelta]

Hola a todos

En el grupo de Facebook de PHPCentral he realizado una consulta, supuse que seria facil y rápida la resolución del caso. Pareciera que no es tan sencillo.
Hilo de la conversación en Facebook

Me esta sacando de quicio este problema.


Estoy utilizando WAMPSERVER

Estoy grabando en un campo de tipo TEXT en una tabla utilizando PDO
Es un texto que es enviado por medio de un formulario con un TEXTAREA

Cuando guardo un texto que contiene por ej la cadena \\dcserver\public\documents\
Al insertar el texto lo guarda como: \dcserver\public\documents\
Quitándole una barra \
es decir cuando encuentra una doble barra \\ elimina una barra

La tabla posee varias columnas, el codigo php tiene mas contenido, pero lo he resumido para hacer pruebas y que sea mas entendible para ustedes.

Sin el formulario.

Simulare la entrada del dato proveniente del formulario con la variable $tarea el ingreso de datos puede contender codigo HTML, XML y datos del tipo \\algo\otraCosa, es un texto.

TABLA: tareas



<?php

class DB {

// Conexion a la base de datos con PDO
    public static function conectar() {
        $instancia = NULL;
        try {
            $instancia = new PDO("mysql:host=localhost;dbname=databasetest", "userdb", "UserPwd_DB.792.73s7", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
        } catch (PDOException $e) {
            echo 'Error de conexión: ' . $e->getMessage();
            exit;
        }
        return $instancia;
    }

}

class tarea {
    /* Insertar Nuevo Registro */

    private $nombreTabla = "tareas";

    public function nuevo($tarea) {



$query = <<<EOF
                    INSERT INTO {$this->nombreTabla} 
                         (id, Tarea) 
                    VALUES 
                         (NULL, :tarea);
EOF;

        $conn = DB::conectar();
        $stmt = $conn->prepare($query);
        $stmt->bindParam(':tarea', $tarea, PDO::PARAM_STR);
        $stmt->execute();

        if ($stmt->rowCount() == 1) {
            return TRUE;
        } else {
            return FALSE;
        }
    }

    /* Listar Tareas */

    public function listar() {
        $conn = DB::conectar();
        $query = "SELECT * FROM {$this->nombreTabla} ORDER BY id DESC LIMIT 10;";
        $stmt = $conn->prepare($query);
        $stmt->execute();
        $row = $stmt->fetchAll();

        echo "Lista, cantidad (" . count($row) . ") </BR></BR>";

        foreach ($row as $registro) {
            echo $registro["id"] . '. <strong>TAREA:</strong> </BR><textarea rows="10" cols="76">' . $registro["tarea"] . '</textarea></BR>';
            echo $registro["id"] . '. <strong>TAREA:</strong> </BR>' . nl2br(htmlentities($registro["tarea"]));

            echo "</BR><HR></BR>";
        }
    }

}

/********** FIN DE LAS CLASES **********/

header('Content-Type: text/html; charset=utf-8');


/* PRUEBA DE INSERT  */
$miTarea = new tarea;

$tarea = <<<EOF
 \\dcserver\public\documents\
  \a\b\c\
  \\\\a\b\c\
  \\a\\b\\c\\
  c:\users\miusuario\

  URL: http://php.net/manual/es/function.stripcslashes.php

  Año 2016 Mes Agosto Día 2

  NET SHARE
  ADMIN$       C:\Windows                      Admin remota

  Insertar Codigo THML en la base de datos

  <table border="0" width="100%" id="table1" cellpadding="0" cellspacing="0">
  <tr>
  <td>&nbsp;</td>
  <td width="921">
  <img border="0" src="img/isl_logo_mediano.png" width="393" height="246"></td>
  </tr>
  </table>

  Insertar codigo XML

  <?xml version="1.0" encoding="UTF-8" ?>
  <!DOCTYPE Edit_Mensaje SYSTEM "Edit_Mensaje.dtd">

  <Edit_Mensaje>
  <Mensaje>
  <Remitente>
  <Nombre>Nombre del remitente</Nombre>
  <Mail> Correo del remitente </Mail>
  </Remitente>
  <Destinatario>
  <Nombre>Nombre del destinatario</Nombre>
  <Mail>Correo del destinatario</Mail>
  </Destinatario>
  <Texto>
  <Asunto>
  Este es mi documento con una estructura muy sencilla
  no contiene atributos ni entidades...
  </Asunto>
  <Parrafo>
  Este es mi documento con una estructura muy sencilla
  no contiene atributos ni entidades...
  </Parrafo>
  </Texto>
  </Mensaje>
  </Edit_Mensaje>
EOF;
 
$miTarea->nuevo($tarea);

if ($miTarea) {
    echo 'Se Ha Guardado:</BR></BR>' . nl2br(htmlentities($tarea)) . '<HR>';
} else {
    echo 'No se guardo';
}

$miTarea->listar();


Al ejecutar el codigo lo que hace es insertar el dato en la base de datos, muestra el dato que fue insertado y luego lista los 10 últimos guardados en un textarea y en HTML.

El resultado de ello es algo como:
Se Ha Guardado:

\dcserver\public\documents\
\a\b\c\
\\a\b\c\
\a\b\c\
c:\users\miusuario\

URL: http://php.net/manual/es/function.stripcslashes.php

Año 2016 Mes Agosto Día 2

NET SHARE
ADMIN$ C:\Windows Admin remota

Insertar Codigo THML en la base de datos

<table border="0" width="100%" id="table1" cellpadding="0" cellspacing="0">
<tr>
<td>&nbsp;</td>
<td width="921">
<img border="0" src="img/isl_logo_mediano.png" width="393" height="246"></td>
</tr>
</table>

Insertar codigo XML

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE Edit_Mensaje SYSTEM "Edit_Mensaje.dtd">

<Edit_Mensaje>
<Mensaje>
<Remitente>
<Nombre>Nombre del remitente</Nombre>
<Mail> Correo del remitente </Mail>
</Remitente>
<Destinatario>
<Nombre>Nombre del destinatario</Nombre>
<Mail>Correo del destinatario</Mail>
</Destinatario>
<Texto>
<Asunto>
Este es mi documento con una estructura muy sencilla
no contiene atributos ni entidades...
</Asunto>
<Parrafo>
Este es mi documento con una estructura muy sencilla
no contiene atributos ni entidades...
</Parrafo>
</Texto>
</Mensaje>
</Edit_Mensaje>

Como se puede ver elimina las \\

Las pruebas que he realizado fueron varias entre ellas utilizar:
htmlentities() y html_entity_decode()
htmlspecialchars() y htmlspecialchars_decode()

$tarea = str_replace('\\','\\\\',$tarea); 

el resultado es
\\dcserver\\public\\documents\\
\\a\\b\\c\\
\\\\a\\b\\c\\
\\a\\b\\c\\

Tambien Probe:
$tarea = preg_replace("/([\/*])/ixm", "\/", $tarea);

Sin obtener el resultado deseado con lo antes mencionado.



Luego probé lo siguiente (No garba en la base de datos, simplemente lo imprime):
$tarea = "\\dcserver\public\documents\
\a\b\c\
 \\\\a\b\c\       
\\a\\b\\c\\
 c:\users\miusuario\
Año 2016 Mes Agosto Día 2";
echo 'El Texto Ingresado es:</BR></BR>'.nl2br($tarea).'<HR>';


En ese caso no se inserta en la base de datos, simplemente se le asigna a la variable $tarea el contenido y se imprime en pantalla, el resultado es el mismo, le quita una \ cuando hay \\ y si hay 4 le quita 2, es decir le quita la mitad de barras.

Muestra en pantalla
El Texto Ingresado es:

\dcserver\public\documents\
\a\b\c\
\\a\b\c\
\a\b\c\
c:\users\miusuario\
Año 2016 Mes Agosto Día 2


Con el último ejemplo asumo que el problema no esta en insertar en la base de datos, sino en lago que estoy haciendo mal en el codigo de PHP, como que escapa las barras \\

Espero que me puedan dar un manto de luz sobre este caso antes que me vuelva loco jejeje

Muchas gracias por la atención

Que tengan un buen dia

saludos
Etiquetas: $_POST - Formato - Formulario - HTML - MySQL - PHP - PHP PDO - Pregunta - Variables Votos: 3 - Respuestas: 10 - Vistas: 17 Compartir en: Google Facebook Twitter LinkedIn Link
 

Respuestas:

  • Fecha: 02-08-2016 16:02:41 Por lo que veo en el post que hice la consulta salen las \\ jejejee   Votos: 2 - Link respuesta
     
  • Fecha: 02-08-2016 19:07:52 Hola Walter, interesante pregunta, suele confundir porque el resultado obtenido suele ser de lo más diverso dependiendo del origen de los datos, la utilización de comillas simples, dobles, heredoc, nowdoc y funciones de escapeo/encoding varias...

    Veamos un ejemplo 100% ejecutable y luego las concluciones del caso (creo que es la mejor forma de encarar la explicación).

    <?php
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $tarea = (isset($_POST['tarea'])) ? $_POST['tarea'] : '';
        $tarea = htmlspecialchars($tarea, ENT_QUOTES);
        echo $tarea;
        exit;
    }
    ?>
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
        </head>
        <body>
            <form method="POST">
                <label>Texto:</label><br/>
                <textarea name="tarea" cols="30">\\dcserver\public\documents\</textarea><br/>
                <input type="submit" value="Enviar"/>
            </form>
        </body>
    </html>
    


    Descripción del ejemplo:
    Básicamente lo que podemos observar en el ejemplo es una Script PHP que procesa tanto peticiones GET como POST. Ante una petición GET muestra una web HTML con un formulario para enviar los datos, y ante una petición POST intenta recuperar el dato enviado (campo textarea llamado "tarea") e imprime su contenido en pantalla.
    Si inspeccionamos como son enviados los datos en la petición POST veremos que estos son encodeados por defecto con application/x-www-form-urlencoded, o sea, el dato enviado \\dcserver\public\documents\ es encodeado como 5C%5Cdcserver%5Cpublic%5Cdocuments%5C
    Luego del lado del servidor al almacenar el dato recibido (desde $_POST['tarea']) en la variable $tarea no hay ningun tipo de interpretación por parte de PHP, simplemente guarda el dato en la variable, luego por seguridad se utiliza htmlspecialchars que convierte caracteres especiales en entidades HTML, con el parámetro ENT_QUOTES para convertir tanto las comillas dobles como las simples en entidades HTML (podrías no utilizar htmlspecialchars y el resultado para el ejemplo sería el mismo).

    Notas extra:
    * Como el formulario no tiene especificado un atributo "action" el destino de la petición POST es la misma web.
    * El texto "\\dcserver\public\documents\" ya esta presugerido en el HTML a fin de hacer más amenas las pruebas, pero claro esta que este puede ser quitado/remplazado.

    Por otro lado, si hacemos una asignación de la variable $tarea con un texto proveniente del Script PHP (o sea, puesto entre comillas simples, dobles, heredoc, nowdoc) tendremos que lidiar con el concepto de interpretación de PHP, y aquí es donde el caracter barra invertida (backslash o contrabarra) es conflictivo, porque se utiliza para escapar caracteres, recomiendo leer en profundidad Cadenas de caracteres (Strings) de la documentación oficial para comprender el concepto.

    Al margen de lo dicho, veamos un ejemplo simple y que dice la documentación al respecto:
    <?php
    $tarea = '\\dcserver\public\documents';
    echo $tarea;
    

    Como resultado del script se imprime en pantalla: \dcserver\public\documents

    Transcribo de la documentación: ...para especificar una comilla simple literal, se ha de escapar con una barra invertida (\). Para especificar una barra invertida literal, se duplica (\\). Todas las demás instancias de barras invertidas serán tratadas como una barra invertida literal: esto significa que otras secuencias de escape que podrían utilizarse, tales como \r o \n, serán mostradas literalmente tal y como se especifican, en lugar de tener cualquier otro significado especial...

    En conclusión (y aquí es donde tal vez puede ser confuso el concepto) por más que usemos comillas simples para especificar un string seremos afectados por la barra inverntida en casos puntuales.

    Walter, espero que mi respuesta te sea de ayuda.

    Saludos y buen código!

    PD: es un placer leer la documentación de tus preguntas/errores/respuestas/notas :) , así da gusto tomarse un tiempo para contestar, una pregunta que claramente esta a la altura de una buena respuesta.
      Votos: 3 - Link respuesta
     
  • Fecha: 03-08-2016 22:10:12 Fernando Muchísimas gracias por dedicarle tiempo a mi consulta y brindarme una magistral respuesta.
    No había logrado conseguir una respuesta en la red, he visto algunas e incluso en la web de stackoverflow que no coincidían con mi caso sobre el backslash .
    He estado varias horas liando con este tema probando de todo, y leyendo, hasta que he encontrado la solución con tu excelente respuesta.
    He leído atentamente tu respuesta y el link que me has dejado de Cadenas de caracteres (Strings)
    Tuve que leer un par de veces para entender los conceptos ya que difieren ligeramente entre ellos.

    He realizado unos cambios y en pantalla se imprime como deseo con las backslash dobles, como a su vez es guardado en la base de datos sin problemas.

    No he utilizado htmlspecialchars($tarea, ENT_QUOTES);
    a fin que en la base de datos quede registrado tal cual ha sido enviado por el formulario por el método POST.


    Dejaré mi interpretación sobre lo leído en la respuesta y lo que ensayado, tal vez le sirva a otros.
    Sobre Nowdoc y heredoc en PHP usando mi caso.

    $tarea = <<<'EOF' el tag, etiqueta, con comillas simples. No se realiza ningún análisis en el contenido de la variable, es decir no se hace escapes.

    Nowdocs, comillas simples:

    $tarea = <<<'EOF'
    \\dcserver\public\documents\
    \\dcserver\private\documents\myuser$
    C:\users\miusuario\
    
    URL: http://php.net/manual/es/language.types.string.php
    
    NET SHARE
    ADMIN$  C:\Windows  Admin remota
    
    Otros ejemplos con backslash 
    \a\b\c\
    \\\\a\b\c\
    \\a\\b\\c\\
    EOF;
    echo $tarea;
    


    En el Resultado no se altera el contenido de la variable:

    \\dcserver\public\documents\
    \\dcserver\private\documents\myuser$
    C:\users\miusuario\
    
    URL: http://php.net/manual/es/language.types.string.php
    
    NET SHARE
    ADMIN$ C:\Windows Admin remota
    
    Otros ejemplos con backslash
    \a\b\c\
    \\\\a\b\c\
    \\a\\b\\c\\

    Heredoc comillas dobles o sin encomillar.
    $tarea = <<<"EOF" o <<<EOFel tag, etiqueta, con comillas dobles o sin encomillar. Se realiza análisis en el contenido de la variable y se escapa.

    $tarea = <<<"EOF"'
    \\dcserver\public\documents\
    \\dcserver\private\documents\myuser$
    C:\users\miusuario\
    
    URL: http://php.net/manual/es/language.types.string.php
    
    NET SHARE
    ADMIN$  C:\Windows  Admin remota
    
    Otros ejemplos con backslash 
    \a\b\c\
    \\\\a\b\c\
    \\a\\b\\c\\
    EOF;
    echo $tarea;
    

    o bien:
    $tarea = <<<EOF
    \\dcserver\public\documents\
    \\dcserver\private\documents\myuser$
    C:\users\miusuario\
    
    URL: http://php.net/manual/es/language.types.string.php
    
    NET SHARE
    ADMIN$  C:\Windows  Admin remota
    
    Otros ejemplos con backslash 
    \a\b\c\
    \\\\a\b\c\
    \\a\\b\\c\\
    EOF;
    echo $tarea;
    


    El resultado se altera el contenido de la variable.
    \dcserver\public\documents\
    \dcserver\private\documents\myuser$
    C:\users\miusuario\
    
    URL: http://php.net/manual/es/language.types.string.php
    
    NET SHARE
    ADMIN$ C:\Windows Admin remota
    
    Otros ejemplos con backslash
    \a\b\c\
    \\a\b\c\
    \a\b\c\
    



    Otros ejemplos incluyendo variables.
    <?php
    // Nowdoc y Heredoc
    $titulo = 'Aprendiendo con PHP Central';
    
    $now = <<<'NOWDOC'
        Usando <b>Nowdoc</b> comillas simples '
            
        Yo estoy $titulo
    NOWDOC;
    
    $here = <<<HEREDOC
        Usando <b>Heredoc</b> sin comillas 
            
        Yo estoy $titulo
    HEREDOC;
    
    $hereComillasDobles = <<<"HEREDOC"
        Usando Usando <b>Heredoc</b> con comillas dobles "
            
        Yo estoy $titulo
    HEREDOC;
    
    echo $now;
    echo '</BR>';
    echo $here;
    echo '</BR>';
    echo $hereComillasDobles;

    En este caso en pantalla se ve:

    Usando Nowdoc comillas simples ' Yo estoy $titulo
    Usando Heredoc sin comillas Yo estoy Aprendiendo con PHP Central
    Usando Usando Heredoc con comillas dobles " Yo estoy Aprendiendo con PHP Central

    Espero que sirvan los ejemplos, si hay algo mal por favor mencionarlo ;)

    Cuasi OFFTOPICS - Consulta sobre seguridad:
    1- Si no uso htmlspecialchars pero al guardar (INSERT) el dato en la tabla utilizó $stmt->bindParam(':tarea', $tarea, PDO::PARAM_STR); con ello evito un intento de ataque?

    2- Al recuperar el registro de la base de datos con SELECT tambien tengo que utilizar $stmt->bindParam(':tarea', $tarea, PDO::PARAM_STR); para evitar una inyección de codigo?



    FERNANDO por todo.

    Recuerden que he dejado planteado 2 preguntas en este post antes de los ejemplos.

    Saludos
      Votos: 3 - Link respuesta
     
  • Fecha: 04-08-2016 07:39:44 Hola Walter, me pone muy contento el saber que mi respuesta te ha sido de ayuda y muchas gracias por volcar las conclusiones del aprendizaje / experiencias.

    Con respecto a las 2 subpreguntas que dejas planteadas, paso a dar mis respuesta:
    1- Pregunta: Si no uso htmlspecialchars pero al guardar (INSERT) el dato en la tabla utilizó $stmt->bindParam(':tarea', $tarea, PDO::PARAM_STR); con ello evito un intento de ataque?
    Respuesta: no, con PDOStatement::bindParam salvas un posible inyección de código SQL en tu consulta (SQL Injection), pero no (por ejemplo) un ataque XSS.
    Esto que te comento es relativo, porque todo depende de como tratas el contenido ingresado por el usuario. Si lo guardas tal cual en la base de datos (cosa que yo no recomiendo) luego deberás tener mucho cuidad al momento de mostar ese contenido, porque si el usuario guardo, por ejemplo "<script>alert('ok');</script>" al momento de mostrarlo se podrá ejecutar el código (dependiendo si conviertes o no los caracteres especiales en entidades html).
    Es muy importante el uso de htmlspecialchars <-- esta función trabaja con los caracteres: & (et), " (comilla doble), ' (single quote), < (menor que), > (mayor que), no con todos. De hecho en la documentación oficial, cuando se explica el array $_POST esta puesta la función htmlspecialchars de forma implícita en el "Ejemplo #1 Ejemplo de $_POST"

    2- Pregunta: Al recuperar el registro de la base de datos con SELECT tambien tengo que utilizar $stmt->bindParam(':tarea', $tarea, PDO::PARAM_STR); para evitar una inyección de codigo?
    Respuesta: si y no, eso va a depender de si tienes el parámetro garantizado o no... digamoslo de otra forma, si el parámetro lo genera tu sistema no es necesario, porque se supone que tu generas el parámetro de una forma controlada/segura y no contendrá una inyección de código, ahora bien, si el parámetro es el resultado de algún ingreso de usuario, pues si, usa bindParam para evitar una inyección. No faltará el puritano que diga "usalo siempre" porque el parámetro puede no estar viniendo directamente del usuario, pero al verse comprometido tu sistema por alguna vulnerabilidad puede que el parámetro sea alterado, etc etc.....
    Otro motivo por el cual te puede interesar usar bindParam es por la performance en consultas (queries) repetitivas, ya que en la primera ejecución tendrás la consulta preparada y en las subsiguientes con parámetros distintos no será necesario preparar la consulta nuevamente.

    Saludos y buen código!
      Votos: 3 - Link respuesta
     
  • Fecha: 07-08-2016 03:40:41 Buenos días, estuve leyendo todos los comentarios y, como siempre, aprendiendo.

    De todas maneras, hago mi aporte.

    Estoy utilizando los filtros o filter, tanto filter_input como filter_var, sobre todo utilizo filter_input.

    En , está mucho mejor explicado, obviamente pero un pequeño resumen

    Para utilizar filter_input, se le pasan, por lo menos 2 parámetros y un tercero, que creo, es el más importante

    El primer parámetro, es una constante e indica el tipo de input (GET,POST, etc)
    El segundo parámetro, es un string, el cual es el nombre del índice del input
    El tercero, es el tipo de validación o saneamiento que le quiero dar a la variable input (sabiendo que es lo que espero de ese input). Estos parámetros son constantes, también, y en el caso de ese post, creo que sería FILTER_SANITIZE_URL.

    Para saber los tipos de filtros que existen .

    Espero que haya sido de ayuda

    DIEGO
      Votos: 4 - Link respuesta
     
  • Fecha: 13-08-2016 16:03:32 muchas gracias Fernando y Diego por sus buenas respuestas.

    Se podrá independizar mis preguntas sobre seguridad en una pregunta nueva?
    Sin que se pierdan las respuesta y votos de uds.
    Ya que quiero extender el tema con mas preguntas y conclusiones con ejemplos en creando un escenario.
    y si lo sigo en este post creo que se perdería las conclusiones y las excelentes respuesta
    Creo que es mas util y mas facil de seguir en otro tema.

    En caso que no se pueda, lo continuo aquí.

    Saludos
      Votos: 2 - Link respuesta
     
  • Fecha: 14-08-2016 00:49:18 Hola Walter, si claro, creá una nueva pregunta y así, como bien vos decis, no se mezclan los temas.
    Muchas gracias.
    Saludos,
      Votos: 1 - Link respuesta
     
  • Fecha: 14-08-2016 15:53:01 He realizado la nueva pregunta Consulta sobre seguridad en bases de datos

    pueden migrar sus respuestas a esa pregunta asi, elimino el contenido de mi OFFTOPIC dejando el resto del contenido de ejemplos y demás.
    Y eliminamos las respuestas sobre el OFFTOPICS
    Dejando esta pregunta limpia
    muchas gracias
    Saludos
      Votos: 2 - Link respuesta
     
  • Fecha: 14-08-2016 19:12:24 Muchas gracias Walter, me parece correcto abrir la nueva pregunta para separar los temas y extender un poco más la explicación, pero yo diría que en esta pregunta dejemos los hilos de conversación como se dieron, porque creo que esta bastante claros...

    En estos días veré de participar en la nueva pregunta, muchas gracias!

    Saludos,
      Votos: 1 - Link respuesta
     
  • Fecha: 14-08-2016 20:56:52 Fernando:
    si podes en la nueva pregunta publica tu respuesta dada aqui asi no se pierde la explicación.

    De igual manera la respuesta de Diego estaria bueno que este publicada en el nuevo tema.

    Seguimos en la otra pregunta

    Saludos
      Votos: 1 - Link respuesta
     
Para participar activamente de la comunidad primero debes autenticarte, ingresa al sistema.Iniciar Sesión