[PHP] Plaatjes uploaden voor gebruikers, MySQL of fake path

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ik wil in een script waar gebruikers in kunnen loggen de gebruikers de mogelijkheid bieden om een plaatje te uploaden.

Nu is uploaden met PHP opzichzelf niet zo lastig, maar zodra het om security gaat wel vind ik.

Het makkelijkste zou zijn om een gif/jpg op te slaan in een BLOB in een database. Nu zijn databases hier alleen niet echt voor geschikt vind ik, ik zoek dus een andere manier.

Gewoon uploaden naar een folder is het makkelijkste. Het path opslaan in een MySQL database en je bent klaar.

Toch ben ik bang dat mensen welke een plaatje niet toebehoort, een andermans plaatje dat niet publiekelijk zichbaar is kunnen grabben omdat ze via hun eigen path het path van een ander ook kunnen zien.

De rest is raden dan natuurlijk.

Nu dacht ik eraan om de filenaam te hashen tijdens het uploaden en dan weer te geven gebruik makend van de hash + bestandsnaam welke ik uit de database haal en dan het plaatje met bestandsnaam en al weergeven zoals het hoorde.

Toch lijkt me dit niet handig en wordt een script eigenlijk onlogish complex.

Wat zou een goede oplossing zijn om desnoods deze plaatjes op te slaan in een path dat niet via het web te benaderen is maar ergens op de server staat ?

Acties:
  • 0 Henk 'm!

  • Gerco
  • Registratie: Mei 2000
  • Laatst online: 10-08 02:59

Gerco

Professional Newbie

Plaatje op je filesystem opslaan onder maak_mooie_random_id(). Vervolgens links genereren naar "http://site.nl/image/id/bestandsnaam.jpg" (vereist wel mod_rewrite oid). Dan kun je in je image.php het plaatje ophalen uit je plaatjes directory en met readfile() naar de client sturen.

Niet heel ingewikkeld, de client krijgt toch nog de originele naam te zien (handig voor opslaan) en het is niet mogelijk om de naam van een plaatje van een ander te raden. Nadeel is weer dat image/id/anderenaam.jpg hetzelfde plaatje laat zien. Hoeft echter geen probleem te zijn en als het dat wel is kun je dat altijd nog checken tegen de db en 404 teruggeven als het niet klopt.

Plaatjes die niet publiekelijk zichtbaar zijn, readfile() je dan natuurlijk gewoon niet wanneer de gebruiker niet is ingelogd. Dan kun je een 403 returnen, zoals het hoort.

[edit]
De bestandsnaam hashen is niet zo handig idd. Een hash van de content is dan beter. Al krijg je wel problemen als verschillende mensen hetzelfde plaatje uploaden. Random hash/guid dus.

[ Voor 42% gewijzigd door Gerco op 09-03-2008 23:20 ]

- "Als ik zou willen dat je het begreep, legde ik het wel beter uit!" | All number systems are base 10!


Acties:
  • 0 Henk 'm!

  • GlowMouse
  • Registratie: November 2002
  • Niet online
Je hebt het over niet-publiekelijk zichtbaar, dus authenticatie zal sowieso moeten via een PHP-script. De twee opties die je dan hebt, zijn opslaan in de database of opslaan (en dan via echo) in een niet-publieke map (en dan via readfile). De bestandsnaam is in beide gevallen irrelevant. Velen geven aan de laatste de voorkeur omdat de database erg langzaam zou worden.

Lees ook dit topic.

[ Voor 11% gewijzigd door GlowMouse op 09-03-2008 23:16 ]


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Puur alleen een filename hashen levert onheroepelijk een keer dezelfde hash op dus dat is sowieso niet handig. Wat mij het meeste handige lijkt is iedere foto random hashen en dan deze hash+filename te gebruiken bij het opvragen en dan het echte pad opslaan in je db. Als je kijkt naar flickr en bv hyves zie je ook dat ze random hashes gebruiken.

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
De manier van Gerco lijkt me opzich wel wat, kan alleen ook wel complex worden inderdaad.
Y0ur1 schreef op zondag 09 maart 2008 @ 23:16:
Puur alleen een filename hashen levert onheroepelijk een keer dezelfde hash op dus dat is sowieso niet handig. Wat mij het meeste handige lijkt is iedere foto random hashen en dan deze hash+filename te gebruiken bij het opvragen en dan het echte pad opslaan in je db. Als je kijkt naar flickr en bv hyves zie je ook dat ze random hashes gebruiken.
Zij hashen volgens mij de naam van de foto en plakken er een random has voor.

Ik heb even gezocht naar voorbeelden, maar ik zie alleen mensen welke hem BLOPPEN, en dat is niet slim.

Er zijn weinig mensen die hashen trouwens, tenminste de voorbeeld script wijzen dat uit.

Ik ga iets customs bakken denk ik :)

Acties:
  • 0 Henk 'm!

  • Kwastie
  • Registratie: April 2005
  • Laatst online: 19-09 10:42

Kwastie

Awesomeness

Ik heb een paar weken geleden hetzelfde gemaakt, iets uitgebreider maar toch.

Ik leg het in het kort uit,

Uploaden van een bestand met een normaal script.
Eventueel controleren of het bestand geupload MAG worden. dus bijv. Geen .exe .java bestanden etc.


Vervolgens als het uploaden gelukt is bestand verplaatsen naar 'een' map. (niet in de database zetten) dan zet je alle informatie die je hier onder ziet behalve temp_file location in de database (alleen filename) de bestands naam is een sha1 hash van de microtime + een random waarde.
PHP:
1
2
3
4
5
6
<?php
$file_name = $_FILES['uploadedfile']['name'];  //echte filename
$file_size = $_FILES['uploadedfile']['size']; // de groote in bytes
$file_temp_path = $_FILES['uploadedfile']['tmp_name']; //de geuploade locatie (temp)
$file_type = $_FILES['uploadedfile']['type']; // file formaat bijv. image/png
?>



vervolgens lees je met 'readfile' (een functie) de foto in, en echo je die. (hierdoor is er geen directe link naar het bestand, dus geen security issue) Je moet wel de goede 'headers' echo-en. (dus de filetype in dit geval)

voorbeeld:
header("image/png"); (voor een png image) en dan echo(readfile("filename"));

Database Layout:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
  `id` int(8) NOT NULL auto_increment,
  `userid` int(8) NOT NULL, 
  `catid` int(8) NOT NULL,  
  `name` text NOT NULL,
  `description` mediumtext NOT NULL,
  `filelocation` varchar(150) NOT NULL,
  `filesize` int(8) NOT NULL,
  `filename` varchar(100) NOT NULL,
  `filetype` varchar(100) NOT NULL,
  `totaldownloads` int(8) NOT NULL,
  `uploaded` datetime NOT NULL,
  PRIMARY KEY  (`id`)


Hopelijk lukt het je :) mocht je nog vragen hebben gewoon stellen dus :9

[ Voor 19% gewijzigd door Kwastie op 09-03-2008 23:37 ]

When I get sad i stop being sad and be awesome instead


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Kwastie schreef op zondag 09 maart 2008 @ 23:33:
Ik heb een paar weken geleden hetzelfde gemaakt, iets uitgebreider maar toch.

Ik leg het in het kort uit,

Uploaden van een bestand met een normaal script.
Eventueel controleren of het bestand geupload MAG worden. dus bijv. Geen .exe .java bestanden etc.


Vervolgens als het uploaden gelukt is bestand verplaatsen naar 'een' map. (niet in de database zetten) dan zet je alle informatie die je hier onder ziet behalve temp_file location in de database (alleen filename) de bestands naam is een sha1 hash van de microtime + een random waarde.
PHP:
1
2
3
4
5
6
<?php
$file_name = $_FILES['uploadedfile']['name'];  //echte filename
$file_size = $_FILES['uploadedfile']['size']; // de groote in bytes
$file_temp_path = $_FILES['uploadedfile']['tmp_name']; //de geuploade locatie (temp)
$file_type = $_FILES['uploadedfile']['type']; // file formaat bijv. image/png
?>



vervolgens lees je met 'readfile' (een functie) de foto in, en echo je die. (hierdoor is er geen directe link naar het bestand, dus geen security issue) Je moet wel de goede 'headers' echo-en. (dus de filetype in dit geval)

voorbeeld:
header("image/png"); (voor een png image) en dan echo(readfile("filename"));

Database Layout:
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
  `id` int(8) NOT NULL auto_increment,
  `userid` int(8) NOT NULL, 
  `catid` int(8) NOT NULL,  
  `name` text NOT NULL,
  `description` mediumtext NOT NULL,
  `filelocation` varchar(150) NOT NULL,
  `filesize` int(8) NOT NULL,
  `filename` varchar(100) NOT NULL,
  `filetype` varchar(100) NOT NULL,
  `totaldownloads` int(8) NOT NULL,
  `uploaded` datetime NOT NULL,
  PRIMARY KEY  (`id`)



Hopelijk lukt het je :) mocht je nog vragen hebben gewoon stellen dus :9
Check jij ook nog in de db voor een mogelijke hash-collision? De kans is zeer klein maar theoretisch is het mogelijk natuurlijk, als de hash bestaat kun je hem gewoon opnieuw hashen aangezien je met microtime() werkt. Ohja en waarom sha1 ipv md5? Is dat beter/veiliger dan md5? (ben ik nog niet echt over ingelezen vandaar dat ik het voor het gemak ook meteen vraag)

[ Voor 3% gewijzigd door Y0ur1 op 09-03-2008 23:47 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Tja, hash collisions. :O

De kans op twee dezelfde MD5-hashes bij random input is 1 op 3.402823669e+38. Een kans van één op heel, heel erg veel. Een collision kan voorkomen, maar de kans op een collision is erg klein.

Check gerust of een hash al bestaat, maar ga niet al je tijd en energie steken in het voorkomen van een situatie die waarschijnlijk nooit zal voorkomen; dat is leuk voor maanlanders en medische aparatuur, maar niet voor een hobby-project waar tien mensen gebruik van gaan maken.

Het probleem is, dat je je verliest in details en de grote lijn uit het oog verliest. Liever een collision ééns in de miljoen jaar, dan een inlogprocedure met een fout erin die je had gevonden als je niet zo druk was geweest met de rest van je systeem. :|

Als je met hashes werkt, zorg dan dat je weet wat je doet. Lees je in in cryptografie, zodat je weet waarvoor MD5 en SHA1 zijn ontworpen. Tegenwoordigt hash't iedereen de meest gekke dingen, om de meest aparte redenen. Hashing is een hulpmiddel bij met name integriteitscontrole, maar zeker geen heilige graal.

Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Verwijderd schreef op maandag 10 maart 2008 @ 00:08:
Tja, hash collisions. :O

De kans op twee dezelfde MD5-hashes bij random input is 1 op 3.402823669e+38. Een kans van één op heel, heel erg veel. Een collision kan voorkomen, maar de kans op een collision is erg klein.

Check gerust of een hash al bestaat, maar ga niet al je tijd en energie steken in het voorkomen van een situatie die waarschijnlijk nooit zal voorkomen; dat is leuk voor maanlanders en medische aparatuur, maar niet voor een hobby-project waar tien mensen gebruik van gaan maken.

Het probleem is, dat je je verliest in details en de grote lijn uit het oog verliest. Liever een collision ééns in de miljoen jaar, dan een inlogprocedure met een fout erin die je had gevonden als je niet zo druk was geweest met de rest van je systeem. :|

Als je met hashes werkt, zorg dan dat je weet wat je doet. Lees je in in cryptografie, zodat je weet waarvoor MD5 en SHA1 zijn ontworpen. Tegenwoordigt hash't iedereen de meest gekke dingen, om de meest aparte redenen. Hashing is een hulpmiddel bij met name integriteitscontrole, maar zeker geen heilige graal.
Niet zo negatief :P Het is inderdaad afhankelijk van de grootte van een project, maar bij een site als flickr lijkt me dat toch echt geen overbodige luxe.

Acties:
  • 0 Henk 'm!

  • Morax
  • Registratie: Mei 2002
  • Laatst online: 20:32
Checken of een hash al bestaat is niet zo veel werk hoor, in pseudocode:

code:
1
2
3
4
5
do{
  $hash = generate_hash();

  $hash_exists = check_if_hash_exists($hash);
}while($hash_exists == true);


Zo weet je zeker dat je aan het einde van bovenstaande loop altijd een niet bestaande hash hebt zonder veel moeite ;)

What do you mean I have no life? I am a gamer, I got millions!


Acties:
  • 0 Henk 'm!

  • ibmos2warp
  • Registratie: Januari 2007
  • Laatst online: 20-11-2023

ibmos2warp

Eval is Evil

Kwastie schreef op zondag 09 maart 2008 @ 23:33:
Eventueel controleren of het bestand geupload MAG worden. dus bijv. Geen .exe .java bestanden etc.
Hoe voer jij die controle uit? Zoals je hier doet:
Kwastie schreef op zondag 09 maart 2008 @ 23:33:
PHP:
1
2
3
4
5
6
7
<?php
$file_name = $_FILES['uploadedfile']['name'];  //echte filename
$file_size = $_FILES['uploadedfile']['size']; // de groote in bytes
$file_temp_path = $_FILES['uploadedfile']['tmp_name']; //de geuploade locatie (temp)
//Blaat: Hier dus, de volgende regel:
$file_type = $_FILES['uploadedfile']['type']; // file formaat bijv. image/png
?>
Hopelijk niet. Veel mensen die controleren met die regel of het bestand wel mag geupload worden. Dit is gevaarlijk, omdat er gewoon php bestanden geupload kunnen worden. Zoals je op php.net kunt lezen:
$_FILES['userfile']['type']
The mime type of the file, if the browser provided this information. An example would be "image/gif". This mime type is however not checked on the PHP side and therefore don't take its value for granted.
kan je type niet vertrouwen. Hier is een blogpost, waar wat meer staat.
Als je het niet doet is het oke, veel mensen die letten er niet terwijl het toch (vind ik) een simpele manier is om php op een server te krijgen.

[ Voor 4% gewijzigd door ibmos2warp op 10-03-2008 01:34 ]

Ik weet alles van niks
Vind Excel ongelovelijk irritant.


Acties:
  • 0 Henk 'm!

  • Kwastie
  • Registratie: April 2005
  • Laatst online: 19-09 10:42

Kwastie

Awesomeness

Ok, bedankt óok weer wat geleerd :P direct even naar kijken :+

en over hash-collions .. dit kan niet, microtime is:
"microtime — Return current Unix timestamp with microseconds"
+ er komt nog een extra waarde bij (als bijv. 2 personen tegelijk uploaden, en dan denk ik niet dat ze het op de zelfde milisecode gaat.

@Morax:
Stel dat je een database met 100.000 gebruikers hebt, dan wil je toch niet gaan controleren of daadwerkelijk deze waarde al eens in de database staat. In het extreemste geval hebben de gebruikers hetzelfde plaatje, en dat vind ik zelf geen punt

[ Voor 95% gewijzigd door Kwastie op 10-03-2008 10:08 ]

When I get sad i stop being sad and be awesome instead


Acties:
  • 0 Henk 'm!

  • g4wx3
  • Registratie: April 2007
  • Laatst online: 11-09 09:49
Heb niet alles gelezen, misschien is het al gezegd. maar volgens mij doe je dit het beste via appache.


Telkens er rechtstreeks een plaatje woord geladen (check mime-type) dan geef je een default plaatje, net zoals T.net dat doet, en zoveel andere sites, die niet willen gebruikt worden als plaatjes hoster.

Hoe appache dit axact doet weet ik wel niet

*Je kunt ook alle plaatjes in 1 map zetten, en gewoon die map blokeren, is net iets makkelijker dan mime types in te stellen.

[ Voor 15% gewijzigd door g4wx3 op 10-03-2008 10:08 ]

http://www.softfocus.be/


Acties:
  • 0 Henk 'm!

  • Dennahz
  • Registratie: November 2001
  • Laatst online: 17-09 21:50

Dennahz

Life feels like hell should.

In het systeem wat ik nu maak heb ik de images in een aparte map uit de webserver root gezet. Deze plaatjes worden alleen opgehaald als de hash in orde is.

De hash hier bestaat uit:

- Random ID wat bij inloggen gegenereerd wordt.
- IP adres van user
- user_id
- Hash van gegevens van de ingelogde user: functie, id, ip, random id
- bestandsnaam

Probeer dat maar eens te raden. Verschilt per sessie en gebruiker dus nooit dezelfde hash.

Twitter


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Kwastie schreef op maandag 10 maart 2008 @ 10:01:
@Morax:
Stel dat je een database met 100.000 gebruikers hebt, dan wil je toch niet gaan controleren of daadwerkelijk deze waarde al eens in de database staat.
Tja, want stel je voor dat je een enkele kolom waar uiteraard een index op staat wil je checken in je db. Dat kost je toch al gauw een paar milliseconde... ;)

{signature}


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Dennahz schreef op maandag 10 maart 2008 @ 10:09:
In het systeem wat ik nu maak heb ik de images in een aparte map uit de webserver root gezet. Deze plaatjes worden alleen opgehaald als de hash in orde is.

De hash hier bestaat uit:

- Random ID wat bij inloggen gegenereerd wordt.
- IP adres van user
- user_id
- Hash van gegevens van de ingelogde user: functie, id, ip, random id
- bestandsnaam

Probeer dat maar eens te raden. Verschilt per sessie en gebruiker dus nooit dezelfde hash.
Fake jij het path van het plaatje uiteindelijk ?

Acties:
  • 0 Henk 'm!

  • Morax
  • Registratie: Mei 2002
  • Laatst online: 20:32
Kwastie schreef op maandag 10 maart 2008 @ 10:01:
@Morax:
Stel dat je een database met 100.000 gebruikers hebt, dan wil je toch niet gaan controleren of daadwerkelijk deze waarde al eens in de database staat. In het extreemste geval hebben de gebruikers hetzelfde plaatje, en dat vind ik zelf geen punt
Ach, het is een kleine moeite om die extra check te doen. Zoals al gezegd word, als je een index op het veld op staan voorzie ik geen problemen, dat handelt de DB zo af. En daarnaast is de tijd die deze check extra inneemt natuurlijk nihil als je het vergelijkt met de tijd van het uploaden ;)

[ Voor 3% gewijzigd door Morax op 10-03-2008 10:49 ]

What do you mean I have no life? I am a gamer, I got millions!


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 22:34
Wat een geneuzel, die hashes. Je database kan prima unieke identifiers genereren. Dan sla je gewoon "database-id.bestandsnaam" op je filesysytem. Gegarandeerd uniek en je kunt ook nog aan het bestand zien wat ongeveer de bedoeling was...
Nu zijn databases hier alleen niet echt voor geschikt vind ik, ik zoek dus een andere manier.
Kun je die ook onderbouwen? Als het gaat om het maken van backups dan hebben bestanden in de database bijvoorbeeld best wel voordelen.

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • Kwastie
  • Registratie: April 2005
  • Laatst online: 19-09 10:42

Kwastie

Awesomeness

het path hoef je niet te faken. Je kopieerd het bestand gewoon naar een map (maakt niet uit welke) en leest deze dan met 'readfile' in. Er is dan geen directe link naar dat bestand (wel opletten met de header!)

When I get sad i stop being sad and be awesome instead


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Kwastie schreef op maandag 10 maart 2008 @ 13:58:
het path hoef je niet te faken. Je kopieerd het bestand gewoon naar een map (maakt niet uit welke) en leest deze dan met 'readfile' in. Er is dan geen directe link naar dat bestand (wel opletten met de header!)
Dit is een goede manier inderdaad. Deze zal ik zeker toepassen.
Pagina: 1