You are on page 1of 18

Cmocrearunscriptdeiniciodesesinseguraenphpy

MySQL
8partes: Configurartuservidor ConfigurarlabasededatosMySQL Crearlapginadeconexinparalabasededatos CrearfuncionesPHP
Crearpginasdeprocesamiento CreararchivosdeJavascript CrearpginasHTML Protegerpginas

Conmsymshistoriasdelapirateraenlasnoticias,losdesarrolladoresbuscanmejoresmanerasdeasegurarsusitio.Si
tusitiotieneunsistemademiembros,podraestarenriesgodesercrackeadoylosdatosdelosusuariospodranverse
comprometidos.LapresenteguatemostrarunintentodecrearuniniciodesesinseguraconPHP.Hemospuestonuestro
mejoresfuerzoenprogramarelcdigo,perolaseguridadysobretodolaencriptacinsontemascomplejosquecambiantodo
eltiempoynopodemosdecirquedominamostodoesecampo.Porlotanto,podramoshaberobviadounoscuantostrucos
ennuestraprogramacin.Deseras,hznoslossaberytrataremosdeincorporartodamejoraaloquetenemos.

Seguirlapresenteguateayudaracuidartedemuchostiposdeataquesquepodranemplearloscrackersparaapoderarse
delcontroldelascuentasdeotrosusuarios,eliminarcuentasy/ocambiardatos.Acontinuacintepresentaremosunlistade
posiblesataquesdeloscualeslapresenteguaprocuradefenderse:
SQLInjections
SessionHijacking
NetworkEavesdropping
CrossSiteScripting
BruteForceAttacks

Elenfoqueconsisteenemplearunacombinacindefiltrosdedatos,encriptacinyotrosmtodosparahacerleslavidaun
pocomsdifcilaquienespiensenatacarte.

Tratamosdemejorarcontinuamenteelpresentescript.Laversinmsrecientedelcdigoestdisponibleengithub.Podra
haberalgunasdiferenciasentreelcdigoquedescarguesenesapginayelcdigocitadoenelpresenteartculo.Debers
tenerpresentequenuestroobjetivonohasidoquelapresentacindelaspginasHTMLhechaporlaaplicacinsevea
bonitaenloabsoluto.

TambindeberstenerpresentequenocerramoslasetiquetasPHPenlosarchivosquecontienensolamentecdigoPHP.
Estoestenlneaconlamayoraderecomendacionesdeformatodecdigo.

Porltimo,esnecesarioquesepasquetepedimoscreartodoslosarchivosnoHTMLdelaaplicacinendiversosdirectorios
dentrodeldirectoriorazdelaaplicacin.Lamaneramsfcildecrearlaestructuradeldirectoriocorrectoconsisteen
descargarelcdigomsrecientehaciendoclicenunodelosenlacesantesmencionados.

Porfavor,sinteteconlalibertaddeusarlapresenteaplicacincomobaseparatupropiaimplementacin,peronolausesa
mododeejemplodeunabuenaprcticadeprogramacin.



Cosasquenecesitars

Debidoaqueestaremosutilizandomysqli_*queesunconjuntodeclasesPHPparaaccederanuestrabasededatos
mySQL,necesitarslassiguientesversionesdePHPyMySQL.
PHPversin5.3oposterior
MySQLversin4.1.3oposterior

Evidentemente,tambinnecesitarsunservidorwebconfiguradoparausarPHPparaalojartuspginas.Esteser
muyprobablementeelservidorwebdealojamientodetupgina,amenosqueestsalojandotmismoelsitio.

PararevisarlaversindePHPyMySQLentuservidor,emplealafuncinphpinfo().
Parte
Configurartuservidor
1

1 Instalaunservidorweb,PHPyMySQLentuservidor.

LamayoradelosserviciosdealojamientotienePHPymySQLyainstalados,perotendrsquerevisarquetenganlas
versionesmsrecientesdePHPymySQLparaquelapresenteguapuedasertedeayuda.Sinotienenalmenos
PHP5.3yMySQL5,podrasponerenteladejuiciosucompromisoconlaseguridad.Mantenertusoftwareactualizado
espartedelprocesodeseguridad.

Sitienestupropioservidorocomputadora,debersinstalarelsoftwarerequeridonormalmentesegntusistema.En
general,sinovasausarlaconfiguracinpormotivosdeproduccinyvasadesarrollarenWindowsoOSX,instalar
unpaquetedeaplicaciones(stack)XAMPPserlomsrecomendable.Consiguelaversinapropiadaparatusistema
operativoen:

http://www.apachefriends.org/en/xampp.html

Sinembargo,tenpresentequebajoningunacircunstanciadebersutilizarXAMPPparacrearteunambientede
servidordeproduccin.

EnLinux,usaelgestordepaquetesparadescargareinstalarlospaquetesnecesarios.Algunasdistribuciones,como
Ubuntu,contienentodaslasaplicacionesnecesariasenunsolopaquete.Tansolotendrsquehacerlosiguienteen
unaventanadeterminaldeUbuntu:

sudoaptgetinstalllampserver^phpmyadmin


Peroaunqueinstalesloselementosnecesarios,asegratedeconfigurarMySQLconunacontraseaderazsegura.

Parte
ConfigurarlabasededatosMySQL
2

1 CreaunabasededatosMySQL.

Inicialasesinentubasededatoscomousuarioadministrador(normalmenteraz).

Enlapresenteguacrearemosunabasededatosllamadainicio_seguro.

VecmocrearunabasededatosenphpMyAdmin.

PodrsusarelcdigoacontinuacinohacerlomismoenphpMyAdminoentuclienteGUIMySQLfavorito,silo
deseas:

CREATEDATABASE`secure_login`;

Nota:algunosserviciosdealojamientonotepermitirncrearunabasededatosconphpMyAdmin,poresoaprende
hacerloencPanel.

2 CreaunusuariosoloconlosprivilegiosSELECCIONAR,ACTUALIZAReINSERTAR.

Crearunusuarioconprivilegiosrestringidossignificaqueenelcasodequealgunavezseviolelaseguridadentu
secuenciadecomandos,elhackernopodrborrarnidejarnadadesdenuestrabasededatos.Alutilizaresteusuario
podrshacercasiloquequierascontuaplicacin.Sieresrealmenteparanoico,creaunusuariodiferenteparacada
funcin.

ClaroquenecesitarshaberiniciadosesinenMySQLcomounusuarioconlosprivilegiossuficientesparapoder
crearotrousuario.Porlogeneral,esteusuarioserraz.

Lossiguientesdetallessondelusuarioquehemoscreado:
Usuario:"sec_user"
Contrasea:"eKcGZr59zAa2BEWU"

Nota:terecomendamoscambiarlacontraseadelquemencionamosanteriormentecuandovayasaejecutarloentu
propioservidor.Silohaces,asegratetambindecambiarelcdigoacontinuacinyelcdigodeconexindelabase
dedatosPHPenlaaplicacinquecrearemos.

Recuerdaquenotendrqueserunacontraseaquepuedasrecordar,asquehazlalomscomplicadaposible.Por
ejemplo,esteesungeneradordecontraseasaleatorias.

AcontinuacintambinestarelcdigoSQLparacrearelusuariodebasededatosyotorgarlelospermisos
necesarios.Siloprefieres,tambinpodrashacerloenunclientedebasededatosGUIcomophpmyadmin:

CREATEUSER'sec_user'@'localhost'IDENTIFIEDBY'eKcGZr59zAa2BEWU';
GRANTSELECT,INSERT,UPDATEON`secure_login`.*TO'sec_user'@'localhost';

Sivesquevasaeliminarregistrosdecualquieradelastablasdelpresentemdulo,debersagregarELIMINARala
listadeprivilegiososinopodrascrearunusuariodiferentequetengasolamenteelprivilegioELIMINARysoloenla
tablaenlaquequierasborrarregistros,sinoquiereshacerloenambas.Noesnecesarioqueotorgueselprivilegio
ELIMINARenloabsolutoparaelpresenteejemplodescript.

3 CreaunatablaMySQLquelleveporttulo"miembros".

Elcdigoacontinuacincrearunatablaconcincocampos(identificacin,nombredeusuario,correoelectrnico,
contrasea,sal).UtilizaremoseltipodedatosCHARparaloscamposcuyaextensinconozcamos,yaqueloscampos
contraseaysalsiempretendrn128caracteresdelargo.UtilizarCHARenesoscasosahorrarenergade
procesamiento:

CREATETABLE`secure_login`.`members`(
`id`INTNOTNULLAUTO_INCREMENTPRIMARYKEY,
`username`VARCHAR(30)NOTNULL,
`email`VARCHAR(50)NOTNULL,
`password`CHAR(128)NOTNULL,
`salt`CHAR(128)NOTNULL
)ENGINE=InnoDB;

Comolohemosmencionadoanteriormente,podrshacerloencualquiertipodeclientequeprefieras.

4 Creaunatablaparaalmacenarintentosdeiniciodesesin.

Utilizaremosestatablaparaalmacenarlosintentosdeiniciodesesindeunusuario.Estaesunamaneraconlaque
dificultaremoslosataquesdefuerzabruta:

CREATETABLE`secure_login`.`login_attempts`(
`user_id`INT(11)NOTNULL,
`time`VARCHAR(30)NOTNULL
)ENGINE=InnoDB

5 Creaunafiladepruebaenlatablamiembros.

Serimportantepodersercapazdeprobarelscriptdeiniciodesesin,asqueacontinuacintepresentaremosel
scriptparacrearunusuariocondetallesconocidos:
Username:test_user
Email:test@example.com
Password:6ZaxN2Vzm9NUJT2y

Elcdigoquenecesitarsparapoderiniciarsesincomoesteusuarioes:

INSERTINTO`secure_login`.`members`VALUES(1,'test_user','test@example.com',
'00807432eae173f652f2064bdca1b61b290b52d40e429a7d295d76a71084aa96c0233b82f1feac45529e0726559645acaed6f3ae58a286b9f075916ebf66cacc'
'f9aab579fc1b41ed0c44fe4ecdbfcdb4cb99b9023abb241a6db833288f4eea3c02f76e0d35204a8695077dcf81932aa59006423976224be0390395bae152d4ef'

Parte
Crearlapginadeconexinparalabasededatos
3

1 Creaunapginadeconfiguracionesglobales.

CreaunacarpetallamadaincludeseneldirectoriorazdelaaplicacinyluegocreaunarchivoPHPnuevoenese
directorio.Ponleelnombrepslconfig.php.Enunambientedeproduccin,debersubicaresearchivoytodoslos
demsarchivosincludefueradelarazdedocumentosdelservidorweb.Silohaces,cosaqueterecomendamos,
tendrsquealterarelincludeolasdeclaracionesrequeridastantocomoseanecesarioparaquelaaplicacinpueda
encontrarlosarchivosinclude.

Siubicasestosarchivosfueradelarazdedocumentosdelservidorweb,nosepodrencontrartuarchivoconun
URL.Entonces,enelcasodequealguienhayadejadolaextensinPHPporerrorohayaechadoaperderlos
permisosdearchivo,elarchivonopodrmostrarseentextoenunaventanadelbuscador.

Elarchivotendrvariablesdeconfiguracinglobal.Aspectoscomosialguienpuederegistrarse,siesunaconexin
(HTTPS)segura,entreotros,ademsdelosdetallesdelabasededatospodraniraeselugar.

<?php
/**
*Estossonlosdetallesdeiniciodesesindelabasededatos:
*/
define("HOST","localhost");//Elalojamientoalquedeseasconectarte
define("USER","sec_user");//Elnombredeusuariodelabasededatos
define("PASSWORD","4Fa98xkHVd2XmnfK");//Lacontraseadelabasededatos
define("DATABASE","secure_login");//Elnombredelabasededatos

define("CAN_REGISTER","any");
define("DEFAULT_ROLE","member");

define("SECURE",FALSE);//SOLOPARADESARROLLAR!!!!

2 Crealapginadeconexinparalabasededatos.

EsteserelcdigoPHPquetendrsqueutilizarparaconectartealabasededatosmySQL.Creaunnuevoarchivo
PHPllamadodb_connect.phpeneldirectoriodeincludesdelaaplicacinyagregaelcdigoacontinuacin.Luego
podrsincluirelarchivoencualquierpginaenlaquedeseesconectarteconlabasededatos.

<?php
include_once'pslconfig.php';//Yaquefunctions.phpnoestincluido.
$mysqli=newmysqli(HOST,USER,PASSWORD,DATABASE);

Parte
CrearfuncionesPHP
4

Estasfuncionesharntodoelprocesamientodelscriptdeconexin.Agregatodaslasfuncionesalapgina
llamadafunctions.phpeneldirectorioincludesdelaaplicacin.

1 IniciademaneraseguralasesinPHP.
LassesionesPHPtienenfamadenosermuyseguras,poresoserimportantenosoloponersession_start()
enlapartesuperiordecadapginaquequierasusarparalassesionesPHP.Crearemosunafuncinllamada
sec_session_start(),ellainiciarunasesinPHPdemanerasegura.Debersllamarestafuncinenlapartesuperior
detodapginaenlaquequierasteneraccesoaunavariabledesesinPHP.Siestsrealmentepreocupadoporla
seguridadylaprivacidaddelascookies,chaleunvistazoalsiguienteartculo:Cmocrearunsistemade
administracindesesinseguraenPHPyMySQL.

Estafuncinharquetuscriptdeiniciodesesinseamuchomsseguro.Harqueloscrackersdejendeaccederal
cookiedeidentificacindelasesinconJavaScript(porejemploenunataqueXSS).Asuvez,lafuncin
session_regenerate_id(),lacualregeneralaidentificacindelasesinencadacargadelapgina,ayudara
prevenirunrobodesesin.Nota:sivasausarHTTPSentuaplicacindeiniciodesesin,configuralavariable
$secureaverdadero.Enunambientedeproduccin,seresencialqueempleesHTTPS.

Creaunnuevoarchivollamadofunctions.phpeneldirectorioincludesdetuaplicacinyagrgaleelcdigoa
continuacin.
<?php
include_once'pslconfig.php';

functionsec_session_start(){
$session_name='sec_session_id';//Configuraunnombredesesinpersonalizado.
$secure=SECURE;
//EstodetienequeJavaScriptseacapazdeaccederalaidentificacindelasesin.
$httponly=true;
//Obligaalassesionesasoloutilizarcookies.
if(ini_set('session.use_only_cookies',1)===FALSE){
header("Location:../error.php?err=Couldnotinitiateasafesession(ini_set)");
exit();
}
//Obtienelosparamsdeloscookiesactuales.
$cookieParams=session_get_cookie_params();
session_set_cookie_params($cookieParams["lifetime"],
$cookieParams["path"],
$cookieParams["domain"],
$secure,
$httponly);
//Configuraelnombredesesinalconfiguradoarriba.
session_name($session_name);
session_start();//InicialasesinPHP.
session_regenerate_id();//Regeneralasesin,borralaprevia.
}

2 Crealafuncindeiniciodesesin.
Estafuncincompararelcorreoelectrnicoylacontraseaconlabasededatosy,sihayunacoincidencia,
aparecercomoverdadero(true).Agregaestafuncinatuarchivofunctions.php:
functionlogin($email,$password,$mysqli){
//UsardeclaracionespreparadassignificaquelainyeccindeSQLnoserposible.
if($stmt=$mysqli>prepare("SELECTid,username,password,salt
FROMmembers
WHEREemail=?
LIMIT1")){
$stmt>bind_param('s',$email);//Une$emailalparmetro.
$stmt>execute();//Ejecutalaconsultapreparada.
$stmt>store_result();

//Obtienelasvariablesdelresultado.
$stmt>bind_result($user_id,$username,$db_password,$salt);
$stmt>fetch();

//Haceelhashdelacontraseaconunasalnica.
$password=hash('sha512',$password.$salt);
if($stmt>num_rows==1){
//Sielusuarioexiste,revisasilacuentaestbloqueada
//pormuchosintentosdeconexin.

if(checkbrute($user_id,$mysqli)==true){
//Lacuentaestbloqueada.
//Envauncorreoelectrnicoalusuarioqueleinformaquesucuentaestbloqueada.
returnfalse;
}else{
//Revisaquelacontraseaenlabasededatoscoincida
//conlacontraseaqueelusuarioenvi.
if($db_password==$password){
//Lacontraseaescorrecta!
//Obtnelagentedeusuariodelusuario.
$user_browser=$_SERVER['HTTP_USER_AGENT'];
//ProteccinXSSyaquepodramosimprimirestevalor.
$user_id=preg_replace("/[^09]+/","",$user_id);
$_SESSION['user_id']=$user_id;
//ProteccinXSSyaquepodramosimprimirestevalor.
$username=preg_replace("/[^azAZ09_\]+/",
"",
$username);
$_SESSION['username']=$username;
$_SESSION['login_string']=hash('sha512',
$password.$user_browser);
//Iniciodesesinexitoso
returntrue;
}else{
//Lacontraseanoescorrecta.
//Segrabaesteintentoenlabasededatos.
$now=time();
$mysqli>query("INSERTINTOlogin_attempts(user_id,time)
VALUES('$user_id','$now')");
returnfalse;
}
}
}else{
//Elusuarionoexiste.
returnfalse;
}
}
}

3 Lafuncindefuerzabruta.
Losataquesdefuerzabrutasedancuandounhackerintentaaccederaunacuentacon1000contraseas
diferentes,yaseangeneradasalazarodeundiccionario.Ennuestrasecuenciadecomandos,silacuentadeun
usuarionoinicialasesindespusdemsde5intentos,sucuentasebloquear.

Losataquesdefuerzabrutasondifcilesdeprevenir,parahacerlopodrasutilizarpruebasdeCAPTCHA,bloquearlas
cuentasdeusuarioyagregarunretrasoenlosiniciosdesesinfallidos,aselusuarionopodraccederporotros30
segundos.

RecomendamosenfticamenteusarunCAPTCHA.Comonohemosimplementadoestafuncionalidadennuestro
cdigoejemplo,esperamoshacerloprximamenteconSecureImage,yaquenorequiereinscripcin.Podraspreferir
algomsconocidocomoreCAPTCHAdeGoogle.

Seacualseaelsistemaquedecidasusar,tesugerimosmostrarlaimagendeCAPTCHAdespusdedosiniciosde
sesinfallidosparaevitarincomodaralusuarioinnecesariamente.

Cuandonosenfrentamosaesteproblema,lamayoradelosdesarrolladoressimplementebloquealadireccinIP
despusdeciertacantidaddeiniciosdesesinfallidos.Sinembargo,existenmuchasherramientasparaautomatizar
elproceso.EstaspuedenpasarporunaseriedeproxieseinclusocambiarlaIPencadasolicitud.Bloqueartodas
estasdireccionesIPpodrabloquearlascuentasdetususuarioslegtimostambin.Ennuestrocdigoregistraremos
losintentosfallidosybloquearemoslacuentadelusuariodespusdecincointentossinxito.Estoharqueseenve
uncorreoelectrnicoalusuarioconunenlacepararesetearlo,peronohemosimplementadoestepuntoennuestro
cdigo.Acontinuacintepresentaremoslafuncincheckbrute()hastalafecha.Agrgaseloatucdigo
functions.php:

functioncheckbrute($user_id,$mysqli){
//Obtieneeltimestampdeltiempoactual.
$now=time();

//Todoslosintentosdeiniciodesesinsecuentandesdelas2horasanteriores.
$valid_attempts=$now(2*60*60);

if($stmt=$mysqli>prepare("SELECTtime
FROMlogin_attempts
WHEREuser_id=?
ANDtime>'$valid_attempts'")){
$stmt>bind_param('i',$user_id);

//Ejecutalaconsultapreparada.
$stmt>execute();
$stmt>store_result();

//Sihahabidomsde5intentosdeiniciodesesinfallidos.
if($stmt>num_rows>5){
returntrue;
}else{
returnfalse;
}
}
}

4 Revisaelestadodelasesininiciada.
Loharemosmediantelacomprobacindeuser_idylasvariablesdesesinlogin_string.LavariableSESSION
login_stringtienelainformacindelnavegadordelosusuariosjuntoconlacontraseaunidamedianteunafuncin
hash.Utilizamoslainformacindelnavegador,yaqueesmuypocoprobablequeelusuariolovayaacambiarala
mitaddelasesin.Hacerloayudaraprevenirunrobodesesin.Agregaestafuncinatuarchivofunctions.phpen
lacarpetaincludesdetuaplicacin:

functionlogin_check($mysqli){
//Revisasitodaslasvariablesdesesinestnconfiguradas.
if(isset($_SESSION['user_id'],
$_SESSION['username'],
$_SESSION['login_string'])){

$user_id=$_SESSION['user_id'];
$login_string=$_SESSION['login_string'];
$username=$_SESSION['username'];

//Obtienelacadenadeagentedeusuariodelusuario.
$user_browser=$_SERVER['HTTP_USER_AGENT'];

if($stmt=$mysqli>prepare("SELECTpassword
FROMmembers
WHEREid=?LIMIT1")){
//Une$user_idalparmetro.
$stmt>bind_param('i',$user_id);
$stmt>execute();//Ejecutalaconsultapreparada.
$stmt>store_result();

if($stmt>num_rows==1){
//Sielusuarioexiste,obtienelasvariablesdelresultado.
$stmt>bind_result($password);
$stmt>fetch();
$login_check=hash('sha512',$password.$user_browser);

if($login_check==$login_string){
//Conectado!!
returntrue;
}else{
//Noconectado.
returnfalse;
}
}else{
//Noconectado.
returnfalse;
}
}else{
//Noconectado.
returnfalse;
}
}else{
//Noconectado.
returnfalse;
}
}

5 SanealaURLdePHP_SELF
EstafuncinsanealasalidadelavariabledelservidorPHP_SELF.Esunamodificacindeunafuncindel
mismonombreusadaporelsistemadegestindecontenidoWordPress:

functionesc_url($url){

if(''==$url){
return$url;
}

$url=preg_replace('|[^az09~+_.?#=!&;,/:%@$\|*\'()\\x80\\xff]|i','',$url);

$strip=array('%0d','%0a','%0D','%0A');
$url=(string)$url;

$count=1;
while($count){
$url=str_replace($strip,'',$url,$count);
}

$url=str_replace(';//','://',$url);

$url=htmlentities($url);

$url=str_replace('&amp;','&#038;',$url);
$url=str_replace("'",'&#039;',$url);

if($url[0]!=='/'){
//Solonosinteresanlosenlacesrelativosde$_SERVER['PHP_SELF']
return'';
}else{
return$url;
}
}

Elproblemadeusarunavariabledeservidornofiltradaesquepodrausarseenunataquedesecuenciasde
comandosensitioscruzados.Segnlamayoradereferencias,solotendrsquefiltrarlaconhtmlentities(),sin
embargo,siguesiendoinsuficiente,poresoexistenexcesivasmedidasdeseguridadparaestafuncin.

Otrossugierendejarenblancoelatributodeaccindelformulariooconfigurarloaunacadenavaca.Perohacerloas
podradarlugaraunataquedesecuestrodecliciframe.

Parte
Crearpginasdeprocesamiento
5

1 Crealapginadeprocesamientodeiniciodesesin(process_login.php)

Creaunarchivoparaprocesarlosiniciosdesesin,conelnombreprocess_login.php,eneldirectorio
includesdelaaplicacin.TendrqueiraestedirectorioporquenotieneningnformatoHTML.

UsaremoslaseriedefuncionesPHPmysqli_*,puestoqueestaesunadelasextensionesmySQLmsactualizadas.

<?php
include_once'db_connect.php';
include_once'functions.php';

sec_session_start();//NuestramanerapersonalizadaseguradeiniciarsesinPHP.

if(isset($_POST['email'],$_POST['p'])){
$email=$_POST['email'];
$password=$_POST['p'];//Lacontraseaconhash

if(login($email,$password,$mysqli)==true){
//Iniciodesesinexitosa
header('Location:../protected_page.php');
}else{
//Iniciodesesinexitosa
header('Location:../index.php?error=1');
}
}else{
//LasvariablesPOSTcorrectasnoseenviaronaestapgina.
echo'Solicitudnovlida';
}

2 Creaunasecuenciadecomandosparacerrarsesin.

Elscriptparaelcierredesesindeberiniciarsesin,destruirlayluegoredireccionarlaaotrolugar.Nota:te
recomendamosaadirunaproteccinCSRFaquenelcasodequealguienlogreenviarunenlaceocultoaesta
pgina.ParamayorinformacinsobreCSRF,visitaCodingHorror.

Elcdigoactualparadesconectaralusuario,elcualdebersagregaralarchivotituladologout.phpeneldirectorio
includesdelaaplicacin,eselsiguiente:

<?php
include_once'includes/functions.php';
sec_session_start();

//Desconfiguratodoslosvaloresdesesin.
$_SESSION=array();

//Obtienelosparmetrosdesesin.
$params=session_get_cookie_params();

//Borraelcookieactual.
setcookie(session_name(),
'',time()42000,
$params["path"],
$params["domain"],
$params["secure"],
$params["httponly"]);

//Destruyesesin.
session_destroy();
header('Location:../index.php');

3 Pginaderegistro.

Debersincluirelcdigoderegistroendosarchivosnuevos,llamadosregister.phpeneldirectoriorazdela
aplicacinyregister.inc.phpeneldirectorioincludes,locualharlosiguiente:
Obtieneyvalidaelnombredeusuarioqueelusuariodeseaadoptar.
Obtieneyvalidaladireccindecorreoelectrnicodelusuario.
Obtieneyvalidalacontraseaqueelusuariodeseausar.
Ponelacontraseaconhashyladevuelvealapginaregister.php(osea,laenvaasmisma).

LamayoradelavalidacinsehaceenJavaScript,delladodelcliente.Estosedebeaqueelusuarionotienela
motivacinparaburlarestasverificaciones.Porququerraelusuariocrearseunacuentaquenoseratansegura?
HablaremosdeJavaScriptenlasiguienteseccin.

Porahora,solotendrsquecrearelarchivoregister.phpeincluyeelcdigoacontinuacin:

<?php
include_once'includes/register.inc.php';
include_once'includes/functions.php';
?>
<!DOCTYPEhtml>
<html>
<head>
<metacharset="UTF8">
<title>SecureLogin:Formularioderegistro</title>
<scripttype="text/JavaScript"src="js/sha512.js"></script>
<scripttype="text/JavaScript"src="js/forms.js"></script>
<linkrel="stylesheet"href="styles/main.css"/>
</head>
<body>
<!FormularioderegistroqueseemitirsilasvariablesPOSTnose
establecenosilasecuenciadecomandosderegistrohaprovocadounerror.>
<h1>Regstrateconnosotros</h1>
<?php
if(!empty($error_msg)){
echo$error_msg;
}
?>
<ul>
<li>Losnombresdeusuariopodrancontenersolodgitos,letrasmaysculas,minsculasyguionesbajos.</li>
<li>Loscorreoselectrnicosdeberntenerunformatovlido.</li>
<li>Lascontraseasdebernteneralmenos6caracteres.</li>
<li>Lascontraseasdebernestarcompuestaspor:
<ul>
<li>Porlomenosunaletramayscula(AZ)</li>
<li>Porlomenosunaletraminscula(az)</li>
<li>Porlomenosunnmero(09)</li>
</ul>
</li>
<li>Lacontraseaylaconfirmacindeberncoincidirexactamente.</li>
</ul>
<formaction="<?phpechoesc_url($_SERVER['PHP_SELF']);?>"
method="post"
name="registration_form">
Nombredeusuario:<inputtype='text'
name='username'
id='username'/><br>
Correoelectrnico:<inputtype="text"name="email"id="email"/><br>
Contrasea:<inputtype="password"
name="password"
id="password"/><br>
Confirmarcontrasea:<inputtype="password"
name="confirmpwd"
id="confirmpwd"/><br>
<inputtype="button"
value="Register"
onclick="returnregformhash(this.form,
this.form.username,
this.form.email,
this.form.password,
this.form.confirmpwd);"/>
</form>
<p>Returntothe<ahref="index.php">loginpage</a>.</p>
</body>
</html>

Elarchivoregister.inc.phpeneldirectorioincludesdebertenerelcdigoacontinuacin:

<?php
include_once'db_connect.php';
include_once'pslconfig.php';

$error_msg="";

if(isset($_POST['username'],$_POST['email'],$_POST['p'])){
//Sanearyvalidarlosdatosprovistos.
$username=filter_input(INPUT_POST,'username',FILTER_SANITIZE_STRING);
$email=filter_input(INPUT_POST,'email',FILTER_SANITIZE_EMAIL);
$email=filter_var($email,FILTER_VALIDATE_EMAIL);
if(!filter_var($email,FILTER_VALIDATE_EMAIL)){
//Noesuncorreoelectrnicovlido.
$error_msg.='<pclass="error">Theemailaddressyouenteredisnotvalid</p>';
}

$password=filter_input(INPUT_POST,'p',FILTER_SANITIZE_STRING);
if(strlen($password)!=128){
//Lacontraseaconhashdeberserde128caracteres.
//Delocontrario,algomuyrarohabrsucedido.
$error_msg.='<pclass="error">Invalidpasswordconfiguration.</p>';
}

//Lavalidezdelnombredeusuarioydelacontraseahasidoverificadaenelcliente.
//Estosersuficiente,yaquenadiesebeneficiarde
//violarestasreglas.
//

$prep_stmt="SELECTidFROMmembersWHEREemail=?LIMIT1";
$stmt=$mysqli>prepare($prep_stmt);

//Verificaelcorreoelectrnicoexistente.
if($stmt){
$stmt>bind_param('s',$email);
$stmt>execute();
$stmt>store_result();

if($stmt>num_rows==1){
//Yaexisteotrousuarioconestecorreoelectrnico.
$error_msg.='<pclass="error">Auserwiththisemailaddressalreadyexists.</p>';
$stmt>close();
}
$stmt>close();
}else{
$error_msg.='<pclass="error">DatabaseerrorLine39</p>';
$stmt>close();
}

//Verificaelnombredeusuarioexistente.
$prep_stmt="SELECTidFROMmembersWHEREusername=?LIMIT1";
$stmt=$mysqli>prepare($prep_stmt);

if($stmt){
$stmt>bind_param('s',$username);
$stmt>execute();
$stmt>store_result();

if($stmt>num_rows==1){
//Yaexisteotrousuarioconestenombredeusuario.
$error_msg.='<pclass="error">Auserwiththisusernamealreadyexists</p>';
$stmt>close();
}
$stmt>close();
}else{
$error_msg.='<pclass="error">Databaseerrorline55</p>';
$stmt>close();
}

//Pendiente:
//Tambinhabrquetenerencuentalasituacinenlaqueelusuarionotenga
//derechospararegistrarse,alverificarqutipodeusuariointenta
//realizarlaoperacin.

if(empty($error_msg)){
//Crearunasalaleatoria.
//$random_salt=hash('sha512',uniqid(openssl_random_pseudo_bytes(16),TRUE));//Didnotwork
$random_salt=hash('sha512',uniqid(mt_rand(1,mt_getrandmax()),true));

//Creaunacontraseaconsal.
$password=hash('sha512',$password.$random_salt);

//Insertaelnuevousuarioalabasededatos.
if($insert_stmt=$mysqli>prepare("INSERTINTOmembers(username,email,password,salt)VALUES(?,?,?,?)"
$insert_stmt>bind_param('ssss',$username,$email,$password,$random_salt);
//Ejecutalaconsultapreparada.
if(!$insert_stmt>execute()){
header('Location:../error.php?err=Registrationfailure:INSERT');
}
}
header('Location:./register_success.php');
}
}

SinohayningndatoPOSTprovistoalformulario,semostrarelformularioderegistro.Elbotndeenvodel
formulariollamaalafuncindeJavaScriptregformhash().Estafuncinrealizarlasverificacionesdevalidacin
necesariasyenviarelformulariocuandotodoseacorrecto.HablaremosdelasfuncionesdeJavaScriptenla
siguienteseccin.

SiexistendatosPOST,serealizarnalgunasverificacionesdelservidorparasanearlosyvalidarlos.TENPRESENTE
queestasverificacionesnoestnculminadashastalafecha.Algunosdelosproblemassemencionanenlos
comentariosenelarchivo.Alafecha,soloverificamosqueladireccindecorreoelectrnicotengaelformatocorrecto,
quelacontraseaconhashtengalaextensincorrectayqueelusuarionotratederegistraruncorreoelectrnicoya
registrado.

Sitodoescorrecto,seregistraralnuevousuarioyseescribirunregistronuevoenlatablademiembros.

Parte
CreararchivosdeJavascript
6

1 Creaelarchivosha512.js.

EstearchivoesunaimplementacinenJavaScriptdelalgoritmohashsha512.Haremosusodelafuncinhashpara
quelascontraseasnoseenvenentextosimple.

Podrsdescargarelarchivodepajhome.org.uk

(tambinseguardarenelrepositoriodegithub).

Guardatucopiadeestearchivoenundirectoriotituladojseneldirectorioderazdelaaplicacin.

2 Creaelarchivoforms.js.
Estearchivo,elcualdeberscreareneldirectoriojsdelaaplicacin,seencargardelhashdelascontraseas
paralosformulariosdeiniciodesesin(formhash())yderegistro(regformhash()):

functionformhash(form,password){
//Creaunaentradadeelementonuevo,estasernuestrocampodecontraseaconhash.
varp=document.createElement("input");

//Agregaelelementonuevoanuestroformulario.
form.appendChild(p);
p.name="p";
p.type="hidden";
p.value=hex_sha512(password.value);

//Asegratedequelacontraseaentextosimplenoseenve.
password.value="";

//Finalmenteenvaelformulario.
form.submit();
}

functionregformhash(form,uid,email,password,conf){
//Verificaquecadacampotengaunvalor
if(uid.value==''||
email.value==''||
password.value==''||
conf.value==''){

alert('Deberbrindartodalainformacinsolicitada.Porfavor,intentedenuevo');
returnfalse;
}

//Verificaelnombredeusuario

re=/^\w+$/;
if(!re.test(form.username.value)){
alert("Elnombredeusuariodebercontenersololetras,nmerosyguionesbajos.Porfavor,intntelodenuevo"
form.username.focus();
returnfalse;
}

//Verificaquelacontraseatengalaextensincorrecta(mn.6caracteres)
//Laverificacinseduplicaacontinuacin,peroseincluyeparaqueel
//usuariotengaunaguamsespecfica.
if(password.value.length<6){
alert('Lacontraseadeberteneralmenos6caracteres.Porfavor,intntelodenuevo');
form.password.focus();
returnfalse;
}

//Porlomenosunnmero,unaletraminsculayunamayscula
//Almenos6caracteres

varre=/(?=.*\d)(?=.*[az])(?=.*[AZ]).{6,}/;
if(!re.test(password.value)){
alert('Lascontraseasdebernconteneralmenosunnmero,unaletraminsculayunamayscula.Porfavor,intntelodenuevo
returnfalse;
}

//Verificaquelacontraseaylaconfirmacinseaniguales
if(password.value!=conf.value){
alert('Lacontraseaylaconfirmacinnocoinciden.Porfavor,intntelodenuevo');
form.password.focus();
returnfalse;
}

//Creaunaentradadeelementonuevo,estasernuestrocampodecontraseaconhash.
varp=document.createElement("input");

//Agregaelelementonuevoanuestroformulario.
form.appendChild(p);
p.name="p";
p.type="hidden";
p.value=hex_sha512(password.value);

//Asegratedequelacontraseaentextosimplenoseenve.
password.value="";
conf.value="";

//Finalmenteenvaelformulario.
form.submit();
returntrue;
}

Enamboscasos,JavaScriptleponeunhashalacontraseaylatransfierealosdatosPOSTalcrearyrellenarun
campoescondido.

Parte
CrearpginasHTML
7

1 Creaelformulariodeiniciodesesin(login.php).

EstaesunaformadeHTMLcondoscamposdetexto,tituladoscorreoelectrnicoycontrasea.Elbotndeenvo
delformulariollamaralafuncindeJavaScriptformhash(),lacualgenerarunacontraseaconhashyenviarel
contenidodecorreoelectrnicoyp(contraseaconhash)alservidor.Deberscrearestearchivoeneldirectorio
derazdelaaplicacin.

Aliniciarlasesin,lomsrecomendableserutilizaralgoquenoseapblico.Enlapresenteguausaremoselcorreo
electrnicocomoIDdeiniciodesesin,peroelnombredeusuariopodrutilizarsedespusparaidentificaralusuario.
Sielcorreoelectrnicoseocultaenalgunapginadentrodelaaplicacinmsamplia,seaadirotravariable
desconocidaparacrackearlascuentas.

Nota:peseaquehemosencriptadolacontraseademodoquenoseenveentextosimple,servitalqueusesel
protocoloHTTPS(TLS/SSL)alahoradeenviarlascontraseasaunsistemadeproduccin.Noestpordems
insistirenquesimplementeponerunhashalacontraseaserinsuficiente.Podrassufrirunataquemaninthe
middlequepodraleerelhashenviadoyusarloparainiciarsesin.

<?php
include_once'includes/db_connect.php';
include_once'includes/functions.php';

sec_session_start();

if(login_check($mysqli)==true){
$logged='in';
}else{
$logged='out';
}
?>
<!DOCTYPEhtml>
<html>
<head>
<title>SecureLogin:LogIn</title>
<linkrel="stylesheet"href="styles/main.css"/>
<scripttype="text/JavaScript"src="js/sha512.js"></script>
<scripttype="text/JavaScript"src="js/forms.js"></script>
</head>
<body>
<?php
if(isset($_GET['error'])){
echo'<pclass="error">ErrorLoggingIn!</p>';
}
?>
<formaction="includes/process_login.php"method="post"name="login_form">
Correoelectrnico:<inputtype="text"name="email"/>
Contrasea:<inputtype="password"
name="password"
id="password"/>
<inputtype="button"
value="Login"
onclick="formhash(this.form,this.form.password);"/>
</form>
<p>Sinotieneunacuenta,porfavor<ahref="register.php">regstrese.</a></p>
<p>Sihaterminado,porfavor<ahref="includes/logout.php">cierrelasesin.</a></p>
<p>Estconectado.<?phpecho$logged?>.</p>
</body>
</html>

2 Crealapginaregister_success.php.

CreaunapginawebPHPnuevaquellevepornombreregister_success.phpeneldirectoriorazdelaaplicacin.
Estaeslapginaadondeseleredireccionaralusuariodespusdehaberseregistradoconxito.Claroquepodrs
hacerestapginacomoquierasopodrsredireccionarloaotrapgina(ono).Dependerdeti.Debersubicarla
pginaeneldirectoriorazdelaaplicacin.Lapginaactualregister_success.phpquehemosescritoseveas:

<!DOCTYPEhtml>
<html>
<head>
<metacharset="UTF8">
<title>Iniciodesesinsegura:Registroexitoso</title>
<linkrel="stylesheet"href="styles/main.css"/>
</head>
<body>
<h1>Registroexitoso!</h1>
<p>Ahorapodrsregresara<ahref="index.php">lapginadeiniciodesesin</a>einiciarlasesin.</p>
</body>
</html>

3 Crealapginadeerror.

CreaunapginaHTMLnuevaeneldirectoriorazdelaaplicacinyponlepornombreerror.php.Estaeslapginaa
dondeseleredireccionaralusuarioenelcasodequeocurraalgnerrorduranteelprocesodeiniciodelasesin,de
registroocuandosetratedeestablecerunasesinsegura.Elcdigoacontinuacinsimplementemostraruna
pginadeerrorgeneral.Talveznecesitesalgounpocomssofisticado.Noobstante,tenpresentequetodoloquese
agreguealapginasedeberfiltraradecuadamenteparaprotegersecontraposiblesataquesXSS.Elcdigode
ejemplodelapginaeselsiguiente:

<?php
$error=filter_input(INPUT_GET,'err',$filter=FILTER_SANITIZE_STRING);

if(!$error){
$error=Ocurriunerrordesconocido;
}
?>
<!DOCTYPEhtml>
<html>
<head>
<metacharset="UTF8">
<title>SecureLogin:Error</title>
<linkrel="stylesheet"href="styles/main.css"/>
</head>
<body>
<h1>Hubounproblema.</h1>
<pclass="error"><?phpecho$error;?></p>
</body>
</html>

Parte
Protegerpginas
8

1
1 Secuenciadecomandosparalaproteccindepginas.

Unodelosproblemasmscomunesconlossistemasdeautenticacinesqueeldesarrolladorolvidaverificarsiel
usuarioestconectado.Serdesumaimportanciaqueempleeselcdigoacontinuacinencadapginaprotegida
paraverificarqueelusuarioestconectado.Asegratedeemplearestafuncin.

//Agregalaconexinylasfuncionesdelabasededatosaqu.Ver3.1.
sec_session_start();
if(login_check($mysqli)==true){
//Agregaelcontenidodetupginaprotegidaaqu!
}else{
echoNoestautorizadoparaaccederaestapgina.Porfavor,iniciesusesin.;
}


Comoejemplodeloquedebershacer,hemosincluidounapginaprotegidademuestra.Creaunarchivocon
nombreprotected_page.phpeneldirectoriorazdelaaplicacin.Elarchivodeberserparecidoaloque
mostraremosacontinuacin:

<?php
include_once'includes/db_connect.php';
include_once'includes/functions.php';

sec_session_start();
?>
<!DOCTYPEhtml>
<html>
<head>
<metacharset="UTF8">
<title>Iniciodesesinsegura:Pginaprotegida</title>
<linkrel="stylesheet"href="styles/main.css"/>
</head>
<body>
<?phpif(login_check($mysqli)==true):?>
<p>Bienvenido,<?phpechohtmlentities($_SESSION['username']);?>!</p>
<p>
Esteesunejemplodepginaprotegida.Paraaccederaestapgina,losusuarios
deberniniciarsusesin.Enalgnmomento,tambinverificaremoselrol
delusuarioparaquelaspginaspuedandeterminareltipodeusuario
autorizadoparaaccederalapgina.
</p>
<p>Regresarala<ahref="index.php">pginadeiniciodesesin.</a></p>
<?phpelse:?>
<p>
<spanclass="error">Noestautorizadoparaaccederaestapgina.</span>Please<ahref="index.php">login</a>.
</p>
<?phpendif;?>
</body>
</html>


Nuestraaplicacinredireccionaralusuarioaestapginatrashaberseregistradoconxito.Evidentemente,tu
implementacinnotendrqueserigual.

Consejos

Aljatedelafuncinmd5enlosscriptsdeiniciodesesin,elalgoritmohashmd5seconsiderainseguro.
ConmuypocoscambiosenestosscriptsdeejemplospodrstrabajarconotrossistemasSQL,talescomoSQLiteo
PostgreSQL.
UsaHTMLyCSSparadarformatoalformulariodeaccesoydesalidadelaspginasdetuagrado.
Siquieresusarunalgoritmohashdiferenteenlugardesha512,pruebaWhirlpool.EvitausarGost,sha1(amenosqueest
biensaladoytengavariasiteraciones)y,comoyalohemosmencionado,md5.Incentivaatususuariosacrearcontraseas
nicas,seguras,conletrasmaysculas,minsculas,nmerosysmbolos.Consideralaposibilidaddepedirlesatususuarios
crearunnombredeiniciodesesinapartedesunombredeusuarioparaqueseamsseguro.

Advertencias

LapginadeinicioyderegistrodebernemplearHTTPS.Losscriptsdelpresenteartculonoteobliganahacerloy,en
realidad,seramsfcilnohacerloduranteeldesarrollo,peronodebersusarestosscriptsenunambientedeproduccina
menosqueutilicesHTTPS.
AsegratedequeelusuarionopuedavertusscriptsPHP,locualpodraocurrirdebidoaunaconfiguracinincorrectadel
servidor.Existelaposibilidaddequelosusuariosrecojaninformacinacercadetubasededatoscomolosnombresy
contraseassitucdigoPHPesvisible.Loidealseraquetodaslassecuenciasdecomandosincluidasenotrassecuenciaso
pginasestnubicadasenundirectoriofueradelsistemadearchivosdelservidoryquesehagareferenciaaellasconun
caminorelativo,porejemplo,agrega:../../includes/myscript.inc.php.
Nadaes100%seguro.Recuerdamantenertealtantodelasltimasnoticiasdeseguridadparaseguirmejorandolaseguridad
detusscripts.
Estasecuenciadecomandosantifuerzabrutaquebloquealacuentadeunusuariopodraemplearsedemaneraerrneamuy
fcilmente.TerecomendamosfirmementeusarunatcnicaantifuerzabrutacomoCAPTCHA.
TerecomendamosusarunCAPTCHAenlapginadeiniciodesesinparadificultarlosataquesdefuerzabrutayDoS.El
CAPTCHAdeberaparecerenelformulariodespusdedosintentosdeiniciodesesinfallidos,aunquetodavanoest
implementadoenelcdigodeejemplo.
PodrasconseguirunamejorsolucinconunmarcocomoZend2,SymfonyoCakePHP.Todosestosmarcostienenarreglos
paralassesionessegurasymdulosdeseguridadparaayudarconelprocesodeiniciodesesin.Tambin,siutilizasun
marco,probablementeveasqueescribesmejoresaplicaciones.

Referencias

http://crackstation.net/hashingsecurity.htmHashdecontrasea
https://www.owasp.org/index.php/SQL_InjectionInformacinsobreinyeccinSQL

You might also like