Professional Documents
Culture Documents
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('&','&',$url);
$url=str_replace("'",''',$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