Upload scannen
Ik werk met Linux, daar heb je ClamAV, met een daemon "clamd" die je via een socket of TCP/IP kunt aanspreken.
De enige library die ik heb kunnen vinden is php-clamav maar die heeft slechts support tot en met PHP 5.5, en niet meer voor 8.3.
Via de exec() gaat veel te traag omdat het seconden duurt voordat de virusscanner is geinitialiseerd. Dat duurt te lang als je meerdere uploads hebt.
Hoe dit het beste aan te pakken?
In de database geef je dan met een flag aan, dat ze gescand en goedgekeurd zijn.
Misschien helpt het om bij meerdere uploads alles tijdelijk op de server in te zippen en dat weer te scannen? Uiteindelijk hoeft er dan maar één proces van clamav aangeroepen te worden. Maar ik zeg altijd: Meten is weten.
Gewijzigd op 04/09/2024 11:27:13 door - Ariën -
Ik heb nog even verder gezocht en kwam tegen phpMussel (ik kan niets met Composer), Cloudmersive (is dat via een API?) en Quahog. Die laatste ziet er het meest simpel uit.
Toch maar eens kijken of daar iets tussen zit?
Maar hoezo zou een wachtrij weinig praktisch zijn? Je wilt geen virussen verspreiden zolang iets nog niet gescand is.
Dat is echt een beetje te veel.
De daemon draait al dus het zou makkelijker zijn om die opdracht te kunnen geven om enkele bestandjes te scannen die in de /tmp worden geupload.
Ik ben even aan het kijken met de code van Quahog en deze SO thread om te kijken of ik er iets simpelers van kan maken.
Om wat voor soort "binaire bestanden" gaat het?
Hoe roep je die clam aan in de CLI?
Ward van der Put op 04/09/2024 12:57:36:
Om wat voor soort "binaire bestanden" gaat het?
Bestanden uit de /tmp map die via HTTP worden geupload door gewone gebruikers en waarvan de metadata in $_FILES staat. Deze bestanden worden via move_uploaded_file() verplaatst naar een 'veilige' map of als BYTEA naar de database geschreven. Vervolgens kunnen de bestanden door andere gebruikers worden gedownload. Wat ik zou willen is dat je voorkomt dat virussen zich via deze weg verspreiden.
- Ariën - op 04/09/2024 13:17:10:
Hoe roep je die clam aan in de CLI?
Met 'clamscan -r testbestand'
Toevoeging op 04/09/2024 14:04:41:
Ik heb het werkend gekregen:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
declare (strict_types=1);
// Gebaseerd op
// https://github.com/jonjomckay/quahog/tree/master
// https://github.com/clue/socket-raw/tree/1.x
const TIMEOUT = 30;
const CLAMD_SOCKET = '/var/run/clamav/clamd.ctl';
$filename = "/tmp/test.txt";
// Maak socket met timeout in blocking I/O
$socket = @socket_create(AF_UNIX, SOCK_STREAM, 0);
if ($socket === false) { die("Kan geen socket maken"); }
$r = NULL;
$w = [$socket];
$e = NULL;
@socket_select($r, $w, $e, TIMEOUT, 0);
if (@socket_get_option($socket, SOL_SOCKET, SO_ERROR) === false) {
die("Kan timeout niet instellen op socket");
}
if (! socket_set_block($socket)) { die("Geen blocking I/O voor socket"); }
if (! @socket_connect($socket, CLAMD_SOCKET)) { die("Kan niet verbinden met ClamAV"); }
// Geef opdracht bestand te scannen
$msg = "SCAN " . $filename;
$buffer = 'n' . $msg . "\n";
@socket_send($socket, $buffer, strlen($buffer), MSG_DONTROUTE);
// Lees resultaat
$result = null;
do {
$r = [$socket];
$n = NULL;
if (! @socket_select($r, $n, $n, TIMEOUT)) {
print "Timeout";
break;
}
$data = @socket_read($socket, 4096, PHP_NORMAL_READ);
if ($data === false) { die("Kan niet lezen van socket"); }
if ($data === "") { break; } // geen data meer om te lezen
$result .= $data;
if (strcmp(substr($result, -1), "\n") == 0) { break; }
} while (true);
socket_close($socket);
$result = trim($result);
// Interpreteer resultaat
header("Content-type: text/plain");
if (str_ends_with($result, "OK")) {
print "Bestand \"" . $filename . "\" is schoon";
} else if (str_ends_with($result, "FOUND")) {
print "Bestand \"" . $filename . "\" bevat een virus";
} else if (str_ends_with($result, "ERROR")) {
print "Fout bij scannen, zijn de leesrechten goed ingesteld?";
} else {
print "Onbekende fout";
}
?>
declare (strict_types=1);
// Gebaseerd op
// https://github.com/jonjomckay/quahog/tree/master
// https://github.com/clue/socket-raw/tree/1.x
const TIMEOUT = 30;
const CLAMD_SOCKET = '/var/run/clamav/clamd.ctl';
$filename = "/tmp/test.txt";
// Maak socket met timeout in blocking I/O
$socket = @socket_create(AF_UNIX, SOCK_STREAM, 0);
if ($socket === false) { die("Kan geen socket maken"); }
$r = NULL;
$w = [$socket];
$e = NULL;
@socket_select($r, $w, $e, TIMEOUT, 0);
if (@socket_get_option($socket, SOL_SOCKET, SO_ERROR) === false) {
die("Kan timeout niet instellen op socket");
}
if (! socket_set_block($socket)) { die("Geen blocking I/O voor socket"); }
if (! @socket_connect($socket, CLAMD_SOCKET)) { die("Kan niet verbinden met ClamAV"); }
// Geef opdracht bestand te scannen
$msg = "SCAN " . $filename;
$buffer = 'n' . $msg . "\n";
@socket_send($socket, $buffer, strlen($buffer), MSG_DONTROUTE);
// Lees resultaat
$result = null;
do {
$r = [$socket];
$n = NULL;
if (! @socket_select($r, $n, $n, TIMEOUT)) {
print "Timeout";
break;
}
$data = @socket_read($socket, 4096, PHP_NORMAL_READ);
if ($data === false) { die("Kan niet lezen van socket"); }
if ($data === "") { break; } // geen data meer om te lezen
$result .= $data;
if (strcmp(substr($result, -1), "\n") == 0) { break; }
} while (true);
socket_close($socket);
$result = trim($result);
// Interpreteer resultaat
header("Content-type: text/plain");
if (str_ends_with($result, "OK")) {
print "Bestand \"" . $filename . "\" is schoon";
} else if (str_ends_with($result, "FOUND")) {
print "Bestand \"" . $filename . "\" bevat een virus";
} else if (str_ends_with($result, "ERROR")) {
print "Fout bij scannen, zijn de leesrechten goed ingesteld?";
} else {
print "Onbekende fout";
}
?>
Met het testbestand roept ClamAV ook dat er een virus is gevonden, test.txt moet dan deze string hebben:
Zie ook https://en.wikipedia.org/wiki/EICAR_test_file
Ward van der Put op 06/09/2024 12:06:20:
Het is dat je die knipogende smiley er achter zet, anders zou ik nog geneigd zijn om inhoudelijk te reageren.
Maar die video vraagt gewoon om een reactie (ook al zit de criticus er bij). =]
Ik denk niet dat die video een goede promotie is van PHP, een (ex-)accountant vertelt over hoe gaaf PHP wel is en wat je er mee kunt doen. En natuurlijk is het zo dat je in principe van alles in PHP kunt doen, maar de vraag is niet OF het iets kan, maar HOE GOED:
- types zijn nog steeds niet echt handig in PHP, als je een int hebt en je increment die te vaak wordt het vanzelf een float. En functies zijn hoofdletterongevoelig, in tegenstelling tot variabelen. De volgorde waarin je variabelen in standaardfuncties zet is nog steeds verwarrend. En je kunt nog steeds functies callen met meer variabelen dan er in passen.
Neem nou deze code:
Waarom pas een error bij het aanroepen van de functie? PHP kan toch meteen weten dat het fout gaat?
- traits zijn leuk, en gelukkig zijn er tools in IDE's die je helpen enkele fouten te voorkomen. Maar traits zijn lang niet zo krachtig als in... een andere taal. Je kunt ze in PHP helaas niet gebruiken als types van een functieargument. Hoe ondersteunt PHP dan compositie? Alleen maar met traits als een geautomatiseerde copy-paste?
- syntax suger als array destructioning en anonymous classes, generators en closures en de coalesce operator en named arguments scheelt ietsje voor de snelheid van programmeren, maar niet genoeg om daar nou een punt van te maken. Code wordt zelfs gevoeliger voor menselijke fouten door de null chaining operator, het feit dat && iets anders betekent dan 'and' etc.
Het zou handiger zijn als ze PHP beter te volgen maken, zoals geen haakjes bij elk if-statement. Daar heeft iedereen meteen iets aan.
In plaats daarvan komen ze met extra rommel als dat je bij een contructor een type "public readonly" kan noemen. Waarom niet gewoon "protected"? Maar ze voegen die onzin liever toe om hip over te komen.
- 'match' heeft potentie, maar PHP helpt je nog steeds niet met het afvangen van fouten. Deze code geeft gek genoeg geen error:
Code (php)
Je zou toch verwachten dat PHP met minimaal een warning aangeeft dat ik Self::B ben vergeten?
- Wat wel een echte verbetering is, is dat je variabelen kan annoteren dat de gegevens niet gelogd worden. Al heb je daar weinig aan als de webserver ook je wachtwoord opslaat in de HTTP-logging. Of als je je wachtwoord doorstuurt naar de database om het daar pas te decrypten.
Alle echte grote problemen die PHP heeft zijn nog steeds niet opgelost: de afhankelijkheid van configuratie of en hoe iets überhaupt werkt, het gemak waarmee je inefficiënte programma's die slecht te onderhouden zijn maakt en dat er geen duidelijke errors zijn wanneer er dingen niet goed werken terwijl je er van uit gaat dat ze dat wel zouden doen.
Het maakt dat PHP nog steeds complexiteit verbergt waardoor je vaak niet zeker bent of je code goed is, terwijl wel (in een aantal gevallen) lijkt te werken.
En het andere punt, omdat PHP nog verre van perfect is, is zelfs een minor update van 8.2 naar 8.3 weer een major update met weer opnieuw breaking changes. Elke verbetering van PHP gaat elk jaar weer ten koste van je eigengeschreven code!
Mocht je afhankelijk zijn van een library van iemand die niet werkt met de nieuwe PHP, dan zit je met de gebakken peren. En je mag zowieso je eigen codebase weer nalopen (of zelfs unit-tests schrijven of een int een int blijft, en dat soort tijd die je beter aan het leren kennen van serieuzer gereedschap had kunnen besteden.)
In ieder geval had ik in Rust zo een library die meteen met ClamAV scant. En die library blijft het gegarandeerd doen, ook over 10 jaar.
Dat soort dingen en bovengenoemde is het verschil met andere talen. En de reden waarom ik de resterende sites op termijn ook over ga zetten naar Rust.
Tenzij PHP bijdraait. Maar dat zie ik niet snel gebeuren, daarvoor moet er te veel veranderen. En hoe meer onzin er wordt toegevoegd aan PHP hoe lastiger het wordt om dat te verbeteren.