operators <? - ¿Cuál es el uso del símbolo @ en PHP?




?> <?php (10)

He visto usos de @ delante de ciertas funciones, como las siguientes:

$fileHandle = @fopen($fileName, $writeAttributes);

¿Para qué sirve este símbolo?


Answers

Al igual que algunas respuestas anteriores: el operador @ suprime todos los errores en PHP, incluidos avisos, advertencias e incluso errores críticos.

PERO: Por favor, realmente no use el operador @ en absoluto.

¿Por qué?

Bueno, porque cuando usa el operador @ para la supresión de errores, no tiene idea de dónde comenzar cuando ocurre un error. Ya me divertí un poco con el código heredado donde algunos desarrolladores usaban el operador @ con bastante frecuencia. Especialmente en casos como operaciones de archivos, llamadas de red, etc. Todos estos son casos en los que muchos desarrolladores recomiendan el uso del operador @ , ya que a veces esto está fuera de alcance cuando se produce un error aquí (por ejemplo, una API de terceros puede ser inaccesible, etc.) .).

¿Pero de qué sirve todavía no usarlo? Echemos un vistazo desde dos perspectivas:

Como desarrollador: cuando se usa @ , no tengo la menor idea de por dónde empezar. Si hay cientos o incluso miles de llamadas a funciones con @ el error podría ser como todos. No es posible una depuración razonable en este caso. E incluso si es solo un error de la tercera parte, entonces está bien y ya está listo. ;-) Además, es mejor agregar suficientes detalles al registro de errores, de modo que los desarrolladores puedan decidir fácilmente si una entrada de registro es algo que debe verificarse más o si es solo una falla de la tercera parte que está fuera del alcance del desarrollador.

Como usuario: a los usuarios no les importa en absoluto cuál es el motivo de un error o no. El software está ahí para que trabajen, para terminar una tarea específica, etc. No les importa si es culpa del desarrollador o un problema de la tercera parte. Especialmente para los usuarios, recomiendo encarecidamente registrar todos los errores, incluso si están fuera de alcance. Tal vez te darás cuenta de que una API específica está desconectada con frecuencia. ¿Qué puedes hacer? Puede hablar con su socio API y si no pueden mantenerlo estable, probablemente debería buscar otro socio.

En resumen: debe saber que existe algo como @ (el conocimiento siempre es bueno), pero simplemente no lo use . Muchos desarrolladores (especialmente aquellos que están depurando el código de otros) estarán muy agradecidos.


PHP es compatible con un operador de control de errores: el signo at (@) . Cuando se añade a una expresión en PHP, cualquier mensaje de error que pueda generar esa expresión será ignorado.

Si ha configurado una función de controlador de errores personalizado con set_error_handler() entonces se seguirá llamando, pero este controlador de errores personalizado puede (y debería) llamar a error_reporting() que devolverá 0 cuando la llamada que activó el error fue precedida por una @ .

<?php
/* Intentional file error */
$my_file = @file ('non_existent_file') or
    die ("Failed opening file: error was '$php_errormsg'");

// this works for any expression, not just functions:
$value = @$cache[$key];
// will not issue a notice if the index $key doesn't exist.

?>

Nota:-

1) El operador @ solo funciona con expresiones.

2) Una simple regla de oro es: si puede tomar el valor de algo, puede anteponerle al operador @. Por ejemplo, puede anteponerlo a variables, funcionar e incluir llamadas, constantes, etc. No puede añadirlo a las definiciones de funciones o clases, o estructuras condicionales como if y foreach, etc.

Advertencia:-

Actualmente, el prefijo del operador de control de errores "@" incluso deshabilitará el informe de errores para los errores críticos que terminarán la ejecución del script. Entre otras cosas, esto significa que si usa "@" para suprimir los errores de una función determinada y no está disponible o se ha escrito de forma incorrecta, el script morirá allí mismo sin ninguna indicación de por qué.


@ suprime el mensaje de error generado por la función. fopen lanza un error cuando el archivo no se cierra. @ símbolo @ hace que la ejecución se mueva a la línea siguiente, incluso si el archivo no existe. Mi sugerencia sería no utilizar esto en su entorno local cuando desarrolle un código PHP.


Si la apertura falla, se genera un error de nivel E_WARNING. Puede usar @ para suprimir esta advertencia.


Supongamos que no hemos utilizado el operador "@", nuestro código se vería así:

$fileHandle = fopen($fileName, $writeAttributes);

¿Y si no se encuentra el archivo que estamos intentando abrir? Se mostrará un mensaje de error.

Para suprimir el mensaje de error, estamos utilizando el operador "@" como:

$fileHandle = @fopen($fileName, $writeAttributes);

Suprime los errores.

Ver Operadores de Control de Errores en el manual:

PHP es compatible con un operador de control de errores: el signo at (@). Cuando se añade a una expresión en PHP, cualquier mensaje de error que pueda generar esa expresión será ignorado.

Si ha configurado una función de controlador de errores personalizado con set_error_handler() entonces se seguirá llamando, pero este controlador de errores personalizado puede (y debería) llamar a error_reporting() que devolverá 0 cuando la llamada que activó el error fue precedida por una @ ...



También tenga en cuenta que a pesar de que se ocultan los errores, ¡se seguirá ejecutando cualquier controlador de errores personalizado (establecido con set_error_handler )!


@ suprime los mensajes de error.

Se utiliza en fragmentos de código como:

@file_get_contents('http://www.exaple.com');

Si el dominio " http://www.exaple.com " no es accesible, se mostrará un error, pero con @ no se muestra nada.


Esta respuesta está escrita para mostrar cuán trivial es omitir el código de validación de usuario de PHP mal escrito, cómo (y con qué) funcionan estos ataques y cómo reemplazar las antiguas funciones de MySQL con una declaración preparada de forma segura. (probablemente con muchas repeticiones) están ladrando a nuevos usuarios que hacen preguntas para mejorar su código.

En primer lugar, siéntase libre de crear esta base de datos de prueba de mysql (he llamado preparación mía):

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

Con eso hecho, podemos pasar a nuestro código PHP.

Supongamos que el siguiente script es el proceso de verificación para un administrador en un sitio web (simplificado pero funciona si lo copia y lo utiliza para realizar pruebas):

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }

    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Parece lo suficientemente legítimo a primera vista.

El usuario tiene que ingresar un nombre de usuario y contraseña, ¿verdad?

Brillante, no entrar en lo siguiente:

user: bob
pass: somePass

y enviarlo.

La salida es la siguiente:

You could not be verified. Please try again...

¡Súper! Trabajando como se esperaba, ahora probemos el nombre de usuario y la contraseña reales:

user: Fluffeh
pass: mypass

¡Increíble!Hi-fives all round, el código verificó correctamente a un administrador. ¡Es perfecto!

Bueno en realidad no. Digamos que el usuario es una personita inteligente. Digamos que la persona soy yo.

Ingrese en lo siguiente:

user: bob
pass: n' or 1=1 or 'm=m

Y la salida es:

The check passed. We have a verified admin!

Enhorabuena, solo me permitiste ingresar a tu sección de administradores súper protegidos solo cuando ingresé un nombre de usuario falso y una contraseña falsa. En serio, si no me cree, cree la base de datos con el código que proporcioné y ejecute este código PHP, que a simple vista parece verificar bastante bien el nombre de usuario y la contraseña.

Así que, en respuesta, ESO ES POR QUÉ ESTÁ AÚNTEO.

Entonces, echemos un vistazo a lo que salió mal, y por qué acabo de entrar en su cueva de murciélagos solo para administradores. Tomé una conjetura y asumí que no estaba siendo cuidadoso con sus entradas y simplemente las pasó directamente a la base de datos. Construí la entrada de una manera que CAMBIARÍA la consulta que realmente estaba ejecutando. Entonces, ¿qué se suponía que era y qué terminó siendo?

select id, userid, pass from users where userid='$user' and pass='$pass'

Esa es la consulta, pero cuando reemplazamos las variables con las entradas reales que usamos, obtenemos lo siguiente:

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

¿Vea cómo construí mi "contraseña" de modo que primero cierre la comilla alrededor de la contraseña y luego introduzca una comparación completamente nueva? Luego, solo por seguridad, agregué otra "cadena" para que la comilla simple se cierre como se esperaba en el código que originalmente teníamos.

Sin embargo, esto no se trata de que la gente te grite ahora, se trata de mostrarte cómo hacer que tu código sea más seguro.

De acuerdo, entonces, ¿qué salió mal y cómo podemos solucionarlo?

Este es un ataque clásico de inyección SQL. Uno de los más simples para el caso. En la escala de vectores de ataque, este es un niño pequeño que ataca un tanque y gana.

Entonces, ¿cómo protegemos su sección de administrador sagrado y la hacemos agradable y segura? Lo primero que debe hacer es dejar de usar esas mysql_*funciones realmente antiguas y obsoletas . Lo sé, seguiste un tutorial que encontraste en línea y funciona, pero es antiguo, está desactualizado y en el espacio de unos minutos, lo he superado sin siquiera romper un sudor.

Ahora, tiene las mejores opciones de usar mysqli_ o PDO . Personalmente, soy un gran fanático de PDO, por lo que usaré PDO en el resto de esta respuesta. Hay profesionales y contras, pero personalmente encuentro que los profesionales superan con creces a los contras. Es portátil a través de múltiples motores de base de datos, ya sea que esté usando MySQL u Oracle o simplemente con cualquier cosa. Simplemente cambiando la cadena de conexión, tiene todas las funciones sofisticadas que queremos usar y es agradable y limpio. Me gusta limpiar

Ahora, echemos un vistazo a ese código nuevamente, esta vez escrito con un objeto PDO:

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;

    $database='prep';
    $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Las principales diferencias son que no hay más mysql_*funciones. Todo se hace a través de un objeto PDO, en segundo lugar, está utilizando una declaración preparada. Ahora, ¿qué es una declaración preparada que pides? Es una forma de decirle a la base de datos antes de ejecutar una consulta, qué consulta es la que vamos a ejecutar. En este caso, le decimos a la base de datos: "Hola, voy a ejecutar una instrucción de selección que desea id, ID de usuario y pase de la tabla a los usuarios, donde el ID de usuario es una variable y el paso también es una variable".

Luego, en la instrucción de ejecución, pasamos a la base de datos una matriz con todas las variables que ahora espera.

Los resultados son fantásticos. Vamos a probar esas combinaciones de nombre de usuario y contraseña de antes otra vez:

user: bob
pass: somePass

El usuario no fue verificado. Increíble.

Qué tal si:

user: Fluffeh
pass: mypass

Oh, me emocioné un poco, funcionó: el cheque pasó. Tenemos un administrador verificado!

Ahora, probemos los datos que un ingenioso tipo ingresaría para intentar superar nuestro pequeño sistema de verificación:

user: bob
pass: n' or 1=1 or 'm=m

Esta vez, obtenemos lo siguiente:

You could not be verified. Please try again...

Esta es la razón por la que le gritan cuando publica preguntas, es porque la gente puede ver que su código puede ser anulado sin siquiera intentarlo. Por favor, use esta pregunta y respuesta para mejorar su código, para hacerlo más seguro y para utilizar las funciones actuales.

Por último, esto no quiere decir que este es un código PERFECTO. Hay muchas más cosas que podría hacer para mejorarla; por ejemplo, use contraseñas con hash, asegúrese de que cuando almacena información sensetiva en la base de datos, no la almacene en texto sin formato, tenga múltiples niveles de verificación, pero realmente simplemente cambia su antiguo código propenso a la inyección a esto, estará muy bien en el camino para escribir un buen código, y el hecho de que haya llegado tan lejos y que todavía esté leyendo me da un sentido de esperanza de que no solo implementará este tipo del código al escribir sus sitios web y aplicaciones, pero puede salir e investigar esas otras cosas que acabo de mencionar, y más. Escriba el mejor código que pueda, no el código más básico que apenas funciona.





php operators error-suppression