担当しているプロジェクトの脆弱性診断報告書に従てセキュリティ強化対応を行いました。本記事はその代表的な箇所と対策を記録します。お客さまのシステムからエビデンスを取っていけないので、うち会社のパッケージからエビデンスをとって説明します。
1、SQLインジェクションの影響を受ける可能性がある
深刻度
緊急
現象
画面から送信情報に「'」を入力して送信してみるとシステムエラーは発生することがあります。
説明
この指摘内容は結構古典的で絶対考慮すべきです。実はプロジェクトのEfwフレームワークのDB処理にすでにPreparedStatementを用いて対応しました。通常のDB処理ならSQLインジェクションの心配はありません。だが、脆弱性審査されるプロジェクトのDB処理は特別で、CData RestAPIサービスを利用しています。そのプログラムは以下のようです。
...
var email=params["#email"];
var objData =new Record(
rest.get(
"https://myserver/mytable/?$select=MAIL,SEI,MEI,ID"
+"&$filter=DELETE_FLG+eq+'0'+AND+account_type+eq+1"
+"+AND+MAIL+eq"+"'"+email+"'",{
"x-cdata-authtoken":"XXXXXXXXXXXX"
}
).value);
...
「email」の変数は画面から値を受取り、restAPIのパラメータとして利用します。そのパラメータはURLの文字列に足し算する方式です。それで、SQLインジェクションの可能性が出てきました。
対応
ひとつずつRestAPIパラメータに「'」を無害化に処理すると影響するソースの範囲が広いし、修正により2次災害が発生する可能性も高いです。プロジェクトの業務により、「'」が画面から登録したり検索したりする可能性がありません。そして、禁則と置換文字登録で対応するほうがスマートになります。
#########forbidden chars#######################################################
efw.forbidden.characters =<>+'%
efw.forbidden.replacement =\uff1c\uff1e\uff0b\u2019
以上プロパティ設定で、禁則文字を全角変換するから無害化を実現しています。
効果
上記設定の後、画面から「'」を入力されたら、受信時全角「’」になることを確認できます。
2、CORSが適切に設定されていない
深刻度
重要
現象
任意のサイトを表示しているブラウザーから、該当サイトへajax接続する際CORSが許可されてしまいます。再現するため、以下はlocalhostからうちの会社のデモサイトへajax接続してみます。
CORSは許可されています。
説明
これはefw.propertiesにCORS設定不備による現象です。CORS未設定の場合、デフォルトすべてのCORS依頼は許可です。
#########cors##################################################################
#Cross-Origin Resource Sharing
# *:ok to all, null:ng to all, or http://0.0.0.0:8080,http://9:9:9:9
#efw.cors = *
フレームワークとして、CORSのデフォルトをnullにして全部拒否にしたほうが安全かもしれません。いつかフレームワークの初期値を考え直すかもしれません。
CORSの場合cookie情報の維持はとても困難でブラウザー側の設定も必要だそうです。cookieを維持しないままのCORSならJSESSIONIDを伝送できないから、ログイン状態を維持できません。一般的なシステムにとって、この状態で被害を受けることは考えづらいです。もちろん、これはアプリが頑丈だというより、現在のブラウザーが頑丈だというべきです。
対応
CORSを禁止するように設定します。
#########cors##################################################################
#Cross-Origin Resource Sharing
# *:ok to all, null:ng to all, or http://0.0.0.0:8080,http://9:9:9:9
efw.cors = null
効果
効果を試験するため、http://localhost:8080/ から http://127.0.0.1:8080/ へajax呼び出しを行って、効果を確認します。OPTIONS接続のリスポンスにAccess-Control-Allow-Originのヘッダ情報がなくなることを確認できます。
3、URLパラメータにscriptタグの混入でXSS(反射型)の影響を受ける可能性がある
深刻度
警告
現象
URLパラメータから受け取るキー項目を加工してscriptタグを混入します。キー項目はjspのhiddenなどに格納するから、混入した情報はjspに貼り付けられて実行されます。
説明
jspの抜粋は以下です。
<%
String actionMode=(String)request.getParameter("actionMode");
%>
<input type="hidden" class="actionMode" value="<%= actionMode%>"/>
もしactionModeにscriptタグを混入する場合、ブラウザーからみるhtmlは以下になります。これでalertが実行されます。
<input type="hidden" class="actionMode" value="singleedit"><script>alert(1)</script>"/>
審査されるシステムは、ログインしないと閲覧・操作できないので、リンクから簡単に画面を開くことではありません。そして、改ざんURLから広げる影響を考えにくいです。また、Efwフレームワークはajax方式でサーバに送信するから、改ざんキー項目はDBに存在しないのでシステムエラー画面がでて、サーバ障害にならないです。
対応
jspにrequest.getParameterを利用するからこのリスクが存在しています。もしrequest.getParameterを利用しなければこのリスクも消滅するはずです。つまりajaxサーバ処理イベントに直接requestのパラメータを受取るようにすることです。これでjsp側のhidden変数が不要になるから、scriptタグはjspに貼り付けられないです。
var myevent={}
myevent.paramsFormat={}
myevent.fire(params){
var mode=request.get("actionMode");
}
またはjsp側でjstl関数を利用してエスケープします。
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<%
String actionMode=(String)request.getParameter("actionMode");
%>
<input type="hidden" class="actionMode" value="${ fn:escapeXml(actionMode)}"/>
4、イベントIDにscriptタグの混入でXSS(DOM型)の影響を受ける可能性
深刻度
警告
現象
ajaxイベントのイベントIDを加工してscriptタグを混入します。混入した情報をサーバに送信して、サーバはそのイベントが存在していないことを発見して、エラー情報をクライアントに戻します。それで、エラーダイアログにイベントIDの表示とともにscriptが実行されます。
説明
厳密にいうと、イベントIDへの攻撃は意味がありません。efwフレームワークにイベントIDはイベントjsファイルのファイル名なので、イベントIDを改ざんするとファイルが見つからないからエラーが発生し、システムエラー画面に遷移されます。これでscript混入により、サーバ側の誤動作または他のユーザの画面に影響を起こせないです。
だが、クライアントの混入情報をサーバに送って、サーバ側の戻り値により悪意のscriptが実行されることは事実です。この点はDOMベースXSSに当たるから、リスクが存在すると指摘されました。
対応
フレームワーク改善の一つとして対応します。
イベントIDの存在しないエラーの処理に、クライアントからのイベントID情報をhtmlエンコードを行い、混入されるscriptタグを文字列として画面に表示させます。
効果
5、Cookie中のセッションIDがSecure属性で保護されていない
深刻度
警告
現象
以下のエビデンスのように、ヘッダ情報にSet-Cookieの情報には、secureが設定されていません。
説明
一つのwebサイトでhttpとhttps通信が両方存在する場合、httpから接続して、JSESSIONIDなどの情報を取得して、https通信時それを利用してcookie情報を改ざんするというリスクです。
もしhttps通信のみなら、secureがなくても大丈夫のはずです。また、httpとhttpsが使い分けの場合リスクが低くなります。例えば、社外向けはhttps+domainで、社内向けはhttp+ipで、社外からhttpを接続できないようにファイアウォールで設定します。この場合、httpは社外から傍聴できないため、cookie情報は盗まれないはずです。
対応
アプリのweb.xmlにcookie-configの設定を追加します。
<session-config>
<session-timeout>60</session-timeout>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
効果
設定後、もう一回画面を開いてcookieを確認する場合、「Secure」の文言を発見できます。
だが、注意すべきですが、上記エビデンスは、https+domainまたはhttp+localhostまたはhttp+127.0.0.1で確認できます。ほかの場合、そのcookie情報はブラウザーに拒否されるからアプリが正しく稼働できません。
そしてもし社外https+domain、社内http+ipの使い方なら、上記対応方法は実施できません。
6、クリックジャッキング対策ヘッダが適切に設定されていない
深刻度
注意
現象
以下のようにサイトをframeの中に稼働できてしまいます。
<html>
<body>
hello eprocess<br>
<iframe src="https://XXXXXX.co.jp/eprocess/LG01.jsp" style="width:80%;height:80%">
</body>
</html>
説明
ハッキング先のサイトをframeにおいて、外回りに何かユーザさんを騙すコンテンツを置くやり方があります。これを防ぐため、httpヘッダ情報に該当サイトはframeの中に稼働しないというフラグをそう情報をブラウザーに伝えることができます。
- X-Frame-Options: DENY
- X-Content-Type-Options: nosniff
- X-Xss-Protection: 1; mode=block
対応
web.xmlに以下の設定内容を追加します。
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
効果
以下のように上記対応適用済みのローカルサイトに対してテストします。
<html>
<body>
hello eprocess<br>
<iframe src="http://localhost:8080/eprocess/LG01.jsp" style="width:80%;height:80%">
</body>
</html>
7、不要な開発情報が出力される
深刻度
警告
現象
正しいパラメータで以下のように表示します。
パラメータ改ざんによりエラー発生して、詳細エラー情報が表示されます。
説明
エラー詳細内容により、システムの仕組みが推測できてしまって、継続のハッキングのヒントになります。このため、エラー詳細内容を画面に表示しないほうがよいです。
対応
web.xmlに以下の設定情報を追記します。
<error-page>
<!-- Uncaught exception -->
<location>/error.jsp</location>
</error-page>
効果
8、アプリケーションのログに機密な情報が含まれている
深刻度
なし
現象
以下のエビデンスのように、ログインのPWDは明文でログに表しています。
説明
ログに機密情報が含まれると、運用保守の内部担当からその機密情報を入手できます。お客さんの担当者はそれが気になります。
対応
パスワードのパラメータにsecureの装飾子をつけるようにします。
var LG01_submit={};
LG01_submit.name="LG01 ログイン 認証処理";
LG01_submit.paramsFormat={
"#txt_uid":"required:true;display-name:ユーザーID;", //アカウント
"#txt_pwd":"required:true;display-name:パスワード;secure:true;",//パスワード
"#dep_id":"required:true;display-name:事業機能;", //事業機能ID
};
LG01_submit.fire=function(params){
・・・