在php里实现简单而安全的SQL参数转义

我有一天想到一个问题,json和sql的转义字符这么像(其实很多数据格式的特殊字符都很像),能不能共用一下,我现在认为是可以的。

最初我实现了这样一段代码:

<?php
$username = isset($_GET['username']) ? sqlEscape($_GET['username']) : '""';
$password = isset($_GET['password']) ? sqlEscape($_GET['password']) : '""';

$sql = "SELECT * FROM user where username = {$username} AND password = {$password}";

print $sql;

function sqlEscape($param)
{
	if (is_string($param)) {
		return json_encode($param, JSON_UNESCAPED_UNICODE);
	} else {
		return json_encode(json_encode($param, JSON_UNESCAPED_UNICODE), JSON_UNESCAPED_UNICODE);
	}
}

到这里就差不多了,但是我想了想,前面两段赋值语句,要对每个变量做过滤,有点太啰嗦了,于是改了一下

<?php

$s = new S();

isset($_GET['username'], $_GET['username']) OR exit("username and password is required");

print "SELECT * FROM user WHERE username = {$s->{$_GET['username']}} AND password = {$s->{$_GET['password']}}";

/**
 * SQL转义类
 */
class S
{
	function __get($param)
	{
		if (is_string($param) || is_numeric($param) || is_bool($param) || is_null($param)) {
			return json_encode($param, JSON_UNESCAPED_UNICODE);
		} else {
			return json_encode(json_encode($param, JSON_UNESCAPED_UNICODE), JSON_UNESCAPED_UNICODE);
		}
	}
}

这样一来,实现的就相对优雅一点了

当然,现代化的技术是采用预编译(即参数化查询),原生的参数化查询需要为每一个代码编写冗长的逻辑,于是诞生了一些查询框架,其中最知名的是https://github.com/illuminate/database,内置于laravel,中文社区可能用thinkphp比较多,查询框架见https://github.com/top-think/think-orm

这篇文章的意义主要还是在一些小的思考,可以以一些简单、有效、优雅的方式解决一些小问题(我觉得像那种用双引号、单引号、连字符拼接出来的SQL实在是丑)

此外,任何基于转义的处理方案都不能解决宽字节注入的问题,宽字节问题属于编码不规范导致的,统一后端代码和数据库的编码才能解决:应该统一为utf8(mb4)

完。

2 Likes

你有考虑 json_encode 和 addslashes 的性能吗?

压测了一下,在纯字符串的情况下,如果生成同样的字符串

如果只运行一次

显然是连字符影响了结果,那么在生成一个同样的sql语句的情况下

那么其实后者还是会多使用了一个连字符,性能还是会受到影响,但是显然性能差距并没有大到需要考虑,而且在这个例子中,省下的性能又以另一种方式再次占用了计算资源

以前我会考虑性能问题,但是现实的反馈是,在一个开发项目中,性能往往不是首要问题,相比网络的延迟,数据处理的时间往往不值一提

就像python,快速开发但是臃肿,好在有jython作为加速器,甚至像在php中实现路由,或者使用laravel、各种依赖,的确影响性能,但是影响非常有限,并且我们也有加速方案,比如swoole

此外json_encode 和 addslashes有很大的区别,像一些通过request传入的参数,除了字符串还有可能是数组形式,在这种情况下,addslashes无法直接胜任,而json给了我们一个通用的实现方案。当然,从开发规范的角度来说,应当在执行之前做一个类型检查,而addslashes则提供了基于字符串层面的高效转义,但是从快速开发的角度来说,通过json_encode,可以以相当少而简洁的代码实现安全入库,所以为什么不呢

当然,这篇文章其实,主要还是一些小的思考,倒也不必太过计较

1 Like