[PHP] Map beveiligen met php sessions

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
Beste medetweakers,

Na lang zoeken kan ik niet echt een geschikt antwoord vinden op mijn 'probleem'.
Ik zal proberen het zo kort en duidelijk uit te leggen:

Ik heb een inlogscript op basis van php sessions.
Elke user heeft een eigen map, wij nemen als voorbeeld even user 'tweaker'.
In het mapje /tweaker/ staat een index.php die dus beveiligd is met een sessie.
Dit werkt uitstekend, en elk php bestand kan ik beveiligen door middel van een sessie variable check.
Echter staat in het mapje /tweaker/ ook een mapje 'images'.
In dit mapje staan, logisch, plaatjes die de index.php opvraagt en toont aan de user.

Echter wil ik het volgende:
Niemand anders mag de plaatjes uit /tweaker/images/ kunnen opvragen.
Dus als ik www.domeinnaam.nl/tweaker/images/plaatje1.jpg in mijn browser in tik mag het plaatje niet zichtbaar worden.
Ook user 'tweaker' hoeft dit niet te kunnen maar mag wel zolang de sessie loopt.

Via .htaccess kan ik wel de bestanden beveiligen met Order deny, allow Deny from all maar dan kan de index.php ze ook niet meer tonen.
Misschien kan PHP ook de map tijdelijk toegankelijk maken, maar dan heeft in principe iedereen toegang.

Wie weet raad? Is er misschien iets simpels om alleen de /tweaker/index.php toegang te geven tot de map en de rest te blokken? Alvast bedankt!

Acties:
  • +1 Henk 'm!

  • DukeBox
  • Registratie: April 2000
  • Laatst online: 11:51

DukeBox

loves wheat smoothies

Lijk mij makkelijker dat via een rewrite rule op te lossen.. hoe dan ook sessions gebruik je als authenticatie, niet als authorisatie !

Duct tape can't fix stupid, but it can muffle the sound.


Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
Bedankt voor je antwoord!

Ik ben niet thuis in de rewrite rule maar ik zal mij daar in verdiepen.

Het verschil tussen authenticatie en authorisatie pak ik niet helemaal.
Wil je zeggen dat het bad practice is om via $_SESSION['logged_in'] == true; een user toegang tot een bestand te geven?

[ Voor 14% gewijzigd door michaelboon82 op 18-03-2017 20:27 ]


Acties:
  • 0 Henk 'm!

  • DukeBox
  • Registratie: April 2000
  • Laatst online: 11:51

DukeBox

loves wheat smoothies

michaelboon82 schreef op zaterdag 18 maart 2017 @ 20:24:
Wil je zeggen dat het bad practice is om via $_SESSION['logged_in'] == true; een user toegang tot een bestand te geven?
Ja, dat is exact wat ik bedoel.
Authorisatie doe je niet in een cookie, maar in de backend.
Anders kan je zo de cookie waar je geen toegang mee hebt aanpassen naar 'true' en klaar.
Authenticatie kan je wel met maar dien je bij iedere interactie weer te valideren.

Duct tape can't fix stupid, but it can muffle the sound.


Acties:
  • 0 Henk 'm!

  • Ruubster
  • Registratie: Augustus 2008
  • Niet online
Pfoe, dat is een lastige. Ik zou zeggen: Via htaccess en een rewriterule ja. Maar dat kan wel intensief worden. Afbeeldingen op een webserver worden normaal niet geparsed via PHP, jouw browser vraagt gewoon het plaatje op (bv: www.iets.nl/tweakers/afb.jpg) en de webserver stuurt deze terug. Daar wordt PHP niet voor gebruikt.

Dusss als je dit via PHP wil beveiligen, zou ik zeggen: Via een htaccess rewriterule kan je zeggen dat alles achter /tweakers/*.jpg via een PHP-script loopt, waarbij je in het PHP-script weer de *.jpg en de sessie controleert, het bestand laadt via PHP en dit terugstuurt. Bedenk ook dat je liever je bestanden buiten je webroot (publieke map) wilt hebben, zodat ze in ieder geval vrijwel nooit van buiten te benaderen zijn.

Maar dat is wel een smerige oplossing, lijkt me. Ligt er geheel aan hoe vaak een bestand wordt opgeroepen, want naast dat je dus de authenticatie per bestand gaat doen, moet elk bestand ook ingeladen worden in PHP en verzonden worden. Daar is PHP nou niet de beste en efficiëntste oplossing voor. Andere oplossingen zijn er, maar zijn wel helemaal afhankelijk van hoe je alles neerzet, wat je doel is, hoe het zit met kosten enzovoort. Ik denk dat je bijvoorbeeld Amazon S3 zou kunnen gebruiken voor enige beveiliging.

Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
Volgens mij autoriseer ik in de backend, omdat ik php een user/wachtwoord laat vergelijken met in de database opgeslagen wachtwoorden.

Maar voor de duidelijkheid hier mijn code:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if ($_SERVER['REQUEST_METHOD'] == 'POST'){
  if ((!preg_match('/^[a-z0-9]+$/', $_POST['user'])) || (!preg_match('/^[a-z0-9]+$/', $_POST['pass']))){
    //Ongeldig gebruik tekens
  }else{
    $stmt = $db->prepare("SELECT * FROM users WHERE name = ?");
    $stmt->execute([$_POST['user']]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    if (password_verify(trim($_POST['pass']), $row['pass'])) {
      if ($_SESSION['csrf_code'] == $_POST['csrf_code']){
        $_SESSION['logged_in'] = true; 
        $_SESSION['gebruiker'] = $row['name']; 
        //inloggen gelukt!
      }else{
        //csrf mismatch
      }
    }else{
      //inloggen mislukt
    }
  }
}


En het beveiligd bestand:
PHP:
1
2
3
4
5
6
if(!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] == false){ 
    header('Location: login_form.php'); 
    exit(); 
}else{
  //welkom 
}


Als ik iets stoms doe hoor ik dat graag maar na vele tutorials doorgenomen te hebben dacht ik echt dat ik php sessies goed gebruik.

@Ruubster
Jammer dat het een moeilijke is.
Ik dacht dat het iets kleins was als in de .htaccess file te zetten:
Deny all except /tweaker/index.php O-)
Daar hoopte ik in ieder geval op.
In de map /tweaker/images/ kunnen een stuk of 50 bestanden staan die de index.php op het scherm wil toveren. Voor de rest mogen ze door niemand die de direct path weet te bekijken zijn.

Dit alles is maar een wens en het gaat zeker niet om supergeheime bestanden.
Mijn doel is om te proberen alles zo secure mogelijk te krijgen wat in mijn eigen macht en Notepad++ mogelijkheden ligt :+
Oftewel het mag mijzelf tijd kosten maar er hoeven geen bedrijfsmatige oplossingen aan te pas komen.

[ Voor 22% gewijzigd door michaelboon82 op 18-03-2017 21:02 ]


Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Voorbeeld code, moet je verder uitwerken en beveiligen!!!!!
code:
1
RewriteRule (.+(?:jpeg|png|jpg)) image.php?img=$1 [L]

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (empty($_SESSION['logged_in']))
{ 
    header('Location: login_form.php');
}
else if (empty($_GET['img'])
 || false !== strpos($_GET['img'], '..')
 || !preg_match('#.+(jpe?g|png)$#D', $_GET['img'])
 || !is_file(__DIR__ . $_GET['img']))
{
  exit('Not found');
}
else
{
  header('Content-Type image/jpeg');
  $fp = fopen(__DIR__ . $_GET['img']);
  fpassthru($fp);
}

[ Voor 6% gewijzigd door DJMaze op 19-03-2017 10:29 ]

Maak je niet druk, dat doet de compressor maar


Acties:
  • +1 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Dan ben je er toch? Nu alleen zorgen dat de bestanden buiten de webroot staan en ze een beetje efficient via PHP ontsluiten.

offtopic:
En doe iets aan die regex, ipv dat je gebruikers die dmv van een fatsoenlijke password manager default ook leestekens in hun wachtwoord krijgen af straft. Check op minimum lengte bij aanmaken (en een riante max lengte ipv hash-kosten), misschien nog een sterkte-indicatie erbij en verder mogen dergelijke restricties een keer tot het verleden behoren.


edit:
Response header en passthru is inderdaad wat je zocht. Zoals vaker kan DJMaze zich niet inhouden om het voor te kauwen met discutabele code. Let op: van die bestands-extensie regex klopt niet heel veel (nutteloze '.+' en geen einde string check). En nog belangrijker:
Code van DJMaze bevat een groot security probleem (random file inclusion) : Controleer uit welk pad je bestanden serveert (bijvoorbeeld: gebruik realpath() en check of het begin van het pad overeen komt e.d.) want met de code zoals nu gegeven kan je zonder enig probleem je systeem wachtwoorden en dergelijke laten outputten.

[ Voor 40% gewijzigd door Voutloos op 18-03-2017 21:14 . Reden: Tekst over grove security fout meer laten opvallen. ]

{signature}


Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
@Voutloos
Offtopic is geen enkel probleem ik sta open voor alle hulp.

Ik moet erbij vermelden dat ik een user en pass aanmaak voor iemand.
Ze krijgen een redelijk simpele user (bv tweaker9236) en pass (bv voutloos2847).
De bestanden zijn niet supergeheim dus mocht het gehackt worden dan is er geen man overboord.

@DJMaze
Bedankt voor je suggestie!
Ik snap nog weinig van je code maar ik ga mij er in verdiepen na het weekend en hopelijk lukt het!

@Voutloos
Haha ik lees nu pas je edit :P
Top dat je nu al potentiele foutjes eruit kunt filteren.
Ik ga mij er zeker in verdiepen _/-\o_

[ Voor 13% gewijzigd door michaelboon82 op 18-03-2017 21:11 ]


Acties:
  • 0 Henk 'm!

  • RedHat
  • Registratie: Augustus 2000
  • Laatst online: 05-10 19:42
Je kunt toch gewoon buiten de webroot mappen aanmaken en alleen php daar toegang tot geven?

Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
@RedHat
Klinkt als een makkelijke oplossing.
Ik ga alle voorgestelde suggesties eens bekijken maandag.
Top dat iedereen meedenkt, bedankt!

Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
Voutloos schreef op zaterdag 18 maart 2017 @ 21:04:
edit:
Response header en passthru is inderdaad wat je zocht. Zoals vaker kan DJMaze zich niet inhouden om het voor te kauwen met discutabele code. Let op: van die bestands-extensie regex klopt niet heel veel (nutteloze '.+' en geen einde string check). En nog belangrijker:
Code van DJMaze bevat een groot security probleem (random file inclusion) : Controleer uit welk pad je bestanden serveert (bijvoorbeeld: gebruik realpath() en check of het begin van het pad overeen komt e.d.) want met de code zoals nu gegeven kan je zonder enig probleem je systeem wachtwoorden en dergelijke laten outputten.
Ik zei niet voor niks dat het nog beveiligd moest worden ;)
Maar wachtwoorden outputten is niet makkelijk met mijn versie, moet er namelijk wel "png" of "jpe?g" in staan.
Daarnaast geef ik ook nog de verkeerde header mee voor png bestanden.

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
Dankzij iedereen weet ik nu in ieder geval dat ik de oplossing moet zoeken in de bestanden buiten de root te zetten en dan deze serveren via PHP. Het enige wat ik buiten de root wil zetten zijn jpeg's dus ik hoef niet meerdere bestanden te kunnen 'headeren'.

@DJMaze @Voutloos
Ik ben mij er een beetje in aan het verdiepen (het is allemaal vrij onbekende materie voor mij).
Echter kom ik veel oplossingen tegen op het internet die gebruik maken van readfile($file) in plaats van fpassthru($file).
Is het 1 beter dan de andere?

Even een resumé:
- Readfile/fpassthru gebruiken.
- Ervoor zorgen dat alleen jpg kan 'geheaderd' worden
- $_GET['image'] input filteren
- Checken dat de juiste buiten-de-root directory wordt gebruikt
Bovenstaande zou een veilige oplossing bieden als ik het goed heb?

Offtopic:
Ik zit een beetje met de opmerking van DukeBox in mijn maag.
Kunnen/willen jullie zo zeggen of ik een beetje in de goede richting zit met mijn inlogcode?

Acties:
  • 0 Henk 'm!

Verwijderd

Je kunt in Apache eigen authenticatie modules toevoegen. Even Googlen leverde de onderstaande op. Je kunt via deze module een extern script, ook php, laten bepalen wie wel of geen toegang heeft. (Trefwoorden: mod_auth php)

http://mod-auth-script.sourceforge.net

Acties:
  • 0 Henk 'm!

  • DJMaze
  • Registratie: Juni 2002
  • Niet online
michaelboon82 schreef op zondag 19 maart 2017 @ 12:44:
Echter kom ik veel oplossingen tegen op het internet die gebruik maken van readfile($file) in plaats van fpassthru($file).
Is het 1 beter dan de andere?
Hangt er vanaf of je range requests wil ondersteunen. Zo ja, dan is fopen() defacto.

Zie mijn code hoe dat werkt
https://bitbucket.org/djm...eviewer=file-view-default

Maak je niet druk, dat doet de compressor maar


Acties:
  • 0 Henk 'm!

  • Marcj
  • Registratie: November 2000
  • Laatst online: 09-10 13:26
Verwijderd schreef op zondag 19 maart 2017 @ 12:50:
Je kunt in Apache eigen authenticatie modules toevoegen. Even Googlen leverde de onderstaande op. Je kunt via deze module een extern script, ook php, laten bepalen wie wel of geen toegang heeft. (Trefwoorden: mod_auth php)

http://mod-auth-script.sourceforge.net
Dit is volgens mij veruit de beste oplossing, omdat een webserver veel efficienter plaatjes kan versturen dan dat het via PHP moet gaan.

Nginx heeft hier ook erg goede ondersteuning voor, zie auth_request. Welke webserver gebruik je?

Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
@DJMaze
De bestanden zijn hooguit maar een paar MB groot, dus ik denk dat range requests niet nodig is (dat is tenminste wat ik begrijp van range requests).
Bedankt voor het delen van je code, ik zal er eens voor gaan zitten.
Maar ik ben ook heel benieuwd naar de oplossing van willenjoosten/Marcj, want dan hoef ik de

@Marcj @willemjoosten
Als het goed is, is mijn webserver Apache.
Maar ik zit bij Antagonist en heb geen self-managed hosting.
Dus ik kan volgens mij geen modules installeren.
In DirectAdmin zie ik wel een optie om Apache Handlers te installeren, misschien is dat om modules te installeren?

EDIT:
Nu roep ik een php scriptje aan dat de bestanden opvraagt buiten de root:
PHP:
1
2
3
foreach(glob('/bla/bla/bla/'.'*.jpg') as $file) {
  echo '<img src="image.php?foto='.$file.'">';
}

Werkt uitstekend, en image.php serveert mij de 10 verschillende foto's die in blablabla staan.
Image.php lekker beveiligd enzo, allemaal goed!

Nu dacht ik: voor een beetje extra beveiliging doe ik het volgende:
PHP:
1
2
3
4
foreach(glob('/bla/bla/bla/'.'*.jpg') as $file) {
  $_SESSION['foto'] = $file;
  echo '<img src="image.php">';
}


En dan in image.php: $image = $_SESSION['foto']; in plaats van $image = $_GET['foto'];

Mijn theorie is dat de $_SESSION variabele in de loop elke keer wordt overschreven met een nieuwe foto uit de directory, en dat image.php die foto verwerkt.
Maar hij verwerkt 10 keer de eerste foto helaas :|
Als ik $_SESSION['foto'] echo in de foreach loop dan krijg ik wel 10 verschillende bestandsnamen dus de sessie variabele 'foto' wordt wel elke keer veranderd naar de volgende foto.
Maar image.php onthoudt alleen de eerste laatste waarde die hij krijgt.
Ik heb geen idee wat er fout gaat.

[ Voor 48% gewijzigd door michaelboon82 op 20-03-2017 15:33 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Werkt inderdaad niet als je geen eigen server hebt. Je kunt nog even kijken of ze mod_authn_dbd hebben, daarmee kun je via .htaccess mappen beveiligen op basis van gebruikersgegevens uit een database.

https://httpd.apache.org/docs/2.4/mod/mod_authn_dbd.html

Op mod_authn_file, met users/wachtwoorden in een file. Die kun je vervolgens ook gebruiken in je php.

Acties:
  • 0 Henk 'm!

  • Pizzalucht
  • Registratie: Januari 2011
  • Laatst online: 08:40

Pizzalucht

Snotneus.

Voor de mensen die wel zelf modules kunnen installeren, mod_sendxfile is ook erg interessant! https://tn123.org/mod_xsendfile/

Basically voer je gewoon de authenticatie uit in bijvoorbeeld PHP en offload je het serveren van het bestand naar Apache als een normale static file. Hierdoor werken dingen als range requests ook gewoon en zijn je PHP workers sneller beschikbaar voor nieuwe requests.

Acties:
  • 0 Henk 'm!

  • Radiant
  • Registratie: Juli 2003
  • Niet online

Radiant

Certified MS Bob Administrator

DukeBox schreef op zaterdag 18 maart 2017 @ 20:35:
[...]

Ja, dat is exact wat ik bedoel.
Authorisatie doe je niet in een cookie, maar in de backend.
Anders kan je zo de cookie waar je geen toegang mee hebt aanpassen naar 'true' en klaar.
Authenticatie kan je wel met maar dien je bij iedere interactie weer te valideren.
Hij gebruikt ook geen cookie, maar een session (die op zijn beurt weer een cookie gebruikt om een id in op te slaan). Je kan sessievariabelen niet aanpassen aan de client-kant, want die worden opgeslagen op de server. Even die variabele naar 'true' veranderen zoals je zegt kan dus niet.

Geen bad practice dus.

Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
@willemjoosten
Ik heb nog gekeken maar ik kan niet zien welke modules ze hebben geactiveerd. Ik heb ze een mailtje gestuurd met de vraag welke modules geactiveerd zijn.

De wachtwoorden in mijn database zijn gehashed via de Password Hashing Functions van PHP. Volgens mij kan ik mod_authn_dbd niet gebruiken in combinatie met die functies?

@Radiant
Fijn om te horen!

Acties:
  • 0 Henk 'm!

  • Pizzalucht
  • Registratie: Januari 2011
  • Laatst online: 08:40

Pizzalucht

Snotneus.

michaelboon82 schreef op maandag 20 maart 2017 @ 11:33:
@DJMaze
De bestanden zijn hooguit maar een paar MB groot, dus ik denk dat range requests niet nodig is (dat is tenminste wat ik begrijp van range requests).
Bedankt voor het delen van je code, ik zal er eens voor gaan zitten.
Maar ik ben ook heel benieuwd naar de oplossing van willenjoosten/Marcj, want dan hoef ik de

@Marcj @willemjoosten
Als het goed is, is mijn webserver Apache.
Maar ik zit bij Antagonist en heb geen self-managed hosting.
Dus ik kan volgens mij geen modules installeren.
In DirectAdmin zie ik wel een optie om Apache Handlers te installeren, misschien is dat om modules te installeren?

EDIT:
Nu roep ik een php scriptje aan dat de bestanden opvraagt buiten de root:
PHP:
1
2
3
foreach(glob('/bla/bla/bla/'.'*.jpg') as $file) {
  echo '<img src="image.php?foto='.$file.'">';
}

Werkt uitstekend, en image.php serveert mij de 10 verschillende foto's die in blablabla staan.
Image.php lekker beveiligd enzo, allemaal goed!

Nu dacht ik: voor een beetje extra beveiliging doe ik het volgende:
PHP:
1
2
3
4
foreach(glob('/bla/bla/bla/'.'*.jpg') as $file) {
  $_SESSION['foto'] = $file;
  echo '<img src="image.php">';
}


En dan in image.php: $image = $_SESSION['foto']; in plaats van $image = $_GET['foto'];

Mijn theorie is dat de $_SESSION variabele in de loop elke keer wordt overschreven met een nieuwe foto uit de directory, en dat image.php die foto verwerkt.
Maar hij verwerkt 10 keer de eerste foto helaas :|
Als ik $_SESSION['foto'] echo in de foreach loop dan krijg ik wel 10 verschillende bestandsnamen dus de sessie variabele 'foto' wordt wel elke keer veranderd naar de volgende foto.
Maar image.php onthoudt alleen de eerste laatste waarde die hij krijgt.
Ik heb geen idee wat er fout gaat.
De reden dat je code niet werkt is doordat je die sessie aanpast in de loop. De browser download de plaatjes pas als de hele html terug wordt gestuurd. Op dat moment staat dus de laatste waarde van de loop in de sessie.

Ik denk dat je het beste de bestanden door middel van een rewriterule door php heen laat gaan. Zoiets dus: http://stackoverflow.com/...uthentication-mod-rewrite

Acties:
  • 0 Henk 'm!

  • michaelboon82
  • Registratie: December 2010
  • Laatst online: 06-10 16:08
Antagonist ondersteund geen mod_authn_dbd of een vergelijkbare module.
Hierdoor valt deze optie helaas af, tezamen met andere modules.

Uiteindelijk heb ik een php script geschreven die imho veilig genoeg is en die de jpg's serveert uit een gebruikersmap buiten de root.
Die map is door een rewriterule beveiligd en ik heb ervoor gezorgd dat het scriptje louter alleen jpg's in de GET accepteert.
Daarnaast stopt het scriptje als de gebruiker geen ingelogde sessie heeft lopen en kan het scriptje niks buiten de gebruikersmap terugheaderen.

De snelheid is niet om over naar huis te schrijven helaas maar het is geen groot probleem omdat het toch maar om bestanden van een paar MB groot gaat, die bovendien niet in grote getalen opgevraagd hoeven te worden.

Iedereen heel erg bedankt voor het meedenken, de info en de tijd die jullie erin gestoken hebben _/-\o_
Pagina: 1