【注意喚起】CodeIgniter 1.7.2 Formヘルパーの脆弱性

(2011/04/11 追記) 当初記載されていた対策の修正および CodeIgniter 2.0.0 以降での対策を追記しました。

問題の所在

CodeIgniter の Formヘルパーにはフィールドの値を表示するための set_value() が用意されていますが、set_value() で同じフィールドを 2回目以上表示すると 2回目以降は文字参照に変換されず XSS脆弱性になる可能性があります。

確認ページで、入力値を表示し、隠しフィールドに値を埋め込み POST させるような実装の場合に、この脆弱性に該当します。

原因

CodeIgniter の Formヘルパーの form_prep() に問題があります。Formヘルパーは内部的に form_prep() を呼び出していますが、それが影響しています。

サンプルコード

controllers/form.php

<?php

class Form extends Controller
{
  function Form() {
    parent::Controller();
    $this->load->library('form_validation');
    $this->form_validation->set_rules('status', 'Status', 'trim|required|max_length[140]');
  }

  function index() {
    $this->form_validation->run();
    $this->load->view('form1_view');
  }
}

/* End of file form.php */
/* Location: ./system/application/controllers/form.php */


views/form1_view.php

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Form_validation Sample</title>
</head>
<body>

<?php echo validation_errors(); ?>

<form action="" method="post">
  What are you doing?
  <input type="text" name="status" value="<?php echo set_value('status'); ?>" />
  <input type="submit" value="Update" />
</form>

<?php echo set_value('status'); ?>

</body>
</html>

set_value('status') で statusフィールドに入力された値を表示していますが、下の表示ではエスケープされないため XSS が可能になります。

フォームに、

<s>test</s>

と入力した場合、出力される HTML は、以下のようになります。

<body>


<form action="" method="post">
  What are you doing?
  <input type="text" name="status" value="&lt;s&gt;test&lt;/s&gt;" />
  <input type="submit" value="Update" />
</form>

<s>test</s>
</body>

対策

(2011/04/11 追記)

helpers/form_helper.php を以下のように変更します。

--- system/helpers/form_helper.php.orig	2010-07-12 22:21:54.000000000 +0900
+++ system/helpers/form_helper.php	2011-04-11 13:40:49.647917245 +0900
@@ -617,7 +617,7 @@
 		// @todo need to figure out a way to namespace this so
 		// that we know the *exact* field and not just one with
 		// the same name
-		if (isset($prepped_fields[$field_name]))
+		if (isset($prepped_fields[$field_name]) && $prepped_fields[$field_name] === $str)
 		{
 			return $str;
 		}

(2011/04/11 追記) 当初記載されていた以下の対策は、配列の扱いに問題がありました。ただし、この対策でも脆弱性は修正されます。

--- form_helper.php.orig	2009-07-18 05:25:13.000000000 +0900
+++ form_helper.php	2010-03-15 13:28:09.000000000 +0900
@@ -619,7 +619,7 @@
 		// the same name
 		if (isset($prepped_fields[$field_name]))
 		{
-			return $str;
+			return $prepped_fields[$field_name];
 		}
 		
 		$str = htmlspecialchars($str);

(2011/04/11 追記) CodeIgniter 2.0.0 以降は、以下のように変更します。

--- a/system/helpers/form_helper.php
+++ b/system/helpers/form_helper.php
@@ -631,7 +631,7 @@ if ( ! function_exists('form_prep'))
 		// @todo need to figure out a way to namespace this so
 		// that we know the *exact* field and not just one with
 		// the same name
-		if (isset($prepped_fields[$field_name]))
+		if (isset($prepped_fields[$field_name]) && $prepped_fields[$field_name] === $str)
 		{
 			return $str;
 		}
@@ -643,7 +643,7 @@ if ( ! function_exists('form_prep'))
 
 		if ($field_name != '')
 		{
-			$prepped_fields[$field_name] = $field_name;
+			$prepped_fields[$field_name] = $str;
 		}
 
 		return $str;