Ticket #56: code_audit

File code_audit, 9.9 KB (added by spam1@…, 3 years ago)

Code audit findings

Line 
1
2Remarks:
3
4    * Each bug is only reported the first time it has been seen, though it may
5      occur multiple times.
6
7    * Many of the bugs and minor issues reported are merely defensive coding
8      practices and may therefor seem overly pedantic. But remember, even
9      though code may not be exploitable now does not mean it won't become so
10      in the future. Defensive code minimizes uncaught and future security
11      problems.
12
13
14Global
15
16    Bug:
17
18        Check for usage of magic_quotes_gpc(). If this setting is on, all
19        incoming data should be stripped of slash-escaped quotes. MvBlog already
20        escapes dangerous characters itself and if magic_quotes_gpc() is turned
21        on, quotes will be escaped twice.
22
23    Bug:
24
25        Users can impersonate other users when commenting on a story. There
26        does not seem to be a way to prevent people from impersonating
27        a user. A possible remedy for this would be to allow users to
28        create an account.
29
30    Bug:
31
32        Use of sprintf() for query building might cause unintended behaviour.
33        All %d replacements whose parameter is not an integer will be converted
34        to '0'. This will in many cases not be a problem, but in some cases
35        it could be. Perhaps parameters should be checked for is_int() and
36        > 0 to prevent bogus entries from being entered into the database
37        > (even though these probably do not cause any
38        security risks)
39
40    Security Enhancement:
41
42        Defined states. MvBlog has many places where states are undefined.
43        Undefined states in a program are a cause of possible (but unseen)
44        security problems and bugs. An undefined state is an atomic piece
45        of code (object, method, function, include) which recieves input about
46        which no assumptions can be made. For instance:
47
48        function show_posts($options = array()) {
49            global $db;
50            if (!$options["top"]) { $options["top"] = 0; } else { $options["top"] = (int)$options["top"]; }
51            if (!$options["limit"]) { $options["limit"] = 15; } else { $options["limit"] = (int)$options["limit"]; }
52            //put all categories in array
53            $res =& $db->query("SELECT * FROM categories");
54            if (PEAR::isError($res)) {
55                die($res->getMessage());
56            }
57            while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
58                $cats[$row["id"]] = $row["name"];
59            }
60            $cats[-1] = "asides";
61            if ($options["month"] && $options["year"]) {
62
63        In the above code (taken from admin/index.php), the following variables
64        are in an undefined state:
65
66            $db
67                global variables tend to be problematic. What kind of variable
68                is $db? From inside the function, no assumption can (and
69                should) be made about $db being a database connection, making
70                security auditing problematic.
71
72            $options[]
73
74                No assumptions can be made about the contents of $options.
75
76    Security enhancement:
77
78        MvBlog uses exclusion instead of inclusion. For instance, variables
79        that will go into the database are stripped of quotes (') because
80        they may give problems but all other characters are considered valid.
81
82        Good security practice requires code to use inclusion by default. For
83        instance, a value, lets say an authors name, which will go into the
84        database should be checked against characters which may only appear in
85        the value. In the case of an author's name, only a through z and
86        possibly a space should be allowed.
87
88        Many security problems can thus be contained. Lets look at two possible
89        examples:
90
91        preg_quote()ing for "'" does not prevent users from entering percentage
92        signs into queries. Thus the programmer has to make sure that for every
93        query that uses the LIKE syntax not only the quotes are stripped, but
94        also all special characters which can be used in the LIKE syntax.
95
96        Another example is that of character sets. Does preg_quote() work with
97        different character sets? Suppose our database is of the charset UTF8,
98        but PHP is of the charset ISO8859. Will PHP strip out UTF8 version
99        of quotes? This behaviour is very hard to determine and can't be
100        counted on. It all depends on how PHP _and_ MySQL convert characters
101        from one character set to another. If only certain characters where
102        allowed though, the chances of the above happening would be practically
103        zero. If any other character could be used to perform SQL injections,
104        they would never get through because the code only allows characters in
105        the, say, A-Z range through and nothing else.
106
107xinha/
108
109    Security enhancement:
110
111        Remove examples/ directory.
112       
113        Rationale:
114            Common security vulnerabilities are related to unsecure example
115            scripts left in default installs.
116
117/INSTALL
118
119    Security enhancement:
120
121        Default admin username and password is possibly bad practice. Disable
122        mvblog admin until username and password has been manually configured.
123        If this will not be fixed, mention it also in chapter 8 of the README.
124   
125        Rationale:
126            MvBlog is vulnerable to an account take-over for a short while
127            between installation and configuration. Many users are too lazy to
128            change the default password and must therefor be protected from
129            themselves.
130
131/index.php
132   
133    Bug:
134
135        Unclosed div (if_page)
136
137/sql/mvblog.sql
138
139    Minor issue:
140
141        'ip' field in the 'comments' table is unnecessarily large (255 chars)
142
143    Minor issue:
144
145        'website' field in the 'authors' table is not large enough. Though no
146        official URL size is specified in any RFC, the minimum supported by all
147        browsers is 2048.
148
149    Minor issue:
150
151        'tb_uri' field in the 'articles' table is not large enough. See above.
152
153
154/site_images/empty.txt
155
156    Minor issue:
157
158        'empty.txt' should not appear in public releases.
159
160/debian/
161
162    Minor issue:
163
164        'debian' directory should not appear in public releases.
165
166
167common/functions_blog.php
168
169    Security enhancement:
170
171        line 50: if (!$_SESSION["author_id"]) {
172
173        Reliance on boolean value of non-boolean variable. This piece of code
174        tries to check for the availability of the 'author_id' key in the
175        $_SESSION array, but in reality it checks if the value of the
176        'author_id' key in the $_SESSION array is false. Use
177        array_key_exists() instead. This problem occurs a multitude of times
178        throughout the code.
179
180        Rationale:
181
182            Undefined or hard to define behaviour in software is a lurking
183            place for bugs and security problems. If an attacker were to be
184            able to get the value of 'false' into the "author_id" index, the
185            check would result in true and the code would be run, while that is
186            not the intended behaviour. In this case that would hardly be a
187            problem, but code changes and things that are currently not a
188            problem may change into one later.
189
190        BTW: How does this function actually verify that the administrator
191        is logged in and not just any other user? I assume there is no other
192        user than the admin?
193
194    Minor issue:
195
196        line 165: <?="http://".$_SERVER["SERVER_NAME"].substr($_SERVER["PHP_SELF"],0,strrpos($_SERVER["PHP_SELF"], "/"))."/common/tb.php?id=".$row["id"]?>
197
198        Code does not work when using HTTPS
199
200    Minor issue:
201
202        line 192: echo nl2br(stripslashes(htmlspecialchars(strip_invalid_xml($row["comment"]))));
203
204        Possible reversed order of stripslashes, htmlspecialchars,
205        strip_invalid_xml??  When inserting information into the database,
206        stripslashes() is called right before inserting the information. It
207        should then also be the very first thing that is called right after
208        retrieving information from the database, to prevent undefined
209        behaviour.
210
211    Security enhancement:
212
213        line 320: exec("ls style", $dirindex);
214
215        Unnecessary use of exec() function. Use opendir() instead.
216
217       
218    Security enhancement:
219
220        line 569: $q = "FROM articles WHERE active=1 AND public=1 AND aside = 0 AND date BETWEEN $time_start AND $time_end";
221
222        Minimize query injectability. Move 'FROM articles WHERE active=1' to
223        the non-injectable code on line 587, 593 and 595.
224
225
226        Rationale:
227
228            $q might accidentally become an injectable variable in the future.
229            By moving the 'FROM articles' portion to the actual query (making
230            that portion unmuttable), an attack only has access to the
231            'articles' table instead of all tables.
232
233    Minor issue:
234
235        line 580: case 0;
236
237        Pointless switch case. Remove.
238
239        Rationale:
240
241            The 'default' switch case already handles the 0 case. In the
242            current code '0' is a seperate case which trickels down into the
243            default case. If, in the future, someone accidentally puts a
244            'break' in the 0 case or the 0 case is moved, the $q variable might
245            become unset or injectable causing query errors and possibly
246            SQL injections.
247
248    Bug:
249
250        line 593: $res =& $db->limitQuery("SELECT * $q ORDER BY date DESC", $start, $limit);
251
252        DB::limitQuery does not specify that it converts parameters to ints.
253        Cast to int when calling to avoid possible SQL injection.
254
255/rss.php
256
257    Bug:
258
259        line 87: echo "\t\t\t<author>".$authors[$row["authors_id"]]["email"]." (".$authors[$row["authors_id"]]["fullname"].")</author>\n";
260
261        $authors[$row["authors_id"]]["email"] is not striped of HTML/XML chars
262        which may cause invalid XML code in the RSS feed.
263       
264
265admin/
266
267    Feature enhancement:
268
269        Detection for disabled cookies.
270
271    Feature enhancement:
272
273        Error message on wrong login.