Core Grasp

Computer, Development 8月 28, 2007 #PHP
(Last Updated On: 2018年8月13日)

遅ればせながらCore Graspのパッチを読みました。超ななめ読みなので勘違いしているかも知れません。間違っていたら教えてください。

一番興味があったのはSQLインジェクションの自動検出はどうなっているのかです。以下の関数がSQLインジェクションチェックに利用されています。

+int grasp_check_query(zval *z)
+{
+	char *c,*s;
+	int i,j,l;
+	char q;
+
+	if (grasp_isfull_p(z)) return 0;
+	if (!grasp_isptr_p(z)) return 1;
+	if (z->type != IS_STRING && z->type != IS_CONSTANT) return 1;
+	
+	
+	l = z->value.str.len;
+	c = z->value.str.val;
+	s = z->secmark;
+
+
+	for(i = 0; i<l; i++)
+	{
+		if (s[i] && (c[i]=='-'))			
+		{
+			i++;					
+			if(!(c[i] < '0' || c[i] > '9')) 
+			{
+				for(i;i<l;i++)			
+				{
+					if (c[i] < '0' || c[i] > '9')
+					{
+						if (!s[i])
+							break;	
+						else
+							return 0; 
+					}
+				}
+			}
+			else
+			{
+				return 0;
+			}
+			if(i == l) return 1;
+		}
+		
+
+		if (s[i] && (c[i] < '0' || c[i] > '9')) return 0;
+		
+		if (c[i] == '¥¥')
+		{
+			i++; 
+			if (s[i]) return 0;
+		} else
+		if (c[i] == '¥'' || c[i] == '¥"' || c[i] == '`')
+		{
+			q = c[i];
+			for(i++; i<l; i++)
+			{
+				if (c[i] == '¥¥')
+					i++; else
+				if (c[i] == q)
+					break;
+			}
+			if (i<l && s[i]) return 0;
+		} 
+	}
+	return 1;
+}

どうも文字列の各文字が安全かチェックして安全でない場合はエラーにしているようです。単純に「この文字列(ZVAL)は関数で処理済みで安全」とマークしている訳でないようです。

ではmysql_real_escape_stringとかを拡張して安全マークを追加しているのかな?と思ったらしていないようです。代わりにaddslashesが拡張されていました。

PHP_FUNCTION(addslashes)
 {
 	zval **str;
+#ifdef HAVE_GRASP
+	char *res, *secm;	
+#endif
 
 	if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &str) == FAILURE) {
 		WRONG_PARAM_COUNT;
@@ -2884,11 +3266,23 @@ PHP_FUNCTION(addslashes)
 	if (Z_STRLEN_PP(str) == 0) {
 		RETURN_EMPTY_STRING();
 	}
+#ifdef HAVE_GRASP
+	grasp_normalize(*str);
+	res = 0;
+	secm = 0;
+	//grasp_setptr_p(return_value, zend_get_parameters_secmark());
+	res = php_addslashes_secmark(Z_STRVAL_PP(str),
+	                             Z_STRLEN_PP(str), 
+	                             &Z_STRLEN_P(return_value), 0, &secm, Z_SECMARK_PP(str)
+	                             TSRMLS_CC);
 
+	RETURN_STRING_SECMARK(res, 0, secm);
+#else
 	RETURN_STRING(php_addslashes(Z_STRVAL_PP(str),
 	                             Z_STRLEN_PP(str), 
 	                             &Z_STRLEN_P(return_value), 0 
 	                             TSRMLS_CC), 0);
+#endif
 }

マルチバイト環境ではaddslashesによる文字列エスケープは厳禁な(SQLインジェクションに脆弱になる場合がある)ので実装的には不十分です。

GraspはPHP用のtaintモードとして紹介されていますが、Perl, Rubyなどの一般的にtaintモード呼ばれている機能と多少異なります。

http://www.cs.virginia.edu/~evans/pubs/infosec05.html

に記載されているようなtaintモードになっているようです。
# この論文もななめ読み

具体的には

$sql = “SELECT * FROM table WHERE id = “. $id;
mysql_query($sql);

の様なコードでも$idが危険な文字列かつ攻撃コードを含んでいれば、エラーになります。つまり、$sql文字列中の$id部分が安全かどうかチェックできるようになっています。

例えば、

if (ereg(“[0-9]+”, $_GET[‘id’]) {
$id = $_GET[‘id’];
}

の様にプログラマのミスにより$idが危険な変数になっていても攻撃が回避できます。

完成度が高くなれば通常のtaintモードとは比べ物にならないくらい安全性の向上が期待できると思います。しかし、パッチを超ななめ読みした感じでは実用的になるまでもうしばらく時間が必要な気がしましたが、順調に開発が進めばかなり便利な機能になると思います。
# 現状の実装だど文字エンコーディングがSJIS等の場合はSQLインジェクション
# と誤検出すると思います。addslashesなので元々SJIS等の事などまったく考慮
# されていなくて当たり前です。マルチバイト文字の事は考えてないようですが
# UTF-8ならそれなりに使えそうです。

SQLインジェクションは対策が簡単ですが、XSSは適材適所で対応が分かれるので精度の高い対策は難しいです。しかし、LDAP/XPATH/XQuery等のインジェクション攻撃は基本的にSQLインジェクションと同じレベルで対応可能です。半年後くらいにまたパッチを見てみよう。

投稿者: yohgaki