Files
archived-doc-pt-br/security/filesystem.xml
Leonardo Lara Rodrigues 3ee3071c6c fix and improve translations
2025-09-29 11:40:16 -03:00

237 lines
7.7 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 91570644fbbe4d23e79908e1a04c4c4d003fe587 Maintainer: rafaelbernard Status: ready --><!-- CREDITS: rafaelbernard,narigone,leonardolara -->
<!-- splitted from ./index.xml, last change in rev 1.66 -->
<chapter xml:id="security.filesystem" xmlns="http://docbook.org/ns/docbook">
<title>Segurança do Sistema de Arquivos</title>
<simpara>
O <acronym>PHP</acronym> está sujeito à segurança encontrada na maioria dos sistemas de servidor
com respeito às permissões de arquivos e diretórios. Isso permite que
seja controlado que arquivos no sistema podem ser lidos e por quem. É preciso
ter cuidado com quaisquer arquivos que são lidos por todos para assegurar
que eles podem ser lidos por todos os usuários que têm acesso ao
sistema de arquivos.
</simpara>
<simpara>
Já que o <acronym>PHP</acronym> foi projetado para permitir acesso em nível de usuário ao sistema
de arquivos, é possível escrever um script <acronym>PHP</acronym> que permitirá
ler arquivos do sistema como o <filename>/etc/passwd</filename>, modificar suas conexões
de rede, enviar inúmeros trabalhos de impressão, etc. Isso tem
algumas implicações óbvias, já que é necessário garantir que os arquivos
lidos e gravados são apropriados.
</simpara>
<simpara>
Considere o seguinte script, onde um usuário indica que quer
apagar um arquivo no seu diretório "home". Isso presume uma situação
onde uma interface web <acronym>PHP</acronym> é usada regularmente para controle
de arquivos, então o usuário do Apache tem permissão de apagar arquivos
nos diretórios "home" dos usuários.
</simpara>
<para>
<example>
<title>Verificação fraca de variáveis resulta em....</title>
<programlisting role="php">
<![CDATA[
<?php
// Remove um arquivo do diretório "home" do usuário
$username = $_POST['user_submitted_name'];
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
unlink("$homedir/$userfile");
echo "O arquivo foi removido!";
?>
]]>
</programlisting>
</example>
Já que o nome do usuário e do arquivo são enviados pelo formulário,
um usuário pode enviar um nome de usuário e de arquivo que pertença a outra pessoa,
e apagá-lo, mesmo que eles não tenham permissão para fazê-lo.
Nesse caso, é preciso ter alguma outra forma de autenticação.
Considere o que poderia acontecer se as variáveis enviadas forem
<literal>"../etc/"</literal> e <literal>"passwd"</literal>.
O código então leria efetivamente:
<example>
<title>... um ataque ao sistema de arquivos</title>
<programlisting role="php">
<![CDATA[
<?php
// Remove um arquivo de qualquer lugar no disco rígido
// que o usuário do PHP tenha acesso. Se o PHP tiver acesso de administrador (root):
$username = $_POST['user_submitted_name']; // "../etc"
$userfile = $_POST['user_submitted_filename']; // "passwd"
$homedir = "/home/$username"; // "/home/../etc"
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
echo "O arquivo foi removido!";
?>
]]>
</programlisting>
</example>
Existem duas medidas importantes que você deve tomar para prevenir
esses problemas.
<itemizedlist>
<listitem>
<simpara>
Dar permissões limitadas ao usuário web binário do <acronym>PHP</acronym>.
</simpara>
</listitem>
<listitem>
<simpara>
Checar todas as variáveis que são enviadas.
</simpara>
</listitem>
</itemizedlist>
Aqui temos um script melhorado:
<example>
<title>Checagem com mais segura do nome do arquivo</title>
<programlisting role="php">
<![CDATA[
<?php
// Remove um arquivo do disco rígido que o
// usuário do PHP tenha acesso.
$username = $_SERVER['REMOTE_USER']; // usando um mecanismo de autenticação
$userfile = basename($_POST['user_submitted_filename']);
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (file_exists($filepath) && unlink($filepath)) {
$logstring = "Removido $filepath\n";
} else {
$logstring = "Falha ao remover $filepath\n";
}
$fp = fopen("/home/logging/filedelete.log", "a");
fwrite($fp, $logstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
?>
]]>
</programlisting>
</example>
No entanto, ele ainda possui falhas. Se seu sistema de autenticação
permitir que os usuários criem seus próprios logins, e um usuário
escolher o login <literal>"../etc/"</literal>, o sistema está novamente exposto. Por
essa razão, é preferível escrever uma verificação mais personalizada:
<example>
<title>Verificação mais segura do nome do arquivo</title>
<programlisting role="php">
<![CDATA[
<?php
$username = $_SERVER['REMOTE_USER']; // usando um mecanismo de autenticação
$userfile = $_POST['user_submitted_filename'];
$homedir = "/home/$username";
$filepath = "$homedir/$userfile";
if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $userfile)) {
die("Usuário/arquivo inválido");
}
// etc.
?>
]]>
</programlisting>
</example>
</para>
<para>
Dependendo do sistema operacional, existe uma variedade enorme de arquivos
com os quais se preocupar, incluindo dispositivos (<filename>/dev/</filename>
ou <filename>COM1</filename>), arquivos de configuração (arquivos <filename>/etc/</filename>
e arquivos <literal>.ini</literal>), áreas de armazenamento de arquivo bem conhecidas (<filename>/home/</filename>,
<filename>My Documents</filename>), etc. Por essa
razão, normalmente é mais fácil criar uma política onde se proíbe
tudo exceto aquilo que for explicitamente permitido.
</para>
<sect1 xml:id="security.filesystem.nullbytes">
<title>Problemas relacionados aos bytes nulos (NUL)</title>
<simpara>
Como o <acronym>PHP</acronym> usa funções em C para operações relacionadas ao
sistema de arquivos, ele pode lidar com bytes nulos de maneira inesperada.
Como bytes nulos denotam fim de string em C, strings que os contém
não serão consideradas de forma inteira, mas apenas até que um byte nulo ocorra.
O seguinte exemplo mostra um código vulnerável que demonstra esse problema:
</simpara>
<example>
<title>Script vulnerável a bytes nulos</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file']; // "../../etc/passwd\0"
if (file_exists('/home/wwwrun/' . $file. '.php')) {
// file_exists retornará true porque o arquivo /home/wwwrun/../../etc/passwd existe
include '/home/wwwrun/' . $file . '.php';
// o arquivo /etc/passwd será incluído
}
?>
]]>
</programlisting>
</example>
<para>
Portanto, qualquer string comprometida que é usada em uma operação de sistema de arquivos deve
sempre ser validada corretamente. Aqui está uma versão melhorada do exemplo anterior:
</para>
<example>
<title>Validando entrada corretamente</title>
<programlisting role="php">
<![CDATA[
<?php
$file = $_GET['file'];
// Autorizando valores possíveis
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
-->