Shikata Ga Nai

Private? There is no such things.

クロスサイトスクリプティングの防止のコードレビューについてふれてみた

Hello there, ('ω')ノ


JavaScriptの最大の敵は、クロスサイトスクリプティング(略してXSS)で。

このタイプの攻撃は、Webページが信頼できないソースから。

JavaScriptを含む入力を受け入れ、それをページのコンテキストで。

レンダリングするときに発生して。

これは事実上、コードインジェクションの一形態で。

 

攻撃を防ぐために、アプリケーションはユーザ入力を無効にする必要があり。

AngularやReactなどの最新のJavaScriptフレームワークのほとんどは。

これを暗黙的に実行して。

 

場合によっては、独自のUI要素を設計してJavaScriptフレームワークを。

拡張する必要があるので。

これを行うときは、下記のページコンテキストは。

JavaScriptとして入力を実行するため、危険であることに注意する必要があって。

 

 危険なHTML要素属性:innerHTML、src、onLoad、onClickなど...

 危険な関数:eval、setTimeout、setInterval

 

レガシーエンタープライズアプリケーションを最新のUIに移行するという。

トリッキーな状況に陥ることがあって。

このシナリオでは、サーバ側でレンダリングされたコード(JSP、PHP、ASPX)が。

最新のページと共存し、ユーザ入力が無効にされていない場合。

サーバ側のコードのメンテナンスによって新しいクロスサイトスクリプティングの。

問題が発生する可能性があるので。

そのような場合、危険なHTMLマークアップを安全な文字列に変換するには。

HTMLエンコーディング関数を呼び出す必要があって。

 

下記は、HTMLエンコーディングを介してXSSを防止するコードスニペットで。

 

<div class="form-group">
    <label for="search">Search:</label>
    <input type="text" class="form-control" id="search" name="search">

    <input type="submit" id="submit" class="btn" value="Search">
    <div class="alert alert-danger <%=alertVisibility%>">
        Cannot find <%=request.getParameter("search")%>
    </div>
</div>

 

下記のコードサンプルは、HTMLマークアップを無効にして。

 

<div class="form-group">
    <label for="search">Search:</label>
    <input type="text" class="form-control" id="search" name="search">

    <input type="submit" id="submit" class="btn" value="Search">
    <div class="alert alert-danger <%=alertVisibility%>">
        Cannot find <%=StringEscapeUtils.escapeHtml4(request.getParameter("search"))%>
    </div>
</div>

 

 

<script>
    <%
        String searchTxt = StringEscapeUtils.escapeHtml4(request.getParameter("search"));
    %>

    document.cookie = 'search=<%=searchTxt%>';
</script>

 

下記のコードサンプルは、一重引用符も無効にして。

 

<script>
    <%
        String searchTxt = StringEscapeUtils.escapeHtml4(request.getParameter("search")).replace("'","&#39;");
    %>

    document.cookie = 'search=<%=searchTxt%>';
</script>

 

 

$get("/profile", function(data, status){
    if(data!=null){
        var dataArgs = data.split(",");
        if(dataArgs.length > 1){
            var displayName = dataArgs[0];
            var displayNameDiv = $("#displayNameDiv")[0];
            displayNameDiv.innerHTML = displayName;
            var avatarImg = $("#avatarImg")[0];
            avatarImg.src = dataArgs[1];
        }
    }
});

 

下記のコードサンプルは要素テキストを更新して。

 

$get("/profile", function(data, status){
    if(data!=null){
        var dataArgs = data.split(",");
        if(dataArgs.length > 1){
            var displayName = dataArgs[0];
            var displayNameDiv = $("#displayNameDiv")[0];
            displayNameDiv.innerText = displayNameDiv.textContent = displayName;
            var avatarImg = $("#avatarImg")[0];
            avatarImg.src = dataArgs[1];
        }
    }
});

 

 

$get("/profile", function(data, status){
    if(data!=null){
        var dataArgs = data.split(",");
        if(dataArgs.length > 1){
            var displayName = dataArgs[0];
            setTimeout(`showProfile('${displayName}')`, 1000);
        }
    }
});

 

下記のコードサンプルは、入力を別の引数として「setTimeout」に渡して。

 

$get("/profile", function(data, status){
    if(data!=null){
        var dataArgs = data.split(",");
        if(dataArgs.length > 1){
            var displayName = dataArgs[0];
            setTimeout(showProfile, 1000, displayName);
        }
    }
});

 

 

function HelloWorld(message) {
  return (
    <div>
      <h1>Hello!</h1>
      <p dangerouslySetInnerHTML={{ __html: message }} />;
    </div>
  );
}

 

Reactは、デフォルトでテンプレート内のテキストのエスケープを処理して。

 

function HelloWorld(message) {
  return (
    <div>
      <h1>Hello!</h1>
      <p>{message}</p>;
    </div>
  );
}

 

Best regards, (^^ゞ