Att skicka e-post från PHP-FPM när AppArmor säger nej
Kontaktformuläret på denna sajt anropar inte mail(). Det pratar SMTP direkt mot loopback över ett TCP-uttag, för hand, i cirka fyrtio rader PHP. Anledningen till att vi gör så var ett säkerhetskrav från AppArmor, och lösningen visade sig vara en avsevärt bättre design.
När AppArmor säger nej
Under Ubuntu körs PHP-FPM under en AppArmor-profil som begränsar vad processen får göra. En av de saker profilen blockerar är att exekvera externa binärer, inklusive /usr/sbin/sendmail. När du anropar mail() i PHP försöker den starta sendmail som en underprocess. Profilen nekar detta och anropet misslyckas tyst eller kastar ett fel.
Den vanliga reaktionen är att försvaga sandlådan: redigera AppArmor-profilen, tillåta exekvering av sendmail eller stänga av profilen helt. Men varför skulle en webbprocess som tolkar användarinmatning över huvud taget ha rätt att starta godtyckliga program på servern? Det är precis den attackvektorn AppArmor är till för att stoppa.
Lösningen: Tala SMTP direkt
I stället för att förlita oss på externa binärer öppnar vi en ren nätverksanslutning mot 127.0.0.1:25 och skickar meddelandet via SMTP:
$fp = @fsockopen('127.0.0.1', 25, $errno, $errstr, 3);
// HELO, MAIL FROM, RCPT TO, DATA, QUIT
Detta kräver inga externa processer, inga breddade AppArmor-rättigheter och ingen tung tredjepartsbibliotek som PHPMailer för ett enkelt kontaktformulär.
Fördelarna med att arbeta med sandlådan
- Ingen processstart. Att öppna en lokal TCP-socket tar under en millisekund, jämfört med att starta en hel binär.
- Omedelbar återkoppling. Om den lokala e-postservern inte svarar vet vi det direkt och kan visa ett hjälpsamt felmeddelande för besökaren.
- Bibehållen säkerhetsgräns. PHP-FPM förblir helt låst utan rättigheter att starta program i operativsystemet.
Det är en enkel princip: anpassa koden efter säkerhetsmodellen istället för att kompromissa med säkerhetsmodellen för att passa gammal kod.