「PHPの基礎 - XSS対策」の版間の差分

ナビゲーションに移動 検索に移動
54行目: 54行目:
<br>
<br>
==== DOMのプロパティとメソッドの安全な使用 ====
==== DOMのプロパティとメソッドの安全な使用 ====
* innerHTMLの代わりに、textContentを使用してテキストを設定する。
===== innerHTMLの代わりに、textContentを使用してテキストを設定する =====
* setAttribute関数の代わりに、直接プロパティを設定する。
*: textContentはテキストをHTMLとして解釈せず、純粋なテキストとして扱うため、スクリプトタグや悪意のあるコードが実行されるリスクを排除できる。
*: 例: element.id = 'newId'
*: DOMベースXSS攻撃からの保護が自動的に行われる。
<br>
<br>
<u>※注意</u><br>
<u>HTMLマークアップが必要な場合は使用できない。</u><br>
<u>HTMLを意図的に挿入する必要がある場合は、DOMPurify等のサニタイザーライブラリの使用を検討する。</u><br>
<br>
<syntaxhighlight lang="javascript">
// 危険な例 : innerHTMLの使用
function unsafeSetContent(elementId, content) {
    const element = document.getElementById(elementId);
    element.innerHTML = content;  // 非推奨 : XSS攻撃の可能性あり
}
// 安全な例 : textContentの使用
function safeSetContent(elementId, content) {
    const element = document.getElementById(elementId);
    element.textContent = content;  // 推奨 : HTMLタグとしてパースされない
}
// 実装例 1 : ユーザ入力の表示
function displayUserInput(input) {
    const displayElement = document.getElementById('output');
    displayElement.textContent = input;
}
// 実装例 2 : 動的なリスト項目の追加
function addListItem(text) {
    const li = document.createElement('li');
    li.textContent = text;  // 安全な方法でテキストを設定
    document.querySelector('ul').appendChild(li);
}
// 実装例 3 : エラーメッセージの表示
function showError(errorMessage) {
    const errorElement = document.getElementById('error');
    errorElement.textContent = errorMessage;
    errorElement.classList.add('error');
}
// 実装例 4 : 複数要素の更新
function updateMultipleElements(data) {
    // ユーザー名を安全に表示
    document.getElementById('username').textContent = data.username;
    // スコアを安全に表示
    document.getElementById('score').textContent = data.score;
    // ステータスを安全に表示
    document.getElementById('status').textContent = data.status;
}
// 実装例 5 : テンプレートとの組み合わせ
function createUserCard(user) {
    const template = document.getElementById('user-card-template');
    const clone = template.content.cloneNode(true);
    // 各フィールドを安全に更新
    clone.querySelector('.user-name').textContent  = user.name;
    clone.querySelector('.user-email').textContent = user.email;
    clone.querySelector('.user-role').textContent  = user.role;
    return clone;
}
// 使用例
const safeData = {
    username: "ユーザ1",
    message: "<script>alert('XSS')</script>"
};
// 安全な実装
// 結果 : スクリプトタグがそのままテキストとして表示される
safeSetContent('message', safeData.message);
</syntaxhighlight>
<br>
===== setAttribute関数の代わりに、直接プロパティを設定する =====
直接プロパティを設定する場合は、Webブラウザの内部的なサニタイズ処理が使用できる。<br>
特に、イベントハンドラの設定において、悪意のあるコードの実行を防ぐことができる。<br>
<br>
また、プロパティは型チェックが行われるため、型の安全性が向上する。<br>
<br>
使用シーンを以下に示す。<br>
* 標準的なHTML属性の設定 (id, class, src, href等)
* フォーム要素のプロパティ設定 (value, disabled, required等)
* スタイルプロパティの設定 (style.propertyName)
* データ属性の設定 (dataset.propertyName)
* ARIA属性の設定 (aria-*属性)
<br>
<u>※注意が必要な属性</u><br>
* href属性
*: URLのバリデーションを行った後に設定する。
* src属性
*: 信頼できるソースからのURLのみを使用する。
* onclick等のイベントハンドラ
*: 直接文字列を設定せず、addEventListenerメソッドを使用する。
<br>
<syntaxhighlight lang="javascript">
// 危険な例 : setAttributeの使用
// 悪意のあるスクリプトが実行される可能性がある
function unsafeSetAttribute(element, value) {
    element.setAttribute('onclick', value);  // 避けるべき
    element.setAttribute('href', value);    // 避けるべき
}
// 安全な例 - 直接プロパティ設定
// プロパティに直接設定
function safeSetProperties(element, value) {
    element.id = value;        // 推奨
    element.className = value;  // 推奨
    element.title = value;      // 推奨
}
// 実装例 1 : フォーム要素の操作
// 安全な方法でプロパティを設定
function updateFormElement(input, value) {
    input.disabled = value;    // disabled属性
    input.required = value;    // required属性
    input.value = value;        // value属性
    input.placeholder = value;  // placeholder属性
}
// 実装例 2 : イメージ要素の操作
function updateImage(imgElement, data) {
    imgElement.src = data.url;        // src属性
    imgElement.alt = data.altText;    // alt属性
    imgElement.width = data.width;    // width属性
    imgElement.height = data.height;  // height属性
}
// 実装例 3 : アンカー要素の操作
// URLのバリデーションを行ってから設定
function updateLink(linkElement, data) {
    if (isValidUrl(data.url)) {
      linkElement.href = data.url;
    }
    linkElement.target = '_blank';  // target属性
    linkElement.rel = 'noopener';  // rel属性
}
// 実装例 4 : スタイルの操作
// CSSプロパティを直接設定
function updateStyles(element, styles) {
    element.style.backgroundColor = styles.bg;
    element.style.color = styles.color;
    element.style.fontSize = styles.size;
}
// 実装例 5 : データ属性の操作
// data-*属性を安全に設定
function updateDataAttributes(element, data) {
    element.dataset.id = data.id;
    element.dataset.name = data.name;
    element.dataset.value = data.value;
}
// 実装例 6 : ARIA属性の操作
function updateAriaAttributes(element, state) {
    element.ariaLabel = state.label;
    element.ariaExpanded = state.expanded;
    element.ariaHidden = state.hidden;
}
// URLバリデーション用のヘルパー関数
function isValidUrl(url) {
    try {
      new URL(url);
      return true;
    }
    catch {
      return false;
    }
}
// 使用例
const element = document.getElementById('target');
// 安全な実装
element.className = 'safe-class';
element.id = 'safe-id';
const img = document.createElement('img');
img.src = 'safe-url.jpg';
img.alt = 'Safe image';
// データ属性の安全な設定
const dataElement = document.getElementById('data-element');
dataElement.dataset.userInput = 'Safe data';
</syntaxhighlight>
<br>
==== URLパラメータの処理 ====
==== URLパラメータの処理 ====
* location.hashやlocation.searchからの値を使用する前に、<code>encodeURIComponent</code>関数でエンコードする。
* location.hashやlocation.searchからの値を使用する前に、<code>encodeURIComponent</code>関数でエンコードする。

案内メニュー