02. juni 2009 - 13:42Der er
20 kommentarer og 1 løsning
Hacker-sikring i PHP
Jeg har en CMS-hjemmeside som jeg selv har programmeret i PHP/MySQL, som nu er blevet hacked 3 gange. Jeg antager at hackeren kommer ind via login til administrationen, men ved ikke rigtig, hvad jeg kan gøre ved det. Jeg har et sessions-baseret login system med md5-kryptering af passwordet. Nogle der har forslag til, hvad jeg kan udbygge med, så mit system bliver mere sikkert?
husker du at escape specialtegn i passwordet? hvis jeg prøver at logge på med administrator som bruger og password: ' or 1 vil det kunne give sql: SELECT * WHERE user='administrator' AND password='' or 1'
Det vil returnere true da password måske ikke er true, men 1 er !!
Ellers bliver du nok nød til at smide noget kode i pastebin for at kunne hjælpe
"Forudsat at man er paa PHP 5 er det bedre at bruge mysqli eller PDO med prepared statements fremfor den gammeldags maade med escape."
Det er ihvertfald en del lettere, men man skal stadigvæk sanitere strengene for at undgå HTML og script injections.
Jeg vil også lige tilføje at mere sikkert og mindre sikkert er ikke termer man kan bruge når det drejer sig om IT sikkerhed. Enten er det sikkert, eller også er der et hul. Det er vigtigt at forstå at sikkerhed er et udtryk for mangelen af tilstedeværelsen af sikkerhedshuller. Som sådan er sikkerhed et luftigt begreb som man ikke rigtigt kan tale om, men sikkerhedshuller er helt konkrete, og det er dem man skal koncentrere sig om.
Synes godt om
Slettet bruger
02. juni 2009 - 18:06#5
Hej alle Tak for jeres indlæg indtil nu. Det er nok nemmere at komme med feedback, hvis i kan se koden som i også efterlyser, så den er her :
session_save_path("sessions"); session_start(); if (isset($_POST['logind'])) { $dopass = md5($_POST['password']); $db = mysql_connect("a", "b", "c") or die ("Desværre ingen forbindelse til databasen"); mysql_select_db("database", $db) or die ("Kunne ikke vælge databasetabel"); $tjek = mysql_query("SELECT brugernavn, password, navn FROM brugere WHERE brugernavn='$_POST[brugernavn]' AND password='$dopass'") or die ("Kunne ikke vælge felter i database-tabellen"); if(mysql_num_rows($tjek) != 0) { $_SESSION['sessionnr'] = session_id(); $_SESSION['Name'] = $_POST['brugernavn']; header("Location:start.php"); exit; } else { header("Location:index.php?error=fejl"); exit; } }
Hvad er "MD5 uden salt"? Det er ikke meget ved at få konstateret, at databasen er sårbar overfor angreb, det har jeg jo lissom erfaret. Jeg var mere ude efter noget hjælp til at få rettet op på fejlene og lukket hullerne... Nu kan jeg så konkludere, at jeg skal gøre noget ved SQL injection,men det kunne være, at der var noget herudover, jeg skulle tage højde for?! Som ebusiness skriver : "sikkerhedshuller er helt konkrete, og det er dem man skal koncentrere sig om", og det er disse huller, jeg gerne vil have lukket...
Jep, sårbar overfor SQL injections. Nøgleordet er "Sanitize" dit input, dvs. tjek om der eksisterer uhensigtsmæssige tegn eller ej (eks. ville du aldrig accepterer en e-mail-adresse uden snabela). Du "sanitizer" som sådan også med dit kodeord. Du manglede bare dit brugernavn.
Derfor kan du f.eks. gøre: $brugernavn = mysql_escape_string($_POST[brugernavn]); $dopass = md5($_POST['password']); $tjek = mysql_query("SELECT brugernavn, password, navn FROM brugere WHERE brugernavn='$brugernavn' AND password='$dopass'") or die ("Kunne ikke vælge felter i database-tabellen");
Tjek i øvrigt kommentarerne på php's md5 funktion: http://dk2.php.net/md5 Her anbefales det bl.a. at salte sin md5 funktion ved f.eks.: $pwd = md5($salt1.$password.$salt2)
eller blot bruge sha1 i stedet for.
Synes godt om
Slettet bruger
03. juni 2009 - 13:27#9
mrgumble : Vil det sige at hvis jeg bare krypterer med sha1 og sanitizer mit brugernavn som ovenfor, så er hullerne lukket?
Såvidt jeg kan se, er PDO en database-php klasse, der gør det lettere at arbejde med databaserne i php.
Det store, gabende hul i dit script er, at du blindt accepterer input som brugernavnet. Sanitizer du brugernavnet, er dét hul lappet (men nogen kan sikkert godt finde igennem). Mht. md5 og salt, så er det du gør OK. Men det giver ikke seriøse problemer for hackere; se dette indlæg: http://pbeblog.wordpress.com/2008/02/12/secure-hashes-in-php-using-salt/ Hvis du bruger sha1 i stedet for md5, skal du huske, at dit databasefelt skal have plads til 160 tegn i stedet for 128.
Men absolut hacker-sikring er enten sisofys arbejde (det er aldrig nok) eller også må sin server ikke have forbindelse til noget netværk. Eller interface...
"MD5 uden salt" er naar du kalder md5 paa password uden salt.
Du har ikek skrevet noget om at din database er saarbar. Indtil videre antager vi at det er din applikation som er saarbar.
Database saarbarheden viser sig hvis nogen faar fat i en kopi af din database og via et dictionary attack finder passwords og bruger dem til at bryde ind paa andre sites hvor dine brugere har brugt samme password.
Synes godt om
Slettet bruger
03. juni 2009 - 22:57#12
Nu har jeg fået læst lidt om de forskellige ting og kodet lidt på login'et. Er dette bedre eller er der stadig huller, som jeg skal gøre noget ved? Eller eventuelt andre små forbedringer, jeg kan lave uden at skulle kode det hele om?
session_save_path("sessions"); session_start(); if (isset($_POST['logind'])) { $salt1="nogetvilkaarligtekst"; $salt2="nogetmerevilkaarligtekst"; $sukker1 = md5($salt1); $sukker2 = md5($salt2); $peber = sha1($_POST['password']); $brugernavn = mysql_escape_string($_POST[brugernavn]); $db = mysql_connect("a", "b", "c") or die ("Desværre ingen forbindelse til databasen"); mysql_select_db("database", $db) or die ("Kunne ikke vælge databasetabel"); $tjek = mysql_query("SELECT brugernavn, password, navn FROM brugere WHERE brugernavn='$brugernavn' AND password='$sukker1$peber$sukker2'") or die ("Kunne ikke vælge felter i database-tabellen"); if(mysql_num_rows($tjek) != 0) { $_SESSION['sessionnr'] = session_id(); $_SESSION['Name'] = $_POST['brugernavn']; header("Location:start.php"); exit; } else { header("Location:index.php?error=fejl"); exit; } }
Næsten. Jeg kan godt lide din salt, sukker and spice (peber). ;o)
Uanset hvad du skriver i $salt1 og $salt2 bliver $sukker1 og $sukker2 hver en 32 tegn lang streng, som f.eks. "1ab2....cf68" og "7d8e....c8f9" (det er hexadecimalt). Sha1($password) bliver til et 40 tegn lang streng.
Når du indsætter det i din sql-sætning som password='$sukker1$peber$sukker2'" bliver resultatet altid password='1ab2....cf68xxxx7d8e....c8f9", hvor xxxx svarer til sha1-hashet af dit kodeord. Dvs. i din database vil alle kodeord stå som: 1ab2....cf68xxxx7d8e....c8f9 1ab2....cf68yyyy7d8e....c8f9 1ab2....cf68zzzz7d8e....c8f9
Det er grunden til, at dit kodeord skal hashes sammen med dine salts; om dine salts bliver hashet hver for sig er af mindre betydning.
Derfor skal du rette din kode til: $salt1="nogetvilkaarligtekst"; $salt2="nogetmerevilkaarligtekst"; $sukker1 = md5($salt1); $sukker2 = md5($salt2); $spice = sha1($sukker1.$_POST['password'].$sukker2); ... $tjek = mysql_query("SELECT brugernavn, password, navn FROM brugere WHERE brugernavn='$brugernavn' AND password='$spice'") or die ("Kunne ikke vælge felter i database-tabellen");
Derudover skal du huske at ændre din database, så der er plads til 32 eller 40 tegn (hhv. md5 og sha1) og sørge for, at indsatte kodeord bliver hashet sammen med saltene. Ellers bliver de ikke genkendt. :)
Synes godt om
Slettet bruger
05. juni 2009 - 11:55#15
mrgumble: Kan du ikke vise mig hvordan jeg så får oprettet en bruger tilsvarende for det kan jeg ikke få til at virke?! Min kode er :
mysql_query("INSERT INTO brugere (navn, brugernavn, password) VALUES ('$navnfelt','$brugernavnfelt', '$spice')"); }
Det giver nøjagtig den samme tekststreng for password i DB uanset hvilket password, jeg bruger og jeg kan ikke få lov at logge ind med passwordet efterfølgende, så noget er der galt, men hvad...? Og kan du sige, hvad der er den bedste måde at gemme passwordet i DB. Er det varchar(40) eller text?
1. Du skal passe på med at pakke $_POST ud med extract; her kan man også snige nogle variabler ind (hvis man er kyndig). Derfor skal du altid huske at deklarere dine variabler! (Det fremgår ikke af din kode, at du ikke gør det.)
2. Indholdet i $hash vil altid være '$passwordfelt'. Hvorfor lave tricket $hash = '$passwordfelt';?? Det kan jeg ikke gennemskue. Men jeg kan til gengæld sige dig hvad der går galt: Enkelte apostrofer (') fortolker ikke strengen. Så '$passwordfelt' opfattes som '$passwordfelt', uanset hvad variablen indeholder. Derimod fortolkes gåseøjne (") med alle variabler.
Synes godt om
Slettet bruger
05. juni 2009 - 12:13#17
arne_v : Kan du sige mig, hvordan det kan være, at mysql_real_escape_string ikke virker ved login, men mysql_escape_string gør? Har det noget med php-versioner at gøre?
Synes godt om
Slettet bruger
05. juni 2009 - 12:16#18
mrgumble: Jeg er altså ikke supergod til PHP (hvis det ikke fremgår allerede :-)) Kan du komme med et bud på hvordan min kode skal se ud hvis jeg ikke bruger extract?
mysql_query("INSERT INTO brugere (navn, brugernavn, password) VALUES ('$navnfelt','$brugernavnfelt', '$spice')"); }
Og så syntes jeg du skal lukke tråden og tildele point. Hvis du har flere spørgsmål, må du oprette en ny tråd med det specifikke spørgsmål.
/Stefan
Synes godt om
Slettet bruger
05. juni 2009 - 12:52#20
Ok, vi lukker her. Det er sådan et spørgsmål som kan føre en i alle mulige retninger, så beklager hvis det har taget overhånd. Tusind tak for hjælpen, nu er jeg ihvertfald blevet lidt klogere på, hvad der skal til for at sikre sig mod vores hacker-venner :-)
Jeg vil ige tilføje at du mangler garanteret stadigvæk sikring af en masse andet kode.
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.