dnoegel / pdo-inspector
检查PDO查询以发现可能的SQL注入
Requires
- greenlion/php-sql-parser: ^4.0
- symfony/yaml: 2.7.*
This package is auto-updated.
Last update: 2024-09-04 15:55:20 UTC
README
将装饰应用程序的PDO连接并记录所有查询,其中可能包含可能的SQL注入。
它是如何工作的?
每个查询都被拦截并解析。在解析的查询中,找到硬编码的常量值(例如 SELECT * FROM my_table WHERE value = 'hi'
)并将它们声明为可能的SQL注入。
您可以在定义的输出文件夹中找到那些查询的堆栈跟踪、请求信息以及更多详细信息。
如何使用它?
在您想测试的应用程序中,找到创建PDO连接的地方。大多数PHP软件都使用PDO,ORM如Doctrine也使用它。
示例
假设您有一个使用PDO的应用程序,如下所示
$this->_connection = new PDO(
$dsn,
$username,
$password,
$options
);
使用这个
use \\Dnoegel\\DatabaseInspection\\PDOInspectionDecorator;
try {
$this->_connection = new PDOInspectionDecorator(
new PDO(
$dsn,
$username,
$password,
$options
)
);
$this->_connection->setProblemInspector(new \\Dnoegel\\DatabaseInspection\\SqlProblemInspector(
new \\Dnoegel\\DatabaseInspection\\Storage\\JsonStorage('/tmp/sql_inspector_output'),
));
您可以装饰您的PDO连接。
因此,实际上有这三个步骤
- 包含/要求此库的自动加载器。如果您已通过composer将其安装到您的应用程序中,则可以跳过此步骤。
- 使用
PDOInspectionDecorator
装饰PDO连接,该装饰器对PDO连接完全透明,因此您的应用程序将正常使用它。 - 将
\Dnoegel\DatabaseInspection\SqlProblemInspector
的实例注入到PDOInspectionDecorator
中。SqlProblemInspector
只有一个强制要求:JsonStorage
,您可以根据需要配置以将配置文件保存到您喜欢的位置。
白名单
如果您检查了一个查询并认为它是安全的(例如,值在之前被转换为int),您可以将其移动到输出文件夹的whitelist
文件夹中。脚本将不再打扰您。
白名单将确定查询的常量部分,例如 SELECT * FROM my_table WHERE value = 'hi'
和 SELECT * FROM my_table WHERE value = 'another string'
从技术角度来看将是相同的查询。
创建问题检查器
$parser = new \\Dnoegel\\DatabaseInspection\\SqlProblemInspector(
new \\Dnoegel\\DatabaseInspection\\Storage\\JsonStorage(),
new \\Dnoegel\\DatabaseInspection\\RouteProvider\\RouteProvider(),
new \\Dnoegel\\DatabaseInspection\\Trace\\DebugTrace(),
true
)
最后一个参数将决定是否将所有发现保存到problem
文档,还是脚本应尝试区分“问题”和“问题”。“问题”是只包含标量值的发现,这仍然可能是SQL注入,这绝对不是“一切正常”——只是一个“首先检查问题”。
读取“问题”文件
每个问题文件提供以下信息
route
:触发该查询的路由/请求problems
:静态值的列表,在这种情况下0
- 因此这可以可能是白名单的code
:触发有问题查询的函数的代码sql
:正在执行的SQLtrace
:SQL的跟踪——因此您可以了解哪个文件/哪一行执行了它normalized
:解析和规范化的SQL查询——因此在这里移除了所有常量值,以便匹配其他硬编码值相同的查询
{
"route": "\/media\/checkout\/confirm",
"problems": [
{
"expr_type": "const",
"base_expr": "0",
"sub_tree": false
}
],
"code": {
"999": " public function sCountBasket()",
"1000": " {",
"1001": " return $this->db->fetchOne(",
"1002": " 'SELECT COUNT(*) FROM s_order_basket WHERE modus = 0 AND sessionID = ?',",
"1003": " array($this->session->get('sessionId'))",
"!!!": " );",
"1005": " }"
},
"sql": "SELECT COUNT(*) FROM s_order_basket WHERE modus = 0 AND sessionID = ?",
"trace": {
"7": "sBasket: sCountBasket: 84",
"8": "Shopware_Controllers_Frontend_Checkout: postDispatch: 161",
"9": "Enlight_Controller_Action: dispatch: 524",
"10": "Enlight_Controller_Dispatcher_Default: dispatch: 227",
"11": "Enlight_Controller_Front: dispatch: 148",
"12": "Shopware\\Kernel: handle: 492",
"13": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: forward: 255",
"14": "Shopware\\Components\\HttpCache\\AppCache: forward: 449",
"15": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: fetch: 349",
"16": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: lookup: 178",
"17": "Shopware\\Components\\HttpCache\\AppCache: lookup: 213",
"18": "Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache: handle: 114",
"19": "Shopware\\Components\\HttpCache\\AppCache: handle: 101"
},
"normalized": {
"SELECT": [
{
"expr_type": "aggregate_function",
"alias": false,
"base_expr": "COUNT",
"sub_tree": [
{
"expr_type": "colref",
"base_expr": "*",
"sub_tree": false
}
],
"delim": false
}
],
"FROM": [
{
"expr_type": "table",
"table": "s_order_basket",
"no_quotes": {
"delim": false,
"parts": [
"s_order_basket"
]
},
"alias": false,
"hints": false,
"join_type": "JOIN",
"ref_type": false,
"ref_clause": false,
"base_expr": "s_order_basket",
"sub_tree": false
}
],
"WHERE": [
{
"expr_type": "colref",
"base_expr": "modus",
"no_quotes": {
"delim": false,
"parts": [
"modus"
]
},
"sub_tree": false
},
{
"expr_type": "operator",
"base_expr": "=",
"sub_tree": false
},
{
"expr_type": "const",
"base_expr": "NORMALIZED",
"sub_tree": false
},
{
"expr_type": "operator",
"base_expr": "AND",
"sub_tree": false
},
{
"expr_type": "colref",
"base_expr": "sessionID",
"no_quotes": {
"delim": false,
"parts": [
"sessionID"
]
},
"sub_tree": false
},
{
"expr_type": "operator",
"base_expr": "=",
"sub_tree": false
},
{
"expr_type": "colref",
"base_expr": "?",
"no_quotes": {
"delim": false,
"parts": [
"?"
]
},
"sub_tree": false
}
]
}
}
注意
这绝不是一层安全层。 不要 这样使用它。它是一个开发工具,用于解析SQL查询,以便找到可以替换为预处理语句的常量查询部分。不要在生产环境中使用它。