Skip to content

Commit 64d1918

Browse files
Add security docs
1 parent 9fd7a22 commit 64d1918

File tree

2 files changed

+93
-0
lines changed

2 files changed

+93
-0
lines changed

docs/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
This document provides detailed examples for the different input filters available in Ionizer.
44

5+
**Other Documents**
6+
7+
* [Preventing NoSQL Injection with Ionizer](nosql-injection-prevention.md)
8+
9+
**Contents of This Document**
10+
511
* [Scalar Type Filters](#scalar-type-filters)
612
* [`BoolFilter`](#boolfilter)
713
* [`FloatFilter`](#floatfilter)

docs/nosql-injection-prevention.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Preventing NoSQL Injection with Ionizer
2+
3+
NoSQL databases like MongoDB are powerful, but they can be vulnerable to injection attacks if user input is not handled
4+
carefully. This document explains how "request injection" attacks work in PHP with MongoDB and how to use Ionizer to
5+
prevent them.
6+
7+
## The Vulnerability: Request Injection
8+
9+
When building queries for MongoDB in PHP, it's common to use associative arrays. For example, to find a user by their
10+
username, you might construct a query like this:
11+
12+
```php
13+
<?php
14+
$query = new \MongoDB\Driver\Query(['username' => $_GET['username']]);
15+
```
16+
17+
> ![NOTE]
18+
> Professional developers will not recklessly handle superglobals like this and expect to be secure, but it's a good,
19+
> simplified example to work with. In practice, the avenues for setting up this attack are more subtle.
20+
21+
If a user visits `http://example.com?username=alice`, the query becomes `['username' => 'alice']`. All is well so far.
22+
23+
However, PHP has a feature where it can parse query string parameters with square brackets into nested arrays. An
24+
attacker can exploit this. For example, if they craft a URL like this:
25+
26+
`http://example.com?username[$ne]=foo`
27+
28+
PHP will parse `$_GET['username']` into `['$ne' => 'foo']`. Your MongoDB query then becomes:
29+
30+
```php
31+
<?php
32+
$query = new \MongoDB\Driver\Query(['username' => ['$ne' => 'foo']]);
33+
```
34+
35+
This query will select all documents where the `username` is **not equal to** `foo`. This could potentially return all
36+
users in your database, leading to a data leak. This is a form of NoSQL injection.
37+
38+
## The Solution: Strict Input Validation with Ionizer
39+
40+
The best way to prevent this type of vulnerability is to strictly validate all user input before it's used in a database
41+
query. You need to ensure that the data is of the expected type and format.
42+
43+
Ionizer is a library that makes this easy. It allows you to define a set of filters for your expected input. If the
44+
input doesn't match the filters, Ionizer will throw an exception, and you can reject the request.
45+
46+
### Example: Using Ionizer to Sanitize Query Parameters
47+
48+
Here's how you can use Ionizer to protect the example above:
49+
50+
```php
51+
<?php
52+
53+
use ParagonIE\Ionizer\GeneralFilterContainer;
54+
use ParagonIE\Ionizer\Filter\StringFilter;
55+
56+
// 1. Define your filters. We expect 'username' to be a string.
57+
$filterContainer = new GeneralFilterContainer();
58+
$filterContainer->addFilter(
59+
'username',
60+
// We can also add a regex pattern for the username format.
61+
(new StringFilter())->setPattern('^[A-Za-z0-9_\-]{3,24}$')
62+
);
63+
64+
try {
65+
// 2. Process the input against the filters.
66+
// Ionizer will ensure $_GET contains a 'username' key, and its value is a string.
67+
// If $_GET['username'] is an array (like in the attack scenario),
68+
// a TypeError will be thrown.
69+
$filteredInput = $filterContainer($_GET);
70+
71+
// 3. Use the sanitized input in your query.
72+
$query = new \MongoDB\Driver\Query(['username' => $filteredInput['username']]);
73+
74+
// ... proceed to execute the query safely.
75+
76+
} catch (\TypeError $ex) {
77+
// 4. Handle invalid input.
78+
// The input did not match our filter rules.
79+
// Log the error and return an appropriate HTTP response (e.g., 400 Bad Request).
80+
header("HTTP/1.1 400 Bad Request");
81+
echo "Invalid input.";
82+
exit;
83+
}
84+
```
85+
86+
By using Ionizer to validate that `username` is a string, you prevent the attacker from injecting a malicious array
87+
into your MongoDB query, effectively mitigating the request injection vulnerability.

0 commit comments

Comments
 (0)