security/filesystem.xml
91570644fbbe4d23e79908e1a04c4c4d003fe587
...
...
@@ -1,10 +1,10 @@
1
-
<?xml version="1.0" encoding="iso-8859-1"?>
2
-
<!-- $Revision: 1.7 $ -->
1
+
<?xml version="1.0" encoding="utf-8"?>
2
+
<!-- $Revision$ -->
3
3
<!-- splitted from ./index.xml, last change in rev 1.66 -->
4
4
<chapter xml:id="security.filesystem" xmlns="http://docbook.org/ns/docbook">
5
5
<title>Filesystem Security</title>
6
6
<simpara>
7
-
PHP is subject to the security built into most server systems with
7
+
<acronym>PHP</acronym> is subject to the security built into most server systems with
8
8
respect to permissions on a file and directory basis. This allows
9
9
you to control which files in the filesystem may be read. Care
10
10
should be taken with any files which are world readable to ensure
...
...
@@ -12,9 +12,9 @@
12
12
filesystem.
13
13
</simpara>
14
14
<simpara>
15
-
Since PHP was designed to allow user level access to the filesystem,
16
-
it's entirely possible to write a PHP script that will allow you
17
-
to read system files such as /etc/passwd, modify your ethernet
15
+
Since <acronym>PHP</acronym> was designed to allow user level access to the filesystem,
16
+
it's entirely possible to write a <acronym>PHP</acronym> script that will allow you
17
+
to read system files such as <filename>/etc/passwd</filename>, modify your ethernet
18
18
connections, send massive printer jobs out, etc. This has some
19
19
obvious implications, in that you need to ensure that the files
20
20
that you read from and write to are the appropriate ones.
...
...
@@ -22,7 +22,7 @@
22
22
<simpara>
23
23
Consider the following script, where a user indicates that they'd
24
24
like to delete a file in their home directory. This assumes a
25
-
situation where a PHP web interface is regularly used for file
25
+
situation where a <acronym>PHP</acronym> web interface is regularly used for file
26
26
management, so the Apache user is allowed to delete files in
27
27
the user home directories.
28
28
</simpara>
...
...
@@ -32,7 +32,8 @@
32
32
<programlisting role="php">
33
33
<![CDATA[
34
34
<?php
35
-
// remove a file from the user's home directory
35
+

36
+
// Remove a file from the user's home directory
36
37
$username = $_POST['user_submitted_name'];
37
38
$userfile = $_POST['user_submitted_filename'];
38
39
$homedir = "/home/$username";
...
...
@@ -40,6 +41,7 @@ $homedir = "/home/$username";
40
41
unlink("$homedir/$userfile");
41
42

42
43
echo "The file has been deleted!";
44
+

43
45
?>
44
46
]]>
45
47
</programlisting>
...
...
@@ -49,13 +51,15 @@ echo "The file has been deleted!";
49
51
and delete it even if they're not supposed to be allowed to do so.
50
52
In this case, you'd want to use some other form of authentication.
51
53
Consider what could happen if the variables submitted were
52
-
"../etc/" and "passwd". The code would then effectively read:
54
+
<literal>"../etc/"</literal> and <literal>"passwd"</literal>.
55
+
The code would then effectively read:
53
56
<example>
54
57
<title>... A filesystem attack</title>
55
58
<programlisting role="php">
56
59
<![CDATA[
57
60
<?php
58
-
// removes a file from anywhere on the hard drive that
61
+

62
+
// Removes a file from anywhere on the hard drive that
59
63
// the PHP user has access to. If PHP has root access:
60
64
$username = $_POST['user_submitted_name']; // "../etc"
61
65
$userfile = $_POST['user_submitted_filename']; // "passwd"
...
...
@@ -64,6 +68,7 @@ $homedir = "/home/$username"; // "/home/../etc"
64
68
unlink("$homedir/$userfile"); // "/home/../etc/passwd"
65
69

66
70
echo "The file has been deleted!";
71
+

67
72
?>
68
73
]]>
69
74
</programlisting>
...
...
@@ -73,7 +78,7 @@ echo "The file has been deleted!";
73
78
<itemizedlist>
74
79
<listitem>
75
80
<simpara>
76
-
Only allow limited permissions to the PHP web user binary.
81
+
Only allow limited permissions to the <acronym>PHP</acronym> web user binary.
77
82
</simpara>
78
83
</listitem>
79
84
<listitem>
...
...
@@ -88,9 +93,10 @@ echo "The file has been deleted!";
88
93
<programlisting role="php">
89
94
<![CDATA[
90
95
<?php
91
-
// removes a file from the hard drive that
96
+

97
+
// Removes a file from the hard drive that
92
98
// the PHP user has access to.
93
-
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim
99
+
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanism
94
100
$userfile = basename($_POST['user_submitted_filename']);
95
101
$homedir = "/home/$username";
96
102

...
...
@@ -101,6 +107,7 @@ if (file_exists($filepath) && unlink($filepath)) {
101
107
} else {
102
108
$logstring = "Failed to delete $filepath\n";
103
109
}
110
+

104
111
$fp = fopen("/home/logging/filedelete.log", "a");
105
112
fwrite($fp, $logstring);
106
113
fclose($fp);
...
...
@@ -113,13 +120,14 @@ echo htmlentities($logstring, ENT_QUOTES);
113
120
</example>
114
121
However, even this is not without its flaws. If your authentication
115
122
system allowed users to create their own user logins, and a user
116
-
chose the login "../etc/", the system is once again exposed. For
123
+
chose the login <literal>"../etc/"</literal>, the system is once again exposed. For
117
124
this reason, you may prefer to write a more customized check:
118
125
<example>
119
126
<title>More secure file name checking</title>
120
127
<programlisting role="php">
121
128
<![CDATA[
122
129
<?php
130
+

123
131
$username = $_SERVER['REMOTE_USER']; // using an authentication mechanisim
124
132
$userfile = $_POST['user_submitted_filename'];
125
133
$homedir = "/home/$username";
...
...
@@ -130,7 +138,8 @@ if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $us
130
138
die("Bad username/filename");
131
139
}
132
140

133
-
//etc...
141
+
// etc.
142
+

134
143
?>
135
144
]]>
136
145
</programlisting>
...
...
@@ -138,16 +147,17 @@ if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $us
138
147
</para>
139
148
<para>
140
149
Depending on your operating system, there are a wide variety of files
141
-
which you should be concerned about, including device entries (/dev/
142
-
or COM1), configuration files (/etc/ files and the .ini files),
143
-
well known file storage areas (/home/, My Documents), etc. For this
150
+
which you should be concerned about, including device entries (<filename>/dev/</filename>
151
+
or <filename>COM1</filename>), configuration files (<filename>/etc/</filename> files and
152
+
the <literal>.ini</literal> files), well known file storage areas (<filename>/home/</filename>,
153
+
<filename>My Documents</filename>), etc. For this
144
154
reason, it's usually easier to create a policy where you forbid
145
155
everything except for what you explicitly allow.
146
156
</para>
147
157
<sect1 xml:id="security.filesystem.nullbytes">
148
158
<title>Null bytes related issues</title>
149
159
<simpara>
150
-
As PHP uses the underlying C functions for filesystem related
160
+
As <acronym>PHP</acronym> uses the underlying C functions for filesystem related
151
161
operations, it may handle null bytes in a quite unexpected way.
152
162
As null bytes denote the end of a string in C, strings containing them
153
163
won't be considered entirely but rather only until a null byte occurs.
...
...
@@ -159,12 +169,16 @@ if (!ctype_alnum($username) || !preg_match('/^(?:[a-z0-9_-]|\.(?!\.))+$/iD', $us
159
169
<programlisting role="php">
160
170
<![CDATA[
161
171
<?php
172
+

162
173
$file = $_GET['file']; // "../../etc/passwd\0"
163
-
if (file_exists('/home/wwwrun/'.$file.'.php')) {
164
-
// file_exists will return true as the file /home/wwwrun/../../etc/passwd exists
165
-
include '/home/wwwrun/'.$file.'.php';
166
-
// the file /etc/passwd will be included
174
+

175
+
if (file_exists('/home/wwwrun/' . $file . '.php')) {
176
+
// File_exists will return true as the file /home/wwwrun/../../etc/passwd exists
177
+
include '/home/wwwrun/' . $file . '.php';
178
+

179
+
// The file /etc/passwd will be included
167
180
}
181
+

168
182
?>
169
183
]]>
170
184
</programlisting>
...
...
@@ -178,6 +192,7 @@ if (file_exists('/home/wwwrun/'.$file.'.php')) {
178
192
<programlisting role="php">
179
193
<![CDATA[
180
194
<?php
195
+

181
196
$file = $_GET['file'];
182
197

183
198
// Whitelisting possible values
...
...
@@ -185,11 +200,12 @@ switch ($file) {
185
200
case 'main':
186
201
case 'foo':
187
202
case 'bar':
188
-
include '/home/wwwrun/include/'.$file.'.php';
203
+
include '/home/wwwrun/include/' . $file . '.php';
189
204
break;
190
205
default:
191
206
include '/home/wwwrun/include/main.php';
192
207
}
208
+

193
209
?>
194
210
]]>
195
211
</programlisting>
...
...
@@ -209,7 +225,7 @@ sgml-indent-step:1
209
225
sgml-indent-data:t
210
226
indent-tabs-mode:nil
211
227
sgml-parent-document:nil
212
-
sgml-default-dtd-file:"../../manual.ced"
228
+
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
213
229
sgml-exposed-tags:nil
214
230
sgml-local-catalogs:nil
215
231
sgml-local-ecat-files:nil
216
232