Java에서 HTML을 이스케이프 처리하는 데 권장되는 방법


262

탈출 할 수있는 권장 방법이 <, >, "&일반 자바 코드의 문자 출력 HTML? (수동으로 다음을 수행하는 것 외에).

String source = "The less than sign (<) and ampersand (&) must be escaped before using them in HTML";
String escaped = source.replace("<", "&lt;").replace("&", "&amp;"); // ...

2
인용되지 않은 HTML 속성으로 출력하는 경우 공백, 탭, 백 스페이스 등과 같은 다른 문자를 사용하면 공격자가 나열된 문자없이 자바 스크립트 특성을 도입 할 수 있습니다. 자세한 내용은 OWASP XSS Prevention Cheat Sheet를 참조하십시오.
Jeff Williams

BTW,이 코드에서는 "<"전에 "&"를 이스케이프하여 제대로 작동하도록해야합니다. 그렇지 않으면 "& lt;"로 바뀌고 그렇지 않으면 "& lt;"로 렌더링되고 "< ") :source.replace("&", "&amp;").replace("<", "&lt;");
Tey '

답변:


261

StringEscapeUtils 에서 아파치 코 몬즈 랭 :

import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;
// ...
String source = "The less than sign (<) and ampersand (&) must be escaped before using them in HTML";
String escaped = escapeHtml(source);

내용은 버전 3 :

import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
// ...
String escaped = escapeHtml4(source);

2
하지만 StringEscapeUtils좋은 당신이 HTML / XML 공백 정상화 피하고자하는 경우는 공백 제대로 속성을 탈출하지 않습니다. 자세한 내용은 내 대답을 참조하십시오.
Adam Gent

21
위의 예가 깨졌습니다. 지금 escapeHtml4 () 메소드를 사용하십시오.
stackoverflowuser2010 년

3
구아바 팬들은 아래 okranz의 답변을 참조하십시오.
George Hawkins

2
웹 페이지에 UTF-8 인코딩이있는 경우 다음 다섯 개의 ASCII 문자 만 이스케이프하는 Guava의 htmlEscaper 만 있으면됩니다. ' "& <>. Apache의 escapeHtml ()은 UTF-8 웹에서는 필요하지 않은 액센트를 포함하여 비 ASCII 문자를 대체합니다. 페이지?
zdenekca

4
이제 commons-lang3에서 더 이상 사용되지 않습니다. commons.apache.org/proper/commons-text
Danny

137

Apache Commons의 대안 : SpringHtmlUtils.htmlEscape(String input)방법을 사용하십시오 .


9
감사. 내가 (대신 사용했습니다 StringEscapeUtils.escapeHtml()에서 apache-commons그대로 러시아어 문자를 잎 때문에 2.6).
Slava Semushin

6
알아두면 좋습니다. TBH 요즘에는 아파치에게 광범위한 정보를 제공합니다.
Adamski

1
나는 그것을 사용했고, 한자를 그대로 그대로 둡니다.
smartwjw

아래에 언급 된 구아바 대안과 어떻게 비교됩니까?
vishvAs vAsuki

2
그리고 아포스트로피도 인코딩하므로 아파치 StringEscapeUtils와 달리 실제로 유용합니다
David Balažic

57

좋은 짧은 방법 :

public static String escapeHTML(String s) {
    StringBuilder out = new StringBuilder(Math.max(16, s.length()));
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') {
            out.append("&#");
            out.append((int) c);
            out.append(';');
        } else {
            out.append(c);
        }
    }
    return out.toString();
}

에 기초 https://stackoverflow.com/a/8838023/1199155 (앰프가 누락되었습니다)를 합니다. http://www.w3.org/TR/html4/sgml/entities.html 에 따르면 if 절에서 확인 된 4 개의 문자는 128 미만의 유일한 문자입니다 .


좋은. 인코딩의 "html 버전"을 사용하지 않습니다 (예 : "á"는 "& # 225;"대신 "& aacute;"가 됨). 숫자는 IE7에서도 작동하므로 IE7에서도 작동하지 않습니다. 걱정해야합니다. 감사.
nonzaprej

OP가 4 개의 관련 문자를 이스케이프하도록 요청했을 때 모든 문자를 인코딩하는 이유는 무엇입니까? CPU와 메모리를 낭비하고 있습니다.
David Balažic

1
아포스트로피를 잊어 버렸습니다. 따라서 사람들은이 코드를 사용하여 속성 값을 이스케이프하는 모든 곳에 인용되지 않은 속성을 삽입 할 수 있습니다.
David Balažic

45

최신 버전의 Apache Commons Lang 라이브러리 가 있으며 다른 패키지 이름 (org.apache.commons.lang3)을 사용합니다. (가) StringEscapeUtils지금은 다양한 유형의 문서를 탈출 다른 정적 방법이있다 ( http://commons.apache.org/proper/commons-lang/javadocs/api-3.0/index.html ). 따라서 HTML 버전 4.0 문자열을 이스케이프하려면

import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;

String output = escapeHtml4("The less than sign (<) and ampersand (&) must be escaped before using them in HTML");

3
불행히도 HTML 5에는 아무것도 존재하지 않으며, Apache 문서는 HTML 5에 escapeHtml4를 사용하는 것이 적절한 지 명시하지 않습니다.
Paul Vincent Craven

43

Google Guava를 사용하는 사람들 :

import com.google.common.html.HtmlEscapers;
[...]
String source = "The less than sign (<) and ampersand (&) must be escaped before using them in HTML";
String escaped = HtmlEscapers.htmlEscaper().escape(source);

40

Android (API 16 이상)에서 다음을 수행 할 수 있습니다.

Html.escapeHtml(textToScape);

또는 더 낮은 API :

TextUtils.htmlEncode(textToScape);

escapeHtml대신 사용할 이유가 htmlEncode있습니까?
Muz

2
이 두 가지의 차이점에 대한 내 질문 도 참조하십시오 . (@Muz)
JonasCz-복원 모니카

37

이것을 조심하십시오. HTML 문서에는 여러 가지 '컨텍스트'가 있습니다. 요소 내부, 인용 된 속성 값, 인용되지 않은 속성 값, URL 속성, 자바 스크립트, CSS 등 ... 각각에 대해 다른 인코딩 방법을 사용해야합니다. XSS (Cross-Site Scripting)를 방지합니다. 이러한 각 상황에 대한 자세한 내용 은 OWASP XSS 예방 요령 시트 를 확인하십시오 . OWASP ESAPI 라이브러리 ( https://github.com/ESAPI/esapi-java-legacy) 에서 이러한 각 컨텍스트에 대한 이스케이프 메소드를 찾을 수 있습니다 .


6
출력을 인코딩하려는 컨텍스트 가 매우 중요 하다는 점을 지적 해 주셔서 감사 합니다. "인코딩"이라는 용어는 또한 "탈출"보다 훨씬 적절한 동사입니다. 이스케이프는 " XHTML 속성 / SQL 쿼리 매개 변수 / PostScript 인쇄 문자열 / CSV 출력 필드에 대해이 문자열을 어떻게 인코딩 합니까?"와 달리 일종의 특수한 해킹을 의미 합니다.
Roboprog

5
'인코딩'과 '이스케이프'는이를 설명하는 데 널리 사용됩니다. "이스케이프"라는 용어는 일반적으로 백 슬래시 \ "로 따옴표 문자를 이스케이프하는 등 구문 상 관련 문자 앞에"이스케이프 문자 "를 추가 할 때 사용됩니다."인코딩 "이라는 용어는 일반적으로 인용 부호 % 22를 인코딩하는 URL 또는 & # x22 또는 @quot와 같은 HTML 엔터티 인코딩과 같은 다른 형태의 문자
Jeff Williams


1
당신에게 인코더 클래스에 대한 몇 가지 인터넷 검색,보기 저장하려면 static.javadoc.io/org.owasp.esapi/esapi/2.0.1/org/owasp/esapi/...
야쿱 Bochenski

14

일부 목적으로 HtmlUtils :

import org.springframework.web.util.HtmlUtils;
[...]
HtmlUtils.htmlEscapeDecimal("&"); //gives &#38;
HtmlUtils.htmlEscape("&"); //gives &amp;

1
Spring HtmlUtils 의견에서 : * <p> 포괄적 인 문자열 이스케이프 유틸리티 세트의 경우 * Apache Commons Lang 및 StringEscapeUtils 클래스를 고려하십시오. * 우리는 HTML 이스 케이 핑을 위해 Commons Lang에서 런타임 의존성을 피하기 위해이 클래스를 사용하지 않습니다. 또한 Spring의 HTML 이스케이프는보다 유연하고 100 % HTML 4.0을 준수합니다. 프로젝트에서 이미 Apache commons를 사용하고 있다면 아마도 Apache의 StringEscapeUtils를 사용해야합니다.
andreyro

10

@dfa의 대답 org.apache.commons.lang.StringEscapeUtils.escapeHtml은 훌륭 하지만 과거에는 HTML (또는 XML) 속성 을 이스케이프하는 데 사용해서는 안됩니다. 그렇지 않으면 공백이 정규화됩니다 (모든 인접한 공백 문자가 단일 공백이 됨).

공백이 유지되지 않는 속성에 대해 내 라이브러리 (JATL)에 대해 버그를 제기했기 때문에 이것을 알고 있습니다. 따라서 속성과 요소 내용의 이스케이프를 구별하는 (copy n 'paste) 클래스 (JDOM에서 일부를 훔쳤습니다)가 있습니다 .

과거에는 그다지 중요하지 않았지만 (적절한 속성 이스케이프 처리) HTML5의 data-속성 사용을 사용함에 따라 점점 더 큰 관심을 끌고 있습니다.


9

org.apache.commons.lang3.StringEscapeUtils는 더 이상 사용되지 않습니다. 이제 org.apache.commons.text.StringEscapeUtils를 사용해야합니다.

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>${commons.text.version}</version>
    </dependency>

1

대부분의 라이브러리는 UTF-8 세계에서 원하지 않는 수백 개의 기호와 수천 개의 비 ASCII 문자를 포함하여 가능한 모든 것을 이스케이프 처리합니다.

또한 Jeff Williams가 언급했듯이 단일 "이스케이프 HTML"옵션이 없으며 몇 가지 컨텍스트가 있습니다.

인용되지 않은 속성을 사용하지 않고 다른 컨텍스트가 존재한다는 것을 명심하고 내 자신의 버전을 작성했습니다.

private static final long BODY_ESCAPE =
        1L << '&' | 1L << '<' | 1L << '>';
private static final long DOUBLE_QUOTED_ATTR_ESCAPE =
        1L << '"' | 1L << '&' | 1L << '<' | 1L << '>';
private static final long SINGLE_QUOTED_ATTR_ESCAPE =
        1L << '"' | 1L << '&' | 1L << '\'' | 1L << '<' | 1L << '>';

// 'quot' and 'apos' are 1 char longer than '#34' and '#39' which I've decided to use
private static final String REPLACEMENTS = "&#34;&amp;&#39;&lt;&gt;";
private static final int REPL_SLICES = /*  |0,   5,   10,  15, 19, 23*/
        5<<5 | 10<<10 | 15<<15 | 19<<20 | 23<<25;
// These 5-bit numbers packed into a single int
// are indices within REPLACEMENTS which is a 'flat' String[]

private static void appendEscaped(
        StringBuilder builder,
        CharSequence content,
        long escapes // pass BODY_ESCAPE or *_QUOTED_ATTR_ESCAPE here
) {
    int startIdx = 0, len = content.length();
    for (int i = 0; i < len; i++) {
        char c = content.charAt(i);
        long one;
        if (((c & 63) == c) && ((one = 1L << c) & escapes) != 0) {
        // -^^^^^^^^^^^^^^^   -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        // |                  | take only dangerous characters
        // | java shifts longs by 6 least significant bits,
        // | e. g. << 0b110111111 is same as >> 0b111111.
        // | Filter out bigger characters

            int index = Long.bitCount(SINGLE_QUOTED_ATTR_ESCAPE & (one - 1));
            builder.append(content, startIdx, i /* exclusive */)
                    .append(REPLACEMENTS,
                            REPL_SLICES >>> 5*index & 31,
                            REPL_SLICES >>> 5*(index+1) & 31);
            startIdx = i + 1;
        }
    }
    builder.append(content, startIdx, len);
}

행 길이 제한없이 Gist 에서 복사하여 붙여 넣기를 고려하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.