Loading...
MySQL 9.5 Reference Manual 9.5의 14.11 XML Functions의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
Table 14.16 XML Functions
| Name | Description |
|---|---|
ExtractValue() | XPath 표기법을 사용하여 XML 문자열에서 값을 추출 |
UpdateXML() | 대체된 XML 프래그먼트 반환 |
이 섹션에서는 MySQL의 XML 및 관련 기능에 대해 설명합니다.
참고
mysql 및 mysqldump 클라이언트를 --xml 옵션과 함께 실행하여 MySQL에서 XML 형식의 출력 결과를 얻을 수 있습니다. 자세한 내용은
Section 6.5.1, “mysql — The MySQL Command-Line Client” 및 Section 6.5.4, “mysqldump — A Database Backup Program”를 참고하십시오.
XPath 1.0 (XML Path Language, version 1.0) 기능을 제공하는 두 개의 함수가 제공됩니다. XPath 구문 및 사용법에 대한 기본 정보는 이 섹션 뒷부분에 제공됩니다.
그러나 이러한 주제에 대한 심층적인 논의는 이 매뉴얼의 범위를 벗어나므로, 더 정확한 정보는 XML Path Language (XPath) 1.0 standard를 참조해야 합니다. XPath에 익숙하지 않거나 기본 내용을 다시 상기하고 싶은 사람들에게 유용한 자료로, 여러 언어로 제공되는 Zvon.org XPath Tutorial이 있습니다.
참고
이들 함수는 아직 개발 중입니다. 우리는 MySQL 9.5 이후 버전에서 이들 및 XML과 XPath 기능의 다른 측면들을 계속 개선하고 있습니다. 이에 대해 토론하고, 질문을 하거나, 다른 사용자들로부터 도움을 받으려면 MySQL XML User Forum을 이용할 수 있습니다.
이들 함수에서 사용하는 XPath 식은 사용자 변수 및 로컬 저장 프로그램 변수를 지원합니다. 사용자 변수는 느슨하게 검사되고, 저장 프로그램에 로컬한 변수는 엄격하게 검사됩니다(Bug #26518도 참조):
$@variable_name 구문(즉, 사용자 변수)을 사용하는 변수는 검사되지 않습니다. 변수의 타입이 잘못되었거나 이전에 값이 할당되지 않은 경우에도 서버는 경고나 오류를 발생시키지 않습니다. 이는 또한 (예를 들어)
$@myvariable이 의도되었는데
$@myvairable이 사용된 경우와 같이 철자 오류에 대해서도 경고가 주어지지 않으므로, 사용자에게 전적인 책임이 있음을 의미합니다.
예:
1mysql> SET @xml = '<a><b>X</b><b>Y</b></a>'; 2Query OK, 0 rows affected (0.00 sec) 3 4mysql> SET @i =1, @j = 2; 5Query OK, 0 rows affected (0.00 sec) 6 7mysql> SELECT @i, ExtractValue(@xml, '//b[$@i]'); 8+------+--------------------------------+ 9| @i | ExtractValue(@xml, '//b[$@i]') | 10+------+--------------------------------+ 11| 1 | X | 12+------+--------------------------------+ 131 row in set (0.00 sec) 14 15mysql> SELECT @j, ExtractValue(@xml, '//b[$@j]'); 16+------+--------------------------------+ 17| @j | ExtractValue(@xml, '//b[$@j]') | 18+------+--------------------------------+ 19| 2 | Y | 20+------+--------------------------------+ 211 row in set (0.00 sec) 22 23mysql> SELECT @k, ExtractValue(@xml, '//b[$@k]'); 24+------+--------------------------------+ 25| @k | ExtractValue(@xml, '//b[$@k]') | 26+------+--------------------------------+ 27| NULL | | 28+------+--------------------------------+ 291 row in set (0.00 sec)
$variable_name 구문을 사용하는 변수는 저장 프로그램 내부에서 이들 함수가 호출될 때 선언하고 사용할 수 있습니다. 이러한 변수는 정의된 저장 프로그램에 로컬하며, 타입 및 값에 대해 엄격하게 검사됩니다.예:
1mysql> DELIMITER | 2 3mysql> CREATE PROCEDURE myproc () 4 -> BEGIN 5 -> DECLARE i INT DEFAULT 1; 6 -> DECLARE xml VARCHAR(25) DEFAULT '<a>X</a><a>Y</a><a>Z</a>'; 7 -> 8 -> WHILE i < 4 DO 9 -> SELECT xml, i, ExtractValue(xml, '//a[$i]'); 10 -> SET i = i+1; 11 -> END WHILE; 12 -> END | 13Query OK, 0 rows affected (0.01 sec) 14 15mysql> DELIMITER ; 16 17mysql> CALL myproc(); 18+--------------------------+---+------------------------------+ 19| xml | i | ExtractValue(xml, '//a[$i]') | 20+--------------------------+---+------------------------------+ 21| <a>X</a><a>Y</a><a>Z</a> | 1 | X | 22+--------------------------+---+------------------------------+ 231 row in set (0.00 sec) 24 25+--------------------------+---+------------------------------+ 26| xml | i | ExtractValue(xml, '//a[$i]') | 27+--------------------------+---+------------------------------+ 28| <a>X</a><a>Y</a><a>Z</a> | 2 | Y | 29+--------------------------+---+------------------------------+ 301 row in set (0.01 sec) 31 32+--------------------------+---+------------------------------+ 33| xml | i | ExtractValue(xml, '//a[$i]') | 34+--------------------------+---+------------------------------+ 35| <a>X</a><a>Y</a><a>Z</a> | 3 | Z | 36+--------------------------+---+------------------------------+ 371 row in set (0.01 sec)
Parameters.
저장 루틴 내부에서 XPath 식에 사용되는 변수 중 매개변수로 전달되는 것 역시 강한 검사의 대상입니다.
사용자 변수 또는 저장 프로그램에 로컬한 변수를 포함하는 식은(표기법을 제외하고) XPath 1.0 명세에서 변수를 포함하는 XPath 식에 대해 주어진 규칙을 따라야 합니다.
참고
XPath 식을 저장하는 데 사용되는 사용자 변수는 빈 문자열로 취급됩니다. 이로 인해 XPath 식을 사용자 변수로 저장하는 것은 불가능합니다(Bug #32911).
ExtractValue()는 두 개의 문자열 인수, 즉 XML 마크업 프래그먼트인
_xml_frag_와 XPath 식인
xpath_expr(로케이터라고도 함)을 받습니다. 이 함수는 XPath 식에 의해 매칭되는 요소 또는 요소들의 하위 요소인 첫 번째 텍스트 노드의 텍스트(CDATA)를 반환합니다.
이 함수를 사용하는 것은 xpath_expr 뒤에 /text()를 덧붙인 후 매치를 수행하는 것과 동일합니다. 즉,
ExtractValue('<a><b>Sakila</b></a>', '/a/b')와
ExtractValue('<a><b>Sakila</b></a>', '/a/b/text()')는 동일한 결과를 생성합니다.
xml_frag 또는
_xpath_expr_가
NULL이면, 함수는
NULL을 반환합니다.
여러 개의 매치가 발견되는 경우, 각 매칭 요소의 첫 번째 하위 텍스트 노드의 내용이(매칭된 순서대로) 단일 공백으로 구분된 문자열로 반환됩니다.
식(암시적인 /text()을 포함)에서 매칭되는 텍스트 노드를 어떤 이유에서든 찾지 못하는 경우, 단
_xpath_expr_가 유효하고,
_xml_frag_가 올바르게 중첩되고 닫힌 요소로 구성되어 있다면, 빈 문자열이 반환됩니다. 빈 요소에서의 매치와 매치가 전혀 없는 경우는 구분되지 않습니다. 이는 의도된 동작입니다.
xml_frag 안에서 매칭되는 요소가 전혀 없는지, 아니면 해당 요소는 존재하지만 하위 텍스트 노드를 포함하지 않는지를 판별할 필요가 있다면, XPath
count() 함수를 사용하는 식의 결과를 검사해야 합니다. 예를 들어, 다음 두 문장은 모두 아래와 같이 빈 문자열을 반환합니다:
1mysql> SELECT ExtractValue('<a><b/></a>', '/a/b'); 2+-------------------------------------+ 3| ExtractValue('<a><b/></a>', '/a/b') | 4+-------------------------------------+ 5| | 6+-------------------------------------+ 71 row in set (0.00 sec) 8 9mysql> SELECT ExtractValue('<a><c/></a>', '/a/b'); 10+-------------------------------------+ 11| ExtractValue('<a><c/></a>', '/a/b') | 12+-------------------------------------+ 13| | 14+-------------------------------------+ 151 row in set (0.00 sec)
그러나 다음과 같이 실제로 매칭되는 요소가 있었는지는 판별할 수 있습니다:
1mysql> SELECT ExtractValue('<a><b/></a>', 'count(/a/b)'); 2+-------------------------------------+ 3| ExtractValue('<a><b/></a>', 'count(/a/b)') | 4+-------------------------------------+ 5| 1 | 6+-------------------------------------+ 71 row in set (0.00 sec) 8 9mysql> SELECT ExtractValue('<a><c/></a>', 'count(/a/b)'); 10+-------------------------------------+ 11| ExtractValue('<a><c/></a>', 'count(/a/b)') | 12+-------------------------------------+ 13| 0 | 14+-------------------------------------+ 151 row in set (0.01 sec)
주의
ExtractValue()는
CDATA만 반환하며, 매칭된 태그 내부에 포함될 수 있는 어떤 태그나 그들의 내용은 반환하지 않습니다(아래 예제에서 val1으로 반환되는 결과를 참조).
1mysql> SELECT 2 -> ExtractValue('<a>ccc<b>ddd</b></a>', '/a') AS val1, 3 -> ExtractValue('<a>ccc<b>ddd</b></a>', '/a/b') AS val2, 4 -> ExtractValue('<a>ccc<b>ddd</b></a>', '//b') AS val3, 5 -> ExtractValue('<a>ccc<b>ddd</b></a>', '/b') AS val4, 6 -> ExtractValue('<a>ccc<b>ddd</b><b>eee</b></a>', '//b') AS val5; 7 8+------+------+------+------+---------+ 9| val1 | val2 | val3 | val4 | val5 | 10+------+------+------+------+---------+ 11| ccc | ddd | ddd | | ddd eee | 12+------+------+------+------+---------+
이 함수는 contains()와 비교를 수행할 때 현재 SQL 콜레이션을 사용하며, 다른 문자열 함수(예:
CONCAT())와 동일하게, 인수의 콜레이션 강제(coercibility)를 고려한 콜레이션 집계를 수행합니다. 이러한 동작을 제어하는 규칙에 대한 설명은
Section 12.8.4, “Collation Coercibility in Expressions”를 참고하십시오.
(이전에는 항상 바이너리, 즉 대소문자를 구분하는 비교가 사용되었습니다.)
_xml_frag_에 올바르게 중첩되거나 닫히지 않은 요소가 포함된 경우
NULL이 반환되고, 아래 예와 같이 경고가 생성됩니다:
1mysql> SELECT ExtractValue('<a>c</a><b', '//a'); 2+-----------------------------------+ 3| ExtractValue('<a>c</a><b', '//a') | 4+-----------------------------------+ 5| NULL | 6+-----------------------------------+ 71 row in set, 1 warning (0.00 sec) 8 9mysql> SHOW WARNINGS\G 10*************************** 1. row *************************** 11 Level: Warning 12 Code: 1525 13Message: Incorrect XML value: 'parse error at line 1 pos 11: 14 END-OF-INPUT unexpected ('>' wanted)' 151 row in set (0.00 sec) 16 17mysql> SELECT ExtractValue('<a>c</a><b/>', '//a'); 18+-------------------------------------+ 19| ExtractValue('<a>c</a><b/>', '//a') | 20+-------------------------------------+ 21| c | 22+-------------------------------------+ 231 row in set (0.00 sec)
이 함수는 주어진 XML 마크업 프래그먼트
_xml_target_의 일부를 새로운 XML 프래그먼트
_new_xml_로 교체하고, 변경된 XML을 반환합니다.
_xml_target_에서 교체되는 부분은 사용자가 제공한 XPath 식
_xpath_expr_와 매칭되는 부분입니다.
_xpath_expr_와 매칭되는 식을 찾지 못하거나, 여러 개의 매치가 발견되면, 함수는 원래의
xml_target XML 프래그먼트를 반환합니다. 세 인수는 모두 문자열이어야 합니다.
UpdateXML()의 인수 중 하나라도 NULL이면, 함수는 NULL을 반환합니다.
1mysql> SELECT 2 -> UpdateXML('<a><b>ccc</b><d></d></a>', '/a', '<e>fff</e>') AS val1, 3 -> UpdateXML('<a><b>ccc</b><d></d></a>', '/b', '<e>fff</e>') AS val2, 4 -> UpdateXML('<a><b>ccc</b><d></d></a>', '//b', '<e>fff</e>') AS val3, 5 -> UpdateXML('<a><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val4, 6 -> UpdateXML('<a><d></d><b>ccc</b><d></d></a>', '/a/d', '<e>fff</e>') AS val5 7 -> \G 8 9*************************** 1. row *************************** 10val1: <e>fff</e> 11val2: <a><b>ccc</b><d></d></a> 12val3: <a><e>fff</e><d></d></a> 13val4: <a><b>ccc</b><e>fff</e></a> 14val5: <a><d></d><b>ccc</b><d></d></a>
참고
XPath 구문 및 사용법에 대한 심층적인 논의는 이 매뉴얼의 범위를 벗어납니다. 보다 정확한 정보는 XML Path Language (XPath) 1.0 specification을 참고하십시오. XPath에 익숙하지 않거나 기본 내용을 다시 상기하고자 하는 사람들에게 유용한 자료로, 여러 언어로 제공되는 Zvon.org XPath Tutorial이 있습니다.
아래에 몇 가지 기본 XPath 식에 대한 설명과 예제가 이어집니다:
/tag<tag/>가 루트 요소인 경우에만
<tag/>와 매칭됩니다.
예: /a는
<a><b/></a>에서 가장 바깥쪽(루트) 태그와 매칭되므로 매치가 존재합니다.
<b><a/></b>의 내부
a 요소에는 매칭되지 않습니다. 이 경우, 해당 요소는 다른 요소의 하위 요소이기 때문입니다.
/tag1/tag2<tag2/>가 루트 요소인 <tag1/>의 하위 요소인 경우에만
<tag2/>와 매칭됩니다.
예: /a/b는 XML 프래그먼트
<a><b/></a>에서 루트 요소 _a_의 하위 요소인
b 요소와 매칭됩니다.
<b><a/></b>에서는 이 경우 _b_가 루트 요소(따라서 다른 어떤 요소의 하위 요소도 아님)이기 때문에 매치가 없습니다. 또한 XPath 식은
<a><c><b/></c></a>에서도 매치가 없습니다. 이 경우,
_b_는 _a_의 자손이지만
_a_의 실제 하위 요소는 아니기 때문입니다.
이 구조는 세 개 이상의 요소로 확장할 수 있습니다. 예를 들어 XPath 식 /a/b/c는 프래그먼트
<a><b><c/></b></a>의
c 요소와 매칭됩니다.
//tag<tag>의 모든 인스턴스와 매칭됩니다.
예: //a는 다음과 같은 모든 경우에서
a 요소와 매칭됩니다:
<a><b><c/></b></a>;
<c><a><b/></a></b>;
<c><b><a/></b></c>.
//는 /와 결합하여 사용할 수 있습니다. 예를 들어 //a/b는 프래그먼트
<a><b/></a> 또는
<c><a><b/></a></c>에서 b 요소와 매칭됩니다.
참고
//tag는
/descendant-or-self::*/tag와 동등합니다. 종종 이를
/descendant-or-self::tag와 혼동하는데, 후자의 식은 실제로 매우 다른 결과를 초래할 수 있습니다. 다음 예에서 이를 확인할 수 있습니다:
1mysql> SET @xml = '<a><b><c>w</c><b>x</b><d>y</d>z</b></a>'; 2Query OK, 0 rows affected (0.00 sec) 3 4mysql> SELECT @xml; 5+-----------------------------------------+ 6| @xml | 7+-----------------------------------------+ 8| <a><b><c>w</c><b>x</b><d>y</d>z</b></a> | 9+-----------------------------------------+ 101 row in set (0.00 sec) 11 12mysql> SELECT ExtractValue(@xml, '//b[1]'); 13+------------------------------+ 14| ExtractValue(@xml, '//b[1]') | 15+------------------------------+ 16| x z | 17+------------------------------+ 181 row in set (0.00 sec) 19 20mysql> SELECT ExtractValue(@xml, '//b[2]'); 21+------------------------------+ 22| ExtractValue(@xml, '//b[2]') | 23+------------------------------+ 24| | 25+------------------------------+ 261 row in set (0.01 sec) 27 28mysql> SELECT ExtractValue(@xml, '/descendant-or-self::*/b[1]'); 29+---------------------------------------------------+ 30| ExtractValue(@xml, '/descendant-or-self::*/b[1]') | 31+---------------------------------------------------+ 32| x z | 33+---------------------------------------------------+ 341 row in set (0.06 sec) 35 36mysql> SELECT ExtractValue(@xml, '/descendant-or-self::*/b[2]'); 37+---------------------------------------------------+ 38| ExtractValue(@xml, '/descendant-or-self::*/b[2]') | 39+---------------------------------------------------+ 40| | 41+---------------------------------------------------+ 421 row in set (0.00 sec) 43 44 45mysql> SELECT ExtractValue(@xml, '/descendant-or-self::b[1]'); 46+-------------------------------------------------+ 47| ExtractValue(@xml, '/descendant-or-self::b[1]') | 48+-------------------------------------------------+ 49| z | 50+-------------------------------------------------+ 511 row in set (0.00 sec) 52 53mysql> SELECT ExtractValue(@xml, '/descendant-or-self::b[2]'); 54+-------------------------------------------------+ 55| ExtractValue(@xml, '/descendant-or-self::b[2]') | 56+-------------------------------------------------+ 57| x | 58+-------------------------------------------------+ 591 row in set (0.00 sec)
* 연산자는 어떤 요소와도 매칭되는 “와일드카드”처럼 동작합니다. 예를 들어 식
/*/b는 XML 프래그먼트
<a><b/></a> 또는
<c><b/></c>에서 b 요소와 매칭됩니다. 그러나 프래그먼트
<b><a/></b>에서는 매치가 발생하지 않습니다. 이 경우 _b_는 어떤 다른 요소의 하위 요소여야 하기 때문입니다. 와일드카드는 어떤 위치에서도 사용할 수 있습니다. 식
/*/b/*는 루트 요소가 아닌 b 요소의 어떤 하위 요소와도 매칭됩니다.
|(UNION) 연산자를 사용하면 여러 로케이터 중 어떤 것과도 매칭되도록 할 수 있습니다. 예를 들어 식
//b|//c는 XML 타깃의 모든
b 및 c 요소와 매칭됩니다.
또한 하나 이상의 어트리뷰트 값에 기반하여 요소와 매칭되도록 할 수도 있습니다. 이는
tag[@attribute="value"] 구문을 사용하여 수행합니다. 예를 들어 식
//b[@id="idB"]는 프래그먼트 <a><b id="idA"/><c/><b id="idB"/></a>에서 두 번째 b 요소와 매칭됩니다.
attribute="value"를 가지는 어떤 요소와 매칭되게 하려면 XPath 식
//*[attribute="value"]를 사용하십시오.
여러 어트리뷰트 값을 기준으로 필터링하려면 여러 어트리뷰트 비교 절을 연속해서 사용하면 됩니다. 예를 들어 식
//b[@c="x"][@d="y"]는 주어진 XML 프래그먼트에서 어디에서든 발생하는
<b c="x" d="y"/> 요소와 매칭됩니다.
동일한 어트리뷰트가 여러 값 중 어떤 것과도 매칭되는 요소를 찾기 위해서는 | 연산자로 연결된 여러 로케이터를 사용할 수 있습니다. 예를 들어, 어트리뷰트 _c_가 23 또는 17 값 중 하나를 갖는 모든
b 요소와 매칭되도록 하려면 식
//b[@c="23"]|//b[@c="17"]를 사용하십시오. 이 목적을 위해 논리 or 연산자를 사용하는 것도 가능합니다:
//b[@c="23" or @c="17"].
참고
or와 |의 차이는 or는 조건을 연결하고, |는 결과 집합을 연결한다는 점입니다.
XPath Limitations.
이들 함수가 지원하는 XPath 구문에는 현재 다음과 같은 제한 사항이 있습니다:
노드 집합 간 비교(예:
'/a/b[@c=@d]')는 지원되지 않습니다.
모든 표준 XPath 비교 연산자는 지원됩니다(Bug #22823).
상대 로케이터 식은 루트 노드의 컨텍스트에서 해석됩니다. 예를 들어 다음 쿼리와 결과를 고려하십시오:
1mysql> SELECT ExtractValue( 2 -> '<a><b c="1">X</b><b c="2">Y</b></a>', 3 -> 'a/b' 4 -> ) AS result; 5+--------+ 6| result | 7+--------+ 8| X Y | 9+--------+ 101 row in set (0.03 sec)
이 경우 로케이터 a/b는 /a/b로 해석됩니다.
상대 로케이터는 술어(predicate) 내에서도 지원됩니다. 다음 예에서
d[../@c="1"]는 /a/b[@c="1"]/d로 해석됩니다:
1mysql> SELECT ExtractValue( 2 -> '<a> 3 -> <b c="1"><d>X</d></b> 4 -> <b c="2"><d>X</d></b> 5 -> </a>', 6 -> 'a/b/d[../@c="1"]') 7 -> AS result; 8+--------+ 9| result | 10+--------+ 11| X | 12+--------+ 131 row in set (0.00 sec)
변수 참조, 리터럴, 숫자 및 스칼라 함수 호출 등 스칼라 값으로 평가되는 식이 접두(prefix)로 붙은 로케이터는 허용되지 않으며, 사용 시 오류가 발생합니다.
:: 연산자는 다음과 같은 노드 타입과 결합되어 사용할 때는 지원되지 않습니다:
axis::comment()
axis::text()
axis::processing-instructions()
axis::node()
그러나 이름 테스트(예:
axis::name, axis::*)는 아래 예와 같이 지원됩니다:
1mysql> SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b'); 2+-------------------------------------------------------+ 3| ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::b') | 4+-------------------------------------------------------+ 5| x | 6+-------------------------------------------------------+ 71 row in set (0.02 sec) 8 9mysql> SELECT ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*'); 10+-------------------------------------------------------+ 11| ExtractValue('<a><b>x</b><c>y</c></a>','/a/child::*') | 12+-------------------------------------------------------+ 13| x y | 14+-------------------------------------------------------+ 151 row in set (0.01 sec)
“위아래(up-and-down)” 내비게이션은 경로가 루트 요소 “위쪽”으로 향하게 되는 경우 지원되지 않습니다. 즉, 현재 요소의 상위 요소(ancestor) 중 하나 이상이 루트 요소의 상위 요소이기도 한 경우, 해당 요소의 상위 요소의 하위 요소와 매칭되는 식은 사용할 수 없습니다(Bug #16321 참조).
다음 XPath 함수는 지원되지 않거나, 아래와 같이 알려진 이슈가 있습니다:
id()
lang()
local-name()
name()
namespace-uri()
normalize-space()
starts-with()
string()
substring-after()
substring-before()
translate()
다음 축(axis)는 지원되지 않습니다:
following-sibling
following
preceding-sibling
preceding
ExtractValue() 및
UpdateXML()에 인수로 전달되는 XPath 식은 요소 선택자에 콜론 문자(:)를 포함할 수 있으며, 이를 통해 XML 네임스페이스 표기법을 사용하는 마크업에 적용할 수 있습니다. 예:
1mysql> SET @xml = '<a>111<b:c>222<d>333</d><e:f>444</e:f></b:c></a>'; 2Query OK, 0 rows affected (0.00 sec) 3 4mysql> SELECT ExtractValue(@xml, '//e:f'); 5+-----------------------------+ 6| ExtractValue(@xml, '//e:f') | 7+-----------------------------+ 8| 444 | 9+-----------------------------+ 101 row in set (0.00 sec) 11 12mysql> SELECT UpdateXML(@xml, '//b:c', '<g:h>555</g:h>'); 13+--------------------------------------------+ 14| UpdateXML(@xml, '//b:c', '<g:h>555</g:h>') | 15+--------------------------------------------+ 16| <a>111<g:h>555</g:h></a> | 17+--------------------------------------------+ 181 row in set (0.00 sec)
이는 Apache Xalan 및 몇몇 다른 파서에서 허용되는 것과 유사한 점이 있으며, 네임스페이스 선언이나
namespace-uri() 및
local-name() 함수 사용을 요구하는 것보다 훨씬 간단합니다.
Error handling.
ExtractValue() 및
UpdateXML() 모두에 대해, 사용되는 XPath 로케이터는 유효해야 하며 검색 대상 XML은 올바르게 중첩되고 닫힌 요소로 구성되어 있어야 합니다. 로케이터가 유효하지 않은 경우 오류가 발생합니다:
1mysql> SELECT ExtractValue('<a>c</a><b/>', '/&a'); 2ERROR 1105 (HY000): XPATH syntax error: '&a'
_xml_frag_가 올바르게 중첩되고 닫힌 요소로 구성되어 있지 않은 경우,
NULL이 반환되고 아래 예와 같이 경고가 생성됩니다:
1mysql> SELECT ExtractValue('<a>c</a><b', '//a'); 2+-----------------------------------+ 3| ExtractValue('<a>c</a><b', '//a') | 4+-----------------------------------+ 5| NULL | 6+-----------------------------------+ 71 row in set, 1 warning (0.00 sec) 8 9mysql> SHOW WARNINGS\G 10*************************** 1. row *************************** 11 Level: Warning 12 Code: 1525 13Message: Incorrect XML value: 'parse error at line 1 pos 11: 14 END-OF-INPUT unexpected ('>' wanted)' 151 row in set (0.00 sec) 16 17mysql> SELECT ExtractValue('<a>c</a><b/>', '//a'); 18+-------------------------------------+ 19| ExtractValue('<a>c</a><b/>', '//a') | 20+-------------------------------------+ 21| c | 22+-------------------------------------+ 231 row in set (0.00 sec)
주의
UpdateXML()의 세 번째 인수로 사용되는 replacement XML은 올바르게 중첩되고 닫힌 요소로만 구성되어 있는지 여부를 검사하지 않습니다.
XPath Injection. 코드 인젝션은 악의적인 코드가 시스템에 주입되어 권한 및 데이터에 대한 무단 액세스를 얻는 경우를 말합니다. 이는 사용자로부터의 데이터 입력의 타입과 내용에 대해 개발자가 가지는 가정을 악용하는 데 기반합니다. XPath도 이 점에서 예외가 아닙니다.
이러한 일이 발생할 수 있는 일반적인 시나리오는, 로그인 이름과 비밀번호의 조합을 XML 파일에 있는 값과 매칭시켜 인가 처리를 하는 애플리케이션에서, 다음과 같은 XPath 식을 사용하는 경우입니다:
1//user[login/text()='neapolitan' and password/text()='1c3cr34m']/attribute::id
이는 다음과 같은 SQL 문장의 XPath 상응물입니다:
1SELECT id FROM users WHERE login='neapolitan' AND password='1c3cr34m';
XPath를 사용하는 PHP 애플리케이션은 로그인 프로세스를 다음과 같이 처리할 수 있습니다:
1<?php 2 3 $file = "users.xml"; 4 5 $login = $POST["login"]; 6 $password = $POST["password"]; 7 8 $xpath = "//user[login/text()=$login and password/text()=$password]/attribute::id"; 9 10 if( file_exists($file) ) 11 { 12 $xml = simplexml_load_file($file); 13 14 if($result = $xml->xpath($xpath)) 15 echo "You are now logged in as user $result[0]."; 16 else 17 echo "Invalid login name or password."; 18 } 19 else 20 exit("Failed to open $file."); 21 22?>
입력에 대해 어떠한 검사도 수행되지 않습니다. 이는 악의적인 사용자가 로그인 이름과 비밀번호에 모두
' or 1=1을 입력하여 테스트를 “short-circuit”할 수 있음을 의미하며, 그 결과 $xpath는 다음과 같이 평가됩니다:
1//user[login/text()='' or 1=1 and password/text()='' or 1=1]/attribute::id
대괄호 안의 식이 항상 true로 평가되므로, 이는 XML 문서 내 모든
user 요소의 id 어트리뷰트와 매칭되는 다음 식과 사실상 동일합니다:
1//user/attribute::id
이 특정 공격을 피하는 한 가지 방법은, 웹 폼에서 전달된 값이 문자열으로 변환되도록 $xpath 정의에서 보간될 변수 이름을 단순히 따옴표로 감싸는 것입니다:
1$xpath = "//user[login/text()='$login' and password/text()='$password']/attribute::id";
이는 일반적으로 SQL 인젝션 공격을 방지하기 위해 권장되는 전략과 동일합니다. 일반적으로 XPath 인젝션 공격을 방지하기 위해 따라야 할 관행은 SQL 인젝션을 방지하기 위한 관행과 동일합니다:
애플리케이션에서 사용자로부터 온 검증되지 않은 데이터를 절대 허용하지 마십시오.
모든 사용자 입력 데이터에 대해 타입을 검사하십시오. 타입이 잘못된 데이터는 거부하거나 변환하십시오.
숫자 데이터에 대해 허용 범위를 벗어나는 값을 검사하고, 범위를 벗어난 값은 잘라내거나(truncate), 반올림하거나(round), 거부하십시오. 문자열에 대해서는 잘못된 문자를 검사하고, 이를 제거하거나 해당 문자를 포함하는 입력을 거부하십시오.
시스템을 손상시키는 데 사용할 수 있는 단서를 무단 사용자에게 제공할 수 있는 명시적인 오류 메시지를 출력하지 말고, 대신 파일이나 데이터베이스 테이블에 로그하십시오.
SQL 인젝션 공격이 데이터베이스 스키마에 대한 정보를 얻는 데 사용될 수 있는 것처럼, XPath 인젝션도 XML 파일을 탐색하여 그 구조를 파악하는 데 사용될 수 있습니다. 이에 대해서는 Amit Klein의 논문 Blind XPath Injection (PDF 파일, 46KB)에서 논의하고 있습니다.
클라이언트로 다시 전송되는 출력을 검사하는 것도 중요합니다. MySQL
ExtractValue() 함수를 사용할 때 발생할 수 있는 상황을 살펴보십시오:
1mysql> SELECT ExtractValue( 2 -> LOAD_FILE('users.xml'), 3 -> '//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id' 4 -> ) AS id; 5+-------------------------------+ 6| id | 7+-------------------------------+ 8| 00327 13579 02403 42354 28570 | 9+-------------------------------+ 101 row in set (0.01 sec)
ExtractValue()는 여러 매치를 단일 공백 구분 문자열으로 반환하므로, 이 인젝션 공격은 users.xml에 포함된 모든 유효한 ID를 단일 행의 출력으로 사용자에게 제공합니다. 추가적인 안전 장치로, 사용자에게 반환하기 전에 출력 또한 검사해야 합니다. 다음은 간단한 예입니다:
1mysql> SELECT @id = ExtractValue( 2 -> LOAD_FILE('users.xml'), 3 -> '//user[login/text()="" or 1=1 and password/text()="" or 1=1]/attribute::id' 4 -> ); 5Query OK, 0 rows affected (0.00 sec) 6 7mysql> SELECT IF( 8 -> INSTR(@id, ' ') = 0, 9 -> @id, 10 -> 'Unable to retrieve user ID') 11 -> AS singleID; 12+----------------------------+ 13| singleID | 14+----------------------------+ 15| Unable to retrieve user ID | 16+----------------------------+ 171 row in set (0.00 sec)
일반적으로, 사용자에게 데이터를 안전하게 반환하기 위한 지침은 사용자 입력을 받을 때와 동일합니다. 이를 요약하면 다음과 같습니다:
나가는 데이터에 대해 항상 타입 및 허용 가능한 값에 대한 검사를 수행하십시오.
애플리케이션에 대한 정보를 제공하여 이를 악용할 수 있는 오류 메시지를 무단 사용자가 볼 수 없도록 하십시오.
14.10 Cast Functions and Operators
14.12 Bit Functions and Operators