Kép feltöltés validálása PHP-ban, biztonságos-e ?

Kép feltöltés validálása PHP-ban, biztonságos-e ?
2022-03-19T11:35:40+01:00
2022-03-20T17:35:26+01:00
2022-10-15T21:20:24+02:00
BadGirl
Sziasztok,

Az alap koncepció, hogy a felhasználó feltudjon tölteni egy képet. A kép legyen adott formátumú.
Ha tényleges egy valódi képek tölt fel nincs probléma -szerintem-, viszont ha nem valódit, akkor az alábbi kódom kiszűri ?

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post" enctype="multipart/form-data" > <input type="file" name="image"> <input type="submit" name="kep" value="Gomb szövege"> </form> if(isset($_POST['kep'])) { $out = 'kepek\'.$_FILES['image']['name']; move_uploaded_file($_FILES['image']['tmp_name'],$out); $image_type = mime_content_type(check_input('kepek/'.$_FILES['image']['full_path'])); $allowed = array('image/jpeg','image/jpg', "image/png", "image/bmp", "image/JPEG","image/JPG", "image/PNG", "image/BMP"); if(in_array($image_type,$allowed) ){ echo 'OKay'; /*PDO kapcsolat kép neve mentésre kerül az adatbázisba, egyedileg generált név lesz, névütközés ellenőrzés, kép mappában maradt*/ } else { if(file_exists('kepek\'.$_FILES['image']['name'])) unlink('kepek\'.$_FILES['image']['name']); }} function check_input($data) { $data = trim($data); $data = stripslashes($data); $data = htmlspecialchars($data); return $data; }

Azt olvastam, hogy a MIME képes kiszűrni, próbálgattam és kaptam ilyen végeredményeket
application/postscript, 
application/x-macbinary,
application-msword
application/octet-streamend
application/x-dosexecend stb..
ezek nem jók és

a nekem jók
image/png
image/jpg stb..


A MIME-nek elérési útvonalat kell adnom, csak úgytudtam működésre bírni, hogy elmentem egy mappába, viszont az már az éles tárhelyre kerül.  A feltételezett képpel nem csinálok semmit a validáción kívül, még nem tudom hogy tényleg kép-e.

Ha rosszindulatú képfájl és a szerveren egy mappába bele mozgatom -move_uploaded_file-, hogy tudjam validálni, okozhat problémát ?

A képek neve és kiterjesztése lesznek az adatbázisban tárolva, a képek egy mappában lesznek. Blob-okal nem kísérleteznék ebben a projectben. -ha számít a válasz szempontjából-

Illetve a "check_input" függvénynek van értelme ebben a kódrészletben? Nézegettem páldákat és az  egyikben megtalálható volt, viszont képre értelmezve van értelme ? Mivel a képnevére értelmezni fájl.kiterjesztés, ebből szűr dolgokat, elvileg ebben nem lehet veszélyes kódrész mivel az OS eleve nem enged speciális karaktereket használni fájl nevében.
Mutasd a teljes hozzászólást!

  • Azt ugye tudod, hogy a kép már a move_uploaded_file hívás előtt is a szerveren van valahol?
    Ezt az (ideiglenes) útvonalat kapod meg a $_FILES tömbben a megfelelő tmp_name kulcson, tehát van mit odaadni a mimetype-ellenőrződnek még az átmozgatás előtt.
    Mutasd a teljes hozzászólást!
  • Olvasd el ezt!
    Mutasd a teljes hozzászólást!
  • Kliensen használj fileApi-t. Mikor megjelennek a blobok egy kis div-ben mehet rá a jquery load.


    $(".class").on("load loadeddata", function() {
     

    Azon belül mindent ellenőrzöl még mielőtt a szerverre megy. Videót is lehet

    A load után

    .on("error", function() {
    Kitudod rakni ami hibás és nem töltik fel.


    Egy filefeltöltőmet megcsináltam PHP-re is. Nem vacakolok vele, ha több fájlt töltenek fel és ha csak az egyik nem felel meg, akkor egyik fájl se lesz mentve. Nem vagyok PHP-s, de ez így működik:

    <?php //PHP ini -> max_file_uploads = 6 | upload_max_filesize = 25M | post_max_size = 150M if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_FILES["files"])) { if (isset($_POST["filesInputData"])) { $jsonDecode = json_decode($_POST["filesInputData"], true); $data1 = $jsonDecode["data1"]; //Hello Server $data2 = $jsonDecode["data2"]; //MULTIATOM $data3 = $jsonDecode["data3"]; //https://multiatom.com }; $maxFile = 6; $maxSize = 25000000; //byte $acceptMime = array( "image/jpeg"=>"jpg", "image/png"=>"png", "image/svg+xml"=>"svg", "video/mp4"=>"mp4", "video/webm"=>"webm", "video/quicktime"=>"mov", "audio/mpeg"=>"mp3", "application/zip"=>"zip" ); $filesCount = count($_FILES["files"]["name"]); //error if($filesCount > $maxFile) { echo json_encode(array("error"=>"maxFile"), true); exit(); }; foreach ($_FILES["files"]["name"] as $key => $error) { //error if ($_FILES["files"]["error"][$key]) { echo json_encode(array("error"=>"fileError"), true); exit(); }; $fileSize = $_FILES["files"]["size"][$key]; $fileType = $_FILES["files"]["type"][$key]; //error if($fileSize > $maxSize) { echo json_encode(array("error"=>"maxSize"), true); exit(); }; //error if(!array_key_exists($fileType, $acceptMime)) { echo json_encode(array("error"=>"mimeType"), true); exit(); }; }; //OK! Save Files foreach ($_FILES["files"]["name"] as $key => $error) { $tmpName = $_FILES["files"]["tmp_name"][$key]; $fileType = $_FILES["files"]["type"][$key]; $name = mb_convert_encoding($_FILES["files"]["name"][$key], "ISO-8859-2", "UTF-8"); $name = strtolower($name); $pathInfoFile = pathinfo($name); $fileName = substr($pathInfoFile["filename"],0,25); //substr $acceptFileExtension = $acceptMime[$fileType]; $fullFilename = $fileName.".".$acceptFileExtension; move_uploaded_file($tmpName, "uploads/".$fullFilename); }; //success echo json_encode(array("success"=>"success"), true); } else { header("HTTP/1.0 404 Not Found"); }; ?>
    Mutasd a teljes hozzászólást!
  • A $_FILES['image']['tmp_name'] hiba üzenetet kapom.
    Így visszakapott elérési útvonalhoz oda mentem localhoston és nem találtam ott a képet.
    Mutasd a teljes hozzászólást!
  • Pont a biztonság a fő kérdés, a te javaslatod pedig egyáltalán nem az.
    A "type" kulcs helyett a tényleges fájlt kellene csekkolni, ahogy a kérdező is teszi, tehát mime_content_type, finfo_* ... stb.

    Azt pedig, hogy kliens oldalon mit csinálsz / nem csinálsz, ebből a szempontból nem érdekes (bár valóban kényelmesebbé lehet vele tenni a dolgokat).

    Mutasd a teljes hozzászólást!
  • Tehát még a move_uploaded_file előtt hibát kapsz erre:

    $mt = mime_content_type($_FILES['image']['tmp_name']);
    ?

    Beidéznéd a hibaüzenetet?
    Mutasd a teljes hozzászólást!
  • Utoljára 6 éve használtam PHP-t. Csak nagyvonalakban csináltam meg ezt részt nem az én rendszerem használja.
    Mutasd a teljes hozzászólást!
  • OK, csak szóltam, hogy nem safe a javaslatod.
    Mutasd a teljes hozzászólást!
  • Javítgassátok. Ha kell a frontend rész szívesen feltöltöm.
    Mutasd a teljes hozzászólást!
  • print_r(mime_content_type(check_input('kepek/'.$_FILES['image']['tmp_name'])));

    Warning: mime_content_type(kepek/C:xampptmpphp6DA2.tmp): Failed to open stream: No such file or directory in C:\xampp\htdocs\index.php on line 356

    kepek mappát is bele véve a példában. Olyan is volt, hogy egy szinten volt a kép és az azt kezelő index.php is és akkor is előjött a Warning-os rész. -persze elérési útvonalnál 'kepek' nem szerepelt, mivel akkor nem is azzal teszteltem-
    Mutasd a teljes hozzászólást!
  • Így néz ki csak nálam Node.js a backend

    The Uploader
    Mutasd a teljes hozzászólást!
  • Na, akkor még egyszer szájbarágósabban:

    $type = mime_content_type($_FILES['image']['tmp_name']); if(megfelelő type) { // egyéb ellenőrzések, majd move_uploaded_file... stb. }
    Mutasd a teljes hozzászólást!
  • elvileg ebben nem lehet veszélyes kódrész mivel az OS eleve nem enged speciális karaktereket használni fájl nevében.

    Hát ez minimum oprendszerfüggő, de inkább nem igaz. A Windows tilt bizonyos karaktereket, mint előre perjel, kettőspont, csillag, kérdőjel, kacsacsőrök, pipe jel és idézőjel, de ettől még shell injection bőven lehet, főleg ha szóköz van a fájl nevében és erre nem készül fel aki a shell parancsot írja. A Linux tudtommal csak az előre perjelet és a null karaktert tiltja, részéről minden más karakter (sőt, karakterként nem értelmezhető bájtsorozat) lehet a fájl nevében.

    Ha nem bízol benne, hogy a felhasználó által küldött fájlnév biztonságos, akkor mindenféle escape varázslás helyett inkább generálj neki egy nevet magad, ami biztosan jó, és azzal tárold le a fájlrendszeren. Ha emlékezni kell a beküldött névre is, az mehet az adatbázisba, ott nem zavar senkit ha vicces karakterek vannak benne. (Persze az SQL injectionre vigyázni kell, de ez ugyanúgy igaz minden más mezőre is.)

    A többit nálam hozzáértőbb emberek leírták, abba nem szeretnék beleokoskodni.
    Mutasd a teljes hozzászólást!
  • Köszönöm a válaszokat kaptam érdekes és elgondolkodtató információkat.
     Amit H.Lilla küldött átnéztem azután én is találtam más forrásból, de ugyanazon elven működő megoldást.
    Elvileg több módon lehet ellenőrizni,  a kérdésben lévő kódom általánosságban jól működik.
    VISZONT kiegészítésként, 

    if($uploaded_type == 'image/jpeg'){ $img = imagecreatefromjpeg( $uploaded_tmp ); imagejpeg( $img, $temp_file, 100); } else{ $img = imagecreatefrompng( $uploaded_tmp ); imagepng( $img, $temp_file, 9); } imagedestroy( $img );

    Így elvileg a "kis gonosz" kódrészek, már nem kerülnek be az újra alkotott képbe.
    (ezek a metódusok a GD Graphics Library használják, PHP.ini fájlba engedélyezni kell, ha esetleg valaki játszani akar vele.)

    Egy ilyen résszel kiegészítve, hogyha sikeresen lefut, akkor elvileg teljesen nyugodt lehetek, hogy nem lesz probléma ? (próbálok minél közelebb kerülni a 100%  még ha nem is sikerül)
    Ez után mehet pl:move_uploaded_file(), hogy  a végleges mappába kerüljön a kép ?
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd