Table of Contents
まず server template と browser source を区別する
Standard syntax の .jsp page では、<%-- ... --%> は container が page translation 時に無視する JSP comment です。<!-- ... --> は HTML comment で response の template text なので client へ送られます。HTML comment 内の JSP expression、Expression Language(EL)、tag も server で実行され、その結果が「View Source」に現れる場合があります。
したがって HTML comment は、data を隠す、server code を無効にする、XSS を防ぐための境界ではありません。本 guide は 2026 年 9 月 1 日に最終確認し、Jakarta Pages 4.0、Servlet 6.1、Expression Language 6.0、Jakarta Tags 3.0 を基準に、scriptlet より servlet/controller、EL、JSTL を優先します。
1. JSP の translation phase と request phase
Jakarta Pages 4.0 specification は 2 phase を定義します。Container はまず JSP を servlet class に translate・compile し、request ごとにその servlet を実行します。Translation は初回 request 時、または deploy/build 時の precompile で行われます。
| Page element | Translation phase | Request/response result |
|---|---|---|
<%@ page ... %> などの JSP directive | Page translation を設定 | Directive 自体は response に書かれない |
JSP comment <%-- ... --%> | Body は完全に無視 | Response に現れず、内部 EL・scripting element も実行されない |
HTML comment <!-- ... --> | Template text として処理 | Comment と内部の dynamic result が response に出る |
EL ${...} | Page implementation の一部として parse | JSP/EL rule に従い request 時に評価 |
JSP expression <%= ... %> | Java output logic へ translate | Request 時に実行して直接出力する、移行対象の scripting element |
| JSTL/custom tag | Tag invocation へ translate | Request 時に実行し、出力は tag に依存 |
Deploy 後に page が再 translate されるかは container、precompile、change detection に依存します。Development auto-reload を production deploy の証明にせず、WAR、container、JDK の version と digest を記録します。
2. HTML comment、JSP comment、code comment は別物
これは動作だけを示し、private data や user input は含みません。
<%-- Server-only note: the EL here is ignored: ${1 + 1} --%>
<!-- Client-visible note: the EL result is ${1 + 1} -->
Response には HTML comment だけが残り、通常は値が評価済みです。
<!-- Client-visible note: the EL result is 2 -->
Java の // と / ... /、JavaScript/CSS comment、HTML comment はそれぞれの parser にだけ意味があります。JSP comment の代わりにはなりません。JSP document(.jspx)は XML syntax を使い、comment と escaping rule が異なります。この standard-syntax 例を別 test なしに JSP document へコピーしないでください。
3. 過去の問いへの回答:評価されるが、scriptlet は使わない
2011 年原文は <%= expression %> を HTML comment 内に置きました。Specification は template content の HTML comment に dynamic expression を認めるため、expression は実行され、その出力は client に見えます。しかし syntax が有効でも安全設計とは限りません。Expression は side effect、exception、internal state disclosure を起こせ、古い Date#toLocaleString() API も obsolete です。
Scriptlet を HTML comment で囲んで「無効」にしないでください。HTML comment は server execution を止めません。JSP comment は body を無視しますが、business logic を JSP に残すと test、encoding、upgrade が難しくなります。Computation、I/O、authorization decision、date formatting は controller/service へ移し、準備済み表示値だけを request scope に置きます。
4. EL、scriptlet、data boundary
Jakarta Expression Language 6.0 は immediate ${...} evaluation と deferred #{...} evaluation を区別し、hosting technology が利用時期を決めます。通常の JSP view は ${...} で scope 内の bean、map、attribute を読みます。
EL は HTML encoder ではありません。${param.name} を直接出しても自動的に safe HTML にはなりません。User に EL source、property name、method call を組み立てさせてはいけません。Expression は template author が固定し、user value は data に限定します。Resolver が公開する object/method を制限し、view で authorization decision を行わず、exception や comment で internal class、path、query、token を漏らしません。
Scriptlet <% ... %>、declaration <%! ... %>、expression <%= ... %> は Java を template に混在させます。Maintenance guide では使用しません。Migration では characterization test を先に作り、logic を servlet/controller へ移し、表示 branch と loop を EL/JSTL に置換します。
5. 安全な servlet/controller と view の境界
Controller は validation、authorization、display model の準備を行い、直接 request できない WEB-INF 配下の JSP へ forward します。Sample data は trusted constant です。実 application では authentication と authorization を混同しません。
package example;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/welcome")
public final class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
request.setAttribute("message", "Welcome");
request.getRequestDispatcher("/WEB-INF/views/welcome.jsp")
.forward(request, response);
}
}
View は Jakarta Tags 3.0 の standard core URI を使います。c:out の escapeXml default は true ですが、review のため明示します。
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome</title>
</head>
<body>
<%-- Internal implementation note; never place secrets here. --%>
<p><c:out value="${requestScope.message}" escapeXml="true" /></p>
</body>
</html>
Application には Jakarta Tags 3.0 と compatible で version-pinned な implementation が必要です。Servlet/JSP container に必ず bundled されるわけではありません。Source、version、license を記録せず tag-library JAR を shared server directory に手作業で置かないでください。
6. Output encoding は HTML context に合わせる
OWASP XSS Prevention Cheat Sheet は正確な output context に合う encoding を要求します。Input validation は business correctness に役立ちますが output encoding の代替ではありません。CSP は defense in depth であり encoding の代わりではありません。
| Output location | Baseline treatment | Stop condition |
|---|---|---|
| HTML text node | c:out または trusted HTML text encoder | Trusted HTML を含める必要がある。別途 audit した sanitizer と type boundary を導入 |
| Quoted HTML attribute | HTML attribute encoder を使い、必ず quote | Event handler、style、srcdoc など危険な attribute |
| URL parameter | URL builder/encoder で parameter を構築し、最終 attribute を HTML attribute encode | User が scheme または origin を制御 |
| JavaScript string/value | JavaScript-context encoder または safe JSON serializer | HTML encoding を JavaScript encoding として扱おうとしている |
| CSS value | Dynamic CSS を避け、必要なら CSS-context encoder と allowlist | User が property、selector、URL を制御 |
| HTML comment | User value、secret、diagnostic detail を置かない | Value が comment delimiter を含む、または公開で情報漏えい |
c:out は基本 HTML/XML escaping には適しますが、全 context 共通 encoder ではありません。OWASP Java Encoder は context-specific Java/JSP encoding API を提供します。採用時は compatible version を pin し dependency scan します。未監査 string に escapeXml="false" を使いません。
7. String 連結ではなく安全に URL を構築する
Jakarta Tags の c:url と c:param は application path と parameter に明確な境界を作り、c:out が HTML attribute を保護します。
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<c:url var="profileUrl" value="/profile">
<c:param name="id" value="${requestScope.publicUserId}" />
</c:url>
<a href="<c:out value="${profileUrl}" escapeXml="true" />">Profile</a>
この例は access を authorize せず、external scheme も許可しません。Controller は ID validation と object-level authorization を行います。Redirect、download、javascript: URL、open redirect、cross-origin destination には別 allowlist が必要で、URL encoding だけで trusted にはなりません。
8. Comment は secret storage や debug channel ではない
HTML comment は response、CDN、proxy、browser cache、recording、test artifact に入ります。Username、email、internal host、file path、stack trace、SQL、feature flag、脆弱 version 情報、session ID、token、authorization decision を出力しません。c:out で encode しても情報は公開されます。
JSP comment は response には出ませんが source、WAR、backup、code-review system に残るため credential を保存できません。Structured log は server-side request ID で関連付けた必要最小 field だけにします。Personal identifier は削除・hash 化し、cookie、authorization header、request-body secret を記録しません。
WHATWG HTML comment syntax は comment content も制限します。User value が delimiter を壊したり別 DOM を作ったりできます。Untrusted dynamic value を HTML comment に置かないことが最安全です。
9. 最小の実行可能な動作例
次を comment-demo/index.jsp として保存します。Built-in JSP EL のみで JSTL に依存せず、container boundary test を小さく保ちます。Production view では前述の encoding を使用します。
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true" %>
<%-- Server-only marker: this value is ignored: ${1 + 1} --%>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSP comment boundary test</title>
</head>
<body>
<!-- Public diagnostic only: EL result ${1 + 1} -->
<p id="result">EL result: ${1 + 1}</p>
</body>
</html>
Constant arithmetic は phase boundary の観察だけに使います。Request parameter を直接出してよいという意味ではありません。EL result は HTML-context encoding を自動では受けません。
10. 制限された container test boundary
Apache Tomcat 11 documentation は Jakarta Servlet 6.1 と Pages 4.0 に対応します。以下は Docker Official Image の Tomcat pageを使い、loopback のみに bind、webapp を read-only mount、write directory を temporary filesystem に限定します。Tag を pull して immutable digest を記録し、その digest を実行します。将来の再現では記録した digest を直接使います。
set -eu
TEST_ROOT="$PWD/comment-demo"
TOMCAT_TAG=tomcat:11.0-jre21-temurin-noble
test -f "$TEST_ROOT/index.jsp"
docker pull "$TOMCAT_TAG"
TOMCAT_IMAGE=$(docker image inspect "$TOMCAT_TAG" --format '{{index .RepoDigests 0}}')
test -n "$TOMCAT_IMAGE"
echo "$TOMCAT_IMAGE"
docker run --rm --name jsp-comment-test --read-only --cap-drop=ALL --security-opt=no-new-privileges --tmpfs /usr/local/tomcat/conf/Catalina --tmpfs /usr/local/tomcat/temp --tmpfs /usr/local/tomcat/work --tmpfs /usr/local/tomcat/logs --publish 127.0.0.1:18080:8080 --volume "$TEST_ROOT:/usr/local/tomcat/webapps/ROOT:ro" "$TOMCAT_IMAGE"
Command は foreground で動きます。Ctrl-C で止めると --rm が container を削除します。これは local semantics test であり production-hardening template ではありません。Source repository、secret、Docker socket、production data を mount しないでください。
11. Response、log、failure boundary を検証する
別 terminal から local port を request します。HTML comment に評価済み 2 があり、JSP-comment marker が全くないことを要求します。
set -eu
curl --fail --silent --show-error --retry 20 --retry-all-errors --retry-delay 1 http://127.0.0.1:18080/ > response.html
grep --fixed-strings '<!-- Public diagnostic only: EL result 2 -->' response.html
grep --fixed-strings '<p id="result">EL result: 2</p>' response.html
if grep --quiet --fixed-strings 'Server-only marker' response.html; then
exit 1
fi
Container log に JSP translation/compile error がないことも確認します。Response に ${1 + 1} が残る場合、file が JSP servlet で処理されていない、page/application config で EL が disabled、mapping が誤り、または cached static file の可能性があります。Scriptlet で回避しません。Server-only marker が現れたら、意図した artifact と container を request したとまず証明し、file syntax を調べます。
Test artifact に user data はありませんが test 後に削除します。CI では JDK、Tomcat image digest、WAR hash、first-request と later-request result を記録し、1 台の development server だけでなく supported target container ごとに繰り返します。
12. Scriptlet migration checklist
- [ ] 既存 response body、status code、header、authorization behavior の characterization test を作る。
- [ ] DB access、I/O、date formatting、branch、authorization decision を service/controller へ移す。
- [ ] JSP には必要最小の request/view model だけを公開する。
- [ ] Scriptlet の loop と branch を EL/JSTL に置換し、user input から EL を構築しない。
- [ ] 全 dynamic output を HTML、attribute、URL、JavaScript、CSS context ごとに review する。
- [ ] Client-side HTML comment から secret、diagnostic、user value を除く。
- [ ] Login、error page、i18n、null、Unicode、malicious payload を test する。
- [ ] Jakarta API、Tags implementation、JDK、container version と digest を pin する。
- [ ] Staging で全 JSP を precompile または trigger し、translation warning/error を release blocker にする。
- [ ] Response body、browser DOM、security scan を合わせて検証し、見た目だけで判断しない。
13. 現在の公式・権威あるリファレンス
- Jakarta Pages 4.0
- Jakarta Pages 4.0 specification
- Jakarta Servlet 6.1
- Jakarta Expression Language 6.0
- Jakarta Tags 3.0
- WHATWG HTML comments
- OWASP Cross Site Scripting Prevention Cheat Sheet
- OWASP Java Encoder
- Apache Tomcat 11 documentation
- Docker Official Image: Tomcat
14. 2011/2023 年原文の正確なアーカイブ
以下は source_export の可視本文全体です。原文の文字、link、空白、句読点、nonstandard comment dash は変更せず、不活性な外側 code fence だけを追加しました。元の export と Git history は変更していません。
警告:archive は scriptlet、obsolete date API、client-visible comment 内の dynamic data を使います。Historical audit のためだけに保持しています。上の maintained guide を使用してください。
在客户端显示一个注释
Table of Contents
Toggle
- [JSP 语法](https://blog.lazying.art/en/html/computer_internet/java_j2ee_jsp/472/html%e6%b3%a8%e9%87%8a%e4%b8%ad%e6%8f%92%e5%85%a5jsp%e8%a1%a8%e8%be%be%e5%bc%8f.html/#JSP_%E8%AF%AD%E6%B3%95)
- [例子 1](https://blog.lazying.art/en/html/computer_internet/java_j2ee_jsp/472/html%e6%b3%a8%e9%87%8a%e4%b8%ad%e6%8f%92%e5%85%a5jsp%e8%a1%a8%e8%be%be%e5%bc%8f.html/#%E4%BE%8B%E5%AD%90_1)
- [例子 2](https://blog.lazying.art/en/html/computer_internet/java_j2ee_jsp/472/html%e6%b3%a8%e9%87%8a%e4%b8%ad%e6%8f%92%e5%85%a5jsp%e8%a1%a8%e8%be%be%e5%bc%8f.html/#%E4%BE%8B%E5%AD%90_2)
- [总结](https://blog.lazying.art/en/html/computer_internet/java_j2ee_jsp/472/html%e6%b3%a8%e9%87%8a%e4%b8%ad%e6%8f%92%e5%85%a5jsp%e8%a1%a8%e8%be%be%e5%bc%8f.html/#%E6%80%BB%E7%BB%93)
## JSP 语法
<!– comment [ <%= expression %> ] –>
## 例子 1
<!– This file displays the user login screen –>
在客户端的HTML源代码中产生和上面一样的数据:
<!– This file displays the user login screen –>
## 例子 2
<!– This page was loaded on <%= (new java.util.Date()).toLocaleString() %> –>
在客户端的HTML源代码中显示为:
<!– This page was loaded on January 1, 2000 –>
## 总结
这种注释和HTML注释很像,也就是它可以在”查看源代码”中看到。
唯一有些不同的就是,你可以在这个注释中用表达式(例子2所示).这个表达式是不定的,由页面不同而不同,你能够使用各种表达式,只要是合法的就行,更多的请看表达式。
//和/* */在html里也是常用的注释,但只能用在js和CSS语言,不对HTML语言起作用!
