[PHP] CodeIgniter Xss_clean

[PHP] CodeIgniter Xss_clean

Last modified on 2025-04-18 , by hjjae2

CodeIgniter xss_clean() 메서드 살펴보기


개요 #

오픈소스에서 제공하는 xss_clean() 메서드를 사용하고 있는데, 오탐으로 인해 사용자가 입력한 데이터가 변경되어 문제가 발생했다.


문제가 발생한 문자열은 아래와 같다.

원본 데이터

...
HgrSLy0VBO71Kjbr3Co7h76WU0QB1rVT61QGGT5/M/9pPf/muYF3eSdTkxDOZkXZT8vFUwbZiUyX
BGVvMC1epaCn329BFB4G3B+gJPa5k2OjlfogcT0jYWA0sUe1O/7bmW6sEfMwoTqh/VbGQLc/Eawz
mB8+Oxgvkhri175eT62jHhCKyseBKZU4JvyOLNzyMh4g3UU6TkuzKdLdc2Lmk4uEDT5qOs2Kkqnf
KuJyrG3L5oNFuRw=
-----END NEW CERTIFICATE REQUEST-----

변경된 데이터

...
HgrSLy0VBO71Kjbr3Co7h76WU0QB1rVT61QGGT5/M/9pPf/muYF3eSdTkxDOZkXZT8vFUwbZiUyX
BGVvMC1epaCn329BFB4G3B+gJPa5k2OjlfogcT0jYWA0sUe1O/7bmW6sEfMwoTqh/VbGQLc/Eawz
mB8+Oxgvkhri175eT62jHhCKyseBKZU4JvyOLNzyMh4g3UU6TkuzKdLdc2Lmk4uEDT5qOs2Kkqnf KuJyrG3L5 NEW CERTIFICATE REQUEST-----



xss_clean() 살펴보기 #

xss_clean 메서드가 어떻게 구현되어 있는지 살펴보기 위해 상속하고 있는 클래스를 따라가본다.

A Class ---> MYRest_Controller ---> REST_Controller ---> CI_Controller

class A extends MYRest_Controller {

    ...

    public function method1()
    {
	    try {
		    $data = $this->post('data', true, true);

            ...
class MYRest_Controller {

    ...

    public function post($key = NULL, $xss_clean = TRUE, $required = FALSE, $type = FALSE){
		$rs = parent::post($key, $xss_clean);

        ...
	}
}
class REST_Controller {

    ...
    
    public function post($key = NULL, $xss_clean = TRUE)
	{
		if ($key === NULL)
		{
			return $this->_post_args;
		}

		return array_key_exists($key, $this->_post_args) ? $this->_xss_clean($this->_post_args[$key], $xss_clean) : FALSE;
	}

    ...
}
class REST_Controller {

    ...

    protected function _xss_clean($val, $process)
	{
		if (CI_VERSION < 2)
		{
			return $process ? $this->input->xss_clean($val) : $val;
		}

		return $process ? $this->security->xss_clean($val) : $val; // <-- 여기
	}

    ...
}

끝가지 올라가보면, security 클래스(라이브러리)의 xss_clean 메서드가 실행되는 것을 알 수 있다.


class Security {

    ...

    public function xss_clean($str, $is_image = FALSE)
	{
		...

		// Remove evil attributes such as style, onclick and xmlns
		$str = $this->_remove_evil_attributes($str, $is_image);

		...

		return $str;
	}

    ...
}

실제로는 내용이 무척 긴 데, Z살펴봐야할 부분은 _remove_evil_attributes() 부분이다.

$str = $this->_remove_evil_attributes($str, $is_image);

class Security {

    ...

    protected function _remove_evil_attributes($str, $is_image)
	{
		// All javascript event handlers (e.g. onload, onclick, onmouseover), style, and xmlns
		$evil_attributes = array('on\w*', 'style', 'xmlns', 'formaction');

        ...

		do {
			$count = 0;
			$attribs = array();

			// find occurrences of illegal attribute strings with quotes (042 and 047 are octal quotes)
			preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*(\042|\047)([^\\2]*?)(\\2)/is', $str, $matches, PREG_SET_ORDER);

			foreach ($matches as $attr)
			{
				$attribs[] = preg_quote($attr[0], '/');
			}

			// find occurrences of illegal attribute strings without quotes
			preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER);

			foreach ($matches as $attr)
			{
				print_r($attr[0]);
				$attribs[] = preg_quote($attr[0], '/');
			}

			// replace illegal attribute strings that are inside an html tag
			if (count($attribs) > 0)
			{
				$str = preg_replace('/(<?)(\/?[^><]+?)([^A-Za-z<>\-])(.*?)('.implode('|', $attribs).')(.*?)([\s><]?)([><]*)/i', '$1$2 $4$6$7$8', $str, -1, $count);
			}

		} while ($count);

		return $str;
	}

    ...
}

여기서도 핵심 부분은 아래 부분이다.

preg_match_all('/('.implode('|', $evil_attributes).')\s*=\s*([^\s>]*)/is', $str, $matches, PREG_SET_ORDER);

위의 코드를 조금 간단하게 살펴보면 아래와 같다.

preg_match((on\w*)\s*=\s*([^\s>]*), $str);

더 간략하게 하면 아래와 같고, 이 부분에 걸린 것을 알 수 있다.

`(on\w*)\s*=\s*([^\s>]*)`
= `(on\w*)=\s*([^\s]*)`
= `(on\w*)=\s.*`
xss_clean_regex_pattern