1
0
mirror of https://github.com/php/doc-pl.git synced 2026-03-24 07:02:07 +01:00
Files
archived-doc-pl/security/filesystem.xml
2024-10-27 10:29:47 +01:00

237 lines
7.7 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: 91570644fbbe4d23e79908e1a04c4c4d003fe587 Maintainer: sobak Status: ready -->
<!-- splitted from ./index.xml, last change in rev 1.66 -->
<chapter xml:id="security.filesystem" xmlns="http://docbook.org/ns/docbook">
<title>Bezpieczeństwo systemu plików</title>
<simpara>
<acronym>PHP</acronym> podlega zabezpieczeniom wbudowanym w większość systemów serwerowych,
jeśli chodzi o uprawnienia do plików i katalogów. To pozwala
na kontrolę, które pliki w systemie plików mogą być odczytywane. Powinno
się zwracać uwagę na wszelkie pliki odczytywalne dla wszystkich, aby upewnić się,
że mogą one być bezpiecznie odczytane przez wszystkich użytkowników, którzy mają dostęp do tego
systemu plików.
</simpara>
<simpara>
Jako iż <acronym>PHP</acronym> został zaprojektowany, aby pozwolić na dostęp na poziomie użytkownika do systemu plików,
jest absolutnie możliwe, by napisać skrypt <acronym>PHP</acronym>, który pozwoli Ci na
odczytanie plików systemowych takich jak <filename>/etc/passwd</filename>, modyfikację połączeń
ethernet, wysłanie zadań do drukarki itd. Ma to pewne
oczywiste implikacje — musisz się upewnić, że pozwalasz
na odczyt i zapis właściwych plików.
</simpara>
<simpara>
Przeanalizuj poniższy skrypt, w którym użytkownik decyduje, że chciałby
usunąć plik w swoim katalogu domowym. Zakłada on
sytuację, w której interfejs webowy <acronym>PHP</acronym> jest często używany do
zarządzania plikami, więc użytkownik Apache ma uprawnienia do usuwania plików
w katalogu domowym użytkownika.
</simpara>
<para>
<example>
<title>Złe sprawdzanie zmiennych prowadzi do...</title>
<programlisting role="php">
<![CDATA[
<?php
// Usuń plik z katalogu domowego użytkownika
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
unlink("$homedir/$userfile");
echo "Plik został usunięty!";
?>
]]>
</programlisting>
</example>
Ponieważ nazwa użytkownika i nazwa pliku pochodzą z formularza,
użytkownik może wysłać nazwę użytkownika i pliku należące do kogoś innego,
i skasować ich pliki nawet jeśli nie powinni mieć do tego uprawnień.
W tym wypadku powinno się użyć jakiejś innej formy autentykacji.
Zastanów się, co mogłoby się wydarzyć, jeśli przekazano by zmienne
<literal>"../etc/"</literal> i <literal>"passwd"</literal>.
Kod mówiłby wtedy:
<example>
<title>...ataku na system plików</title>
<programlisting role="php">
<![CDATA[
<?php
// Usuwa plik z dowolnego miejsca na dysku, do którego
// ma dostęp użytkownik PHP. Jeśli PHP ma dostęp roota:
$username = $_POST['user_submitted_name']; // "../etc"
$userfile = $_POST['user_submitted_filename']; // "passwd"
$homedir = "/home/$username"; // "/home/../etc"
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
echo "Plik został usunięty!";
?>
]]>
</programlisting>
</example>
Istnieją dwie konkretne akcje, które powinieneś podjąć aby zapobiec
tym problemom.
<itemizedlist>
<listitem>
<simpara>
Plik binarny <acronym>PHP</acronym> wykorzystywany przez strony internetowe powinien mieć ogranicozne uprawnienia,
</simpara>
</listitem>
<listitem>
<simpara>
Sprawdzaj wszystkie przesyłane zmienne.
</simpara>
</listitem>
</itemizedlist>
Oto poprawiony skrypt:
<example>
<title>Bardziej bezpieczne sprawdzanie nazwy pliku</title>
<programlisting role="php">
<![CDATA[
<?php
// Usuwa z dysku plik, do którego
// ma dostęp użytkownik PHP
$username = $_SERVER['REMOTE_USER']; // użycie mechanizmu autoryzacji
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (file_exists($filepath) && unlink($filepath)) {
$logstring = "Usunięto $filepath\n";
} else {
$logstring = "Nie udało się usunąć $filepath\n";
}
$fp = fopen("/home/logging/filedelete.log", "a");
fwrite($fp, $logstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
?>
]]>
</programlisting>
</example>
Jednakże nawet ten skrypt nie jest pozbawiony wad. Jeżeli Twój system
autentykacji pozwala użytkownikom stworzyć ich własne loginy i użytkownik
wybrał login <literal>"../etc/"</literal>, to system jest ponownie w niebezpieczeństwie. Z
tego powodu możesz preferować bardziej dostosowane sprawdzenie:
<example>
<title>Bardziej bezpieczne sprawdzanie nazwy pliku</title>
<programlisting role="php">
<![CDATA[
<?php
$username = $_SERVER['REMOTE_USER']; // użycie mechanizmu autoryzacji
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die("Bad username/filename");
}
// etc.
?>
]]>
</programlisting>
</example>
</para>
<para>
W zależności od systemu operacyjnego, istnieją różne pliki, o których
ochronę powinieneś się zatroszczyć, w tym wpisy urządzeń (<filename>/dev/</filename>
lub <filename>COM1</filename>), pliki konfiguracyjne (pliki w <filename>/etc/</filename> oraz
pliki <literal>.ini</literal>), powszechnie znane miejsca przechowywania plików (<filename>/home/</filename>,
<filename>Moje Dokumenty</filename>) itd. Z tego
powodu zazwyczaj łatwiej jest stworzyć politykę, w której zabraniasz
wszystko poza wprost dozwolonymi plikami.
</para>
<sect1 xml:id="security.filesystem.nullbytes">
<title>Problemy związane z pustym bajtem</title>
<simpara>
Jako iż <acronym>PHP</acronym> wykorzystuje pod maską funkcje C do obsługi operacji na
systemie plików, puste bajty mogą być obsługiwane w nieco zaskakujący sposób.
Jako że puste bajty oznaczają koniec ciągu znaków w C, ciągi znaków zawierające je
nie będą interpretowane w całości a jedynie do wystąpienia pustego bajtu.
Następujący przykład pokazuje podatny kod, który demonstruje ten problem:
</simpara>
<example>
<title>Skrypt podatny na puste bajty</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file']; // "../../etc/passwd\0"
if (file_exists('/home/wwwrun/' . $file . '.php')) {
// file_exists zwróci true, ponieważ plik /home/wwwrun/../../etc/passwd istnieje
include '/home/wwwrun/' . $file . '.php';
// Plik /etc/passwd zostanie dołączony
}
?>
]]>
</programlisting>
</example>
<para>
Z tego powodu każdy skrypt, który wykonuje operacje na systemie plików powinien być zawsze
dokładnie sprawdzony. Oto lepsza wersja poprzedniego przykładu:
</para>
<example>
<title>Poprawne sprawdzanie danych wejściowych</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file'];
// Biała lista dozwolonych wartości
switch ($file) {
case 'main':
case 'foo':
case 'bar':
include '/home/wwwrun/include/' . $file . '.php';
break;
default:
include '/home/wwwrun/include/main.php';
}
?>
]]>
</programlisting>
</example>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->