001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.hayabusa.filter; 017 018import java.io.IOException; 019import java.io.PrintWriter; 020 021import javax.servlet.Filter; 022import javax.servlet.FilterChain; 023import javax.servlet.FilterConfig; 024import javax.servlet.ServletException; 025import javax.servlet.ServletRequest; 026import javax.servlet.ServletResponse; 027import javax.servlet.http.HttpServletRequest; 028 029import org.opengion.fukurou.security.HybsCryptography; 030import org.opengion.fukurou.util.StringUtil; 031import org.opengion.hayabusa.common.HybsSystem; 032import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 033import org.opengion.fukurou.system.ThrowUtil; // 6.4.2.0 (2016/01/29) 034import org.opengion.fukurou.system.HybsConst; // 6.4.5.2 (2016/05/06) 035 036import org.opengion.fukurou.util.FileUtil; // 6.4.5.2 (2016/05/06) 037 038/** 039 * URLCheckFilter は、Filter インターフェースを継承した URLチェッククラスです。 040 * web.xml で filter 設定することにより、該当のリソースに対して、og:linkタグで、 041 * useURLCheck="true"が指定されたリンクURL以外を拒否することができます。 042 * また、og:linkタグを経由した場合でも、リンクの有効期限を設定することで、 043 * リンクURLの漏洩に対しても、一定時間の経過を持って、アクセスを拒否することができます。 044 * また、リンク時にユーザー情報も埋め込んでいますので(初期値は、ログインユーザー)、 045 * リンクアドレスが他のユーザーに知られた場合でも、アクセスを拒否することができます。 046 * 047 * システムリソースの「URL_CHECK_CRYPT」で暗号復号化のキーを指定可能です。 048 * 指定しない場合はデフォルトのキーが利用されます。 049 * キーの形式はHybsCryptographyに従います。 050 * 051 * フィルターに対してweb.xml でパラメータを設定します。 052 * ・filename :停止時メッセージ表示ファイル名 053 * ・ignoreURL:暗号化されたURLのうち空白に置き換える接頭文字列を指定します。 054 * 外部からアクセスしたURLがロードバランサで内部向けURLに変換されてチェックが動作しないような場合に 055 * 利用します。https://wwwX.のように指定します。通常は設定しません。 056 * 057 * 【WEB-INF/web.xml】 058 * <filter> 059 * <filter-name>URLCheckFilter</filter-name> 060 * <filter-class>org.opengion.hayabusa.filter.URLCheckFilter</filter-class> 061 * <init-param> 062 * <param-name>filename</param-name> 063 * <param-value>jsp/custom/refuseAccess.html</param-value> 064 * </init-param> 065 * </filter> 066 * 067 * <filter-mapping> 068 * <filter-name>URLCheckFilter</filter-name> 069 * <url-pattern>/jsp/*</url-pattern> 070 * </filter-mapping> 071 * 072 * @og.group フィルター処理 073 * 074 * @version 4.0 075 * @author Hiroki Nakamura 076 * @since JDK5.0, 077 */ 078public final class URLCheckFilter implements Filter { 079 080 private static final HybsCryptography HYBS_CRYPTOGRAPHY 081 = new HybsCryptography( HybsSystem.sys( "URL_CHECK_CRYPT" ) ); // 5.8.8.0 (2015/06/05) 082 083 private String filename = "jsp/custom/refuseAccess.html" ; // 6.3.8.3 (2015/10/03) アクセス拒否時メッセージ表示ファイル名 084 private boolean isDebug ; 085 private boolean isDecode = true; // 5.4.5.0(2012/02/28) URIDecodeするかどうか 086 087 private String ignoreURL ; // 5.8.6.1 (2015/04/17) 飛んできたcheckURLから取り除くURL文字列 088 089 /** 090 * デフォルトコンストラクター 091 * 092 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 093 */ 094 public URLCheckFilter() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 095 096 /** 097 * フィルター処理本体のメソッドです。 098 * 099 * @og.rev 6.2.0.0 (2015/02/27) new BufferedReader … を、FileUtil.getBufferedReader … に変更。 100 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 101 * @og.rev 6.3.8.3 (2015/10/03) アクセス拒否を示すメッセージファイルの内容を取り出します。 102 * 103 * @param request ServletRequestオブジェクト 104 * @param response ServletResponseオブジェクト 105 * @param chain FilterChainオブジェクト 106 * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。 107 */ 108 public void doFilter( final ServletRequest request, 109 final ServletResponse response, 110 final FilterChain chain ) throws IOException, ServletException { 111 112 if( !isValidAccess( request ) ) { 113 response.setContentType( "text/html; charset=UTF-8" ); 114 final PrintWriter out = response.getWriter(); 115 out.println( refuseMsg() ); // 6.3.8.3 (2015/10/03) 116 out.flush(); 117 return; 118 119 } 120 121 chain.doFilter(request, response); 122 } 123 124 /** 125 * フィルターの初期処理メソッドです。 126 * 127 * フィルターに対してweb.xml で初期パラメータを設定します。 128 * ・filename :停止時メッセージ表示ファイル名(初期:jsp/custom/refuseAccess.html) 129 * ・decode :URLデコードを行ってチェックするか(初期:true) 130 * ・ignoreURL :暗号化されたURLのうち空白に置き換える接頭文字列を指定します(初期:null) 131 * ・debug :URLデコードを行ってチェックするか(初期:false) 132 * 133 * @og.rev 5.4.5.0 (2102/02/28) 134 * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。 135 * @og.rev 5.8.6.1 (2015/04/17) DMZのURL変換対応 136 * @og.rev 6.2.4.1 (2015/05/22) REAL_PATH 対応。realPath は、HybsSystem経由で、取得する。 137 * @og.rev 6.3.8.3 (2015/10/03) filenameの初期値設定。 138 * 139 * @param filterConfig FilterConfigオブジェクト 140 */ 141 public void init(final FilterConfig filterConfig) { 142 143 filename = HybsSystem.getRealPath() + StringUtil.nval( filterConfig.getInitParameter("filename") , filename ); // 6.3.8.3 (2015/10/03) 144 isDecode = StringUtil.nval( filterConfig.getInitParameter("decode"), true ); // 5.4.5.0(2012/02/28) 145 ignoreURL = filterConfig.getInitParameter("ignoreURL"); // 5.8.6.1 (2015/04/17) 146 isDebug = StringUtil.nval( filterConfig.getInitParameter("debug"), false ); 147 } 148 149 /** 150 * フィルターの終了処理メソッドです。 151 * 152 */ 153 public void destroy() { 154 // ここでは処理を行いません。 155 } 156 157 /** 158 * アクセス拒否を示すメッセージ内容。 159 * 160 * @og.rev 6.3.8.3 (2015/10/03) アクセス拒否を示すメッセージファイルの内容を取り出します。 161 * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 162 * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。 163 * 164 * @return アクセス拒否を示すメッセージファイルの内容 165 */ 166 private String refuseMsg() { 167 // アクセス拒否を示すメッセージファイルの内容を管理する FileString オブジェクトを構築する。 168 169 // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 170 return FileUtil.getValue( filename , HybsConst.UTF_8 ); // 6.4.5.2 (2016/05/06) 171 } 172 173 /** 174 * フィルターの内部状態をチェックするメソッドです。 175 * 176 * @og.rev 5.4.5.0 (2012/02/28) Decode 177 * @og.rev 5.8.6.1 (2015/04/17) DMZのURL変換対応 178 * @og.rev 5.8.8.2 (2015/07/17) マルチバイト対応追加 179 * @og.rev 6.3.8.4 (2015/10/09) デバッグメッセージの追加 180 * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。 181 * 182 * @param request ServletRequestオブジェクト 183 * 184 * @return (true:許可 false:拒否) 185 */ 186 private boolean isValidAccess( final ServletRequest request ) { 187 // 6.3.8.4 (2015/10/09) デバッグメッセージの追加 188 if( isDebug ) { 189 System.out.println( ((HttpServletRequest)request).getRequestURI() ); 190 } 191 192 String checkKey = request.getParameter( HybsSystem.URL_CHECK_KEY ); 193 if( checkKey == null || checkKey.isEmpty() ) { 194 if( isDebug ) { 195 System.out.println( " check NG [ No Check Key ]" ); 196 } 197 return false; 198 } 199 200 boolean rtn = false; 201 try { 202 checkKey = HYBS_CRYPTOGRAPHY.decrypt( checkKey ).replace( "&", "&" ); 203 204 if( isDebug ) { 205 System.out.println( " checkKey=" + checkKey ); 206 } 207 208 // 5.8.6.1 (2015/04/17) DMZのURL変換対応 (ちょっと整理しておきます) 209 210 final int tmAd = checkKey.lastIndexOf( ",time=" ); 211 final int usAd = checkKey.lastIndexOf( ",userid=" ); 212 213 String url = checkKey.substring( 0 , tmAd ); 214 final long time = Long.parseLong( checkKey.substring( tmAd + 6, usAd ) ); 215 final String userid = checkKey.substring( usAd + 8 ); 216 217 // 4.3.8.0 (2009/08/01) 218 final String[] userArr = StringUtil.csv2Array( userid ); 219 220 // 5.8.6.1 (2015/04/17)ignoreURL対応 221 if( ignoreURL != null && ignoreURL.length()>0 && url.indexOf( ignoreURL ) == 0 ){ 222 url = url.substring( ignoreURL.length() ); 223 } 224 225 if( isDebug ) { 226 System.out.println( " [ignoreURL]=" + ignoreURL ); // 2015/04/17 (2015/04/17) 227 System.out.println( " [url] =" + url ); 228 System.out.println( " [vtime] =" + time ); 229 System.out.println( " [userid] =" + userid ); 230 } 231 232 String reqStr = ((HttpServletRequest)request).getRequestURL().toString() + "?" + ((HttpServletRequest)request).getQueryString(); 233 // 5.4.5.0 (2012/02/28) URLDecodeを行う 234 if( isDecode ){ 235 if( isDebug ) { 236 System.out.println( "[BeforeURIDecode]="+reqStr ); 237 } 238 reqStr = StringUtil.urlDecode( reqStr ); 239 url = StringUtil.urlDecode( url ); // 5.8.8.2 (2015/07/17) 240 } 241 reqStr = reqStr.substring( 0, reqStr.lastIndexOf( HybsSystem.URL_CHECK_KEY ) -1 ); 242 // String reqStr = ((HttpServletRequest)request).getRequestURL().toString(); 243 final String reqUser = ((HttpServletRequest)request).getRemoteUser(); 244 245 if( isDebug ) { 246 System.out.println( " [reqURL] =" + reqStr ); 247 System.out.println( " [ctime] =" + System.currentTimeMillis() ); 248 System.out.println( " [reqUser]=" + reqUser ); 249 } 250 251 if( reqStr.endsWith( url ) 252 && System.currentTimeMillis() - time < 0 253 && userArr != null && userArr.length > 0 ) { 254 // 4.3.8.0 (2009/08/01) 255 for( int i=0; i<userArr.length; i++ ) { 256 if( "*".equals( userArr[i] ) || reqUser.equals( userArr[i] ) ) { 257 rtn = true; 258 if( isDebug ) { 259 System.out.println( " check OK" ); 260 } 261 break; 262 } 263 } 264 } 265 } 266 catch( final RuntimeException ex ) { 267 if( isDebug ) { 268 final String errMsg = "チェックエラー。 " 269 + " checkKey=" + checkKey 270 + " " + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 271 System.out.println( errMsg ); 272 System.err.println( ThrowUtil.ogStackTrace( ex ) ); // 6.4.2.0 (2016/01/29) 273 } 274 rtn = false; 275 } 276 return rtn; 277 } 278 279 /** 280 * 内部状態を文字列で返します。 281 * 282 * @return このクラスの文字列表示 283 * @og.rtnNotNull 284 */ 285 @Override 286 public String toString() { 287 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 288 .append( "UrlCheckFilter" ) 289 .append( "filename=[" ).append( filename ).append( "],") 290 .append( "isDecode=[" ).append( isDecode ).append( ']'); // 6.0.2.5 (2014/10/31) char を append する。 291 return buf.toString(); 292 } 293}