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.fukurou.security; 017 018import java.util.Map; 019import java.util.Collections; // 6.4.3.1 (2016/02/12) refactoring 020import java.util.LinkedHashMap; // 6.4.3.3 (2016/03/04) 021 022/** 023 * URLHashMap は、セキュリティ強化の為の Hybs独自の暗号化クラスです。 024 * 025 * このクラスは、暗号化キーを受け取り、それに基づいて暗号化/復号化を行います。 026 * ここでの暗号化は、秘密キー方式でバイト文字列に変換されたものを、16進アスキー文字に 027 * 直して、扱っています。よって、暗号化/復号化共に、文字列として扱うことが可能です。 028 * 029 * @og.rev 5.2.2.0 (2010/11/01) 新規追加 030 * @og.rev 6.4.3.3 (2016/03/04) キャッシュを、世代交代のWeakHashMapから、LinkedHashMap(固定容量) に変更します。 031 * @og.group ライセンス管理 032 * 033 * @version 5.2.2.0 (2010/11/01) 034 * @author Kazuhiko Hasegawa 035 * @since JDK1.6, 036 */ 037public final class URLHashMap { 038 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseUnderscoresInNumericLiterals 039 private static final int SIZE = 10_000; // 6.4.3.3 (2016/03/04) 固定容量なので、増やしておきます。 040 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 041 042 /** 043 * LinkedHashMap は、反復順序を持つMapインタフェースで、データ件数を固定に設定します。 044 * ここでは、マップに新しいマッピングが追加されると、自動的に古いマッピングを削除するポリシーを 045 * 適用するために、removeEldestEntry(Map.Entry)メソッドをオーバーライドしています。 046 * 古いマッピングの定義は、コンストラクタの、順序付けモードで指定でき、アクセス順ならtrue、挿入順ならfalse です。 047 * ここでは、固定の個数分だけ、キャッシュし、あふれた古い分は、順次キャッシュから追い出されていきます。 048 * Collections.synchronizedMap で、同期処理を行います。 049 */ 050 // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseDiamondOperator 対応 051// private static final Map<String,String> URLMAP = Collections.synchronizedMap( new LinkedHashMap<String,String>( SIZE*2,0.75f,true ) { // アクセス順 052 private static final Map<String,String> URLMAP = Collections.synchronizedMap( new LinkedHashMap<>( SIZE*2,0.75f,true ) { // アクセス順 053 private static final long serialVersionUID = 643320160304L ; 054 /** 055 * このマップが一番古いエントリを削除するはずの場合にtrueを返します。 056 * 057 * @og.rev 6.4.3.3 (2016/03/04) キャッシュを、世代交代のWeakHashMapから、LinkedHashMap(固定容量) に変更します。 058 * 059 * @param eldest もっとも以前にマップに挿入されたエントリ 060 * 061 * @return もっとも古いエントリをマップから削除すべき場合はtrue。そのエントリを保持すべき場合はfalse 062 */ 063 @Override // LinkedHashMap 064 protected boolean removeEldestEntry( final Map.Entry<String,String> eldest ) { 065 return size() > SIZE; 066 } 067 } ); 068 069 /** 070 * デフォルトコンストラクターをprivateにして、 071 * オブジェクトの生成をさせないようにする。 072 * 073 * @og.rev 5.3.0.0 (2010/12/01) 新規追加 074 */ 075 private URLHashMap() { 076 // 直接インスタンスの生成を禁止 077 } 078 079 /** 080 * URL をハッシュ化/暗号化して返します。 081 * 共通処理を簡易化するための、便利メソッドです。 082 * ここでのハッシュ化/暗号化は、URLのパラメータ(?以降の部分)のみ行います。 083 * また、ここで渡す文字列は、URLを含む文字列なので、href のキーワードを 084 * 手がかりに、? を探して、その部分のみ、変換します。 085 * href を含まない場合は、それ自体が URL と判断して、? を探します。 086 * ? が存在しないケースは、url をそのまま返します。 087 * 088 * URLのアドレスが、"/" から始まる場合は、自身のアドレスと判断し、ハッシュ処理を行います。 089 * そうでない場合(http:// や ../ の場合)は、暗号化処理を行います。 090 * 第2引数は、ハッシュ化/暗号化されたパラメータを指定するキーになります。(標準は、h_r) 091 * 処理を選択します。 092 * href を含まず、'?' だけを含む場合は、'?' 以降を暗号化します。 093 * 094 * ハッシュの場合、このキーを元に、設定値を内部キャッシュ(Map)に設定します。 095 * Map は、3世代持っており、内部キャッシュ(SIZE=6000)を超えると、2世代目に移行し、 096 * さらに、超えると、3世代目に移行します。3世代目は、WeakHashMap を利用している 097 * ため、場合によってはデータがなくなり、アクセス不能になっている可能性があります。 098 * 099 * @param url オリジナルのURL、または、URLを含む文字列 100 * @param hkey パラメータのキーとして使用する文字 101 * @param extOnly 外部URL対応 [true:外部URLの場合のみ処理を実行/false:全実行] 102 * 103 * @return 変換されたURL を含む文字列 104 */ 105 public static String makeUrlChange( final String url , final String hkey , final boolean extOnly ) { 106 if( url == null || hkey == null ) { return null; } 107 // 外部のみ(extOnly=true)で、かつ、内部URLの形式の場合、引数そのままを返します。 108 if( extOnly && url.indexOf( "href=\"/" ) >= 0 ) { return url; } 109 110 String rtnUrl = url; 111 112 final int idx1 = url.indexOf( "href=\"" ); 113 final int idx2 = url.indexOf( '?', idx1 ); // href・・・ がない時(idx1 == -1)でも正常に動作する。 114 if( idx2 >= 0 ) { 115 final String url1 = url.substring( 0,idx2+1 ); // 最初から '?' まで(?を含む) 116 final int idx3 = url.indexOf( '"',idx2+1 ); // 6.0.2.5 (2014/10/31) refactoring 117 if( idx3 >= 0 ) { 118 String url2 = url.substring( idx2+1,idx3 ); // 純粋なパラメータ部分 119 final String url3 = url.substring( idx3 ); // ダブルクオート(含む)以降 120 // href="/ かどうか。文字列の有効長があるかどうかを先にチェックしている。 121 if( idx1 >= 0 && url.length() > idx1+7 && url.charAt( idx1+7 ) == '/' ) { 122 url2 = makeHashKey( url2 ); 123 } 124 else { 125 url2 = makeEncrypt( url2 ); 126 } 127 rtnUrl = url1 + hkey + "=" + url2 + url3 ; 128 } 129 // '?' はあるが、最後のダブルクオートがない場合は、'?' から後ろすべてを暗号化する。 130 else { 131 final String url2 = url.substring( idx2+1 ); // ?以降の部分 132 rtnUrl = url1 + hkey + "=" + makeEncrypt( url2 ) ; 133 } 134 } 135 136 return rtnUrl; 137 } 138 139 /** 140 * 引数の設定値をハッシュ化したときの値(=ハッシュキー)を作成します。 141 * 内部的に、このハッシュキーを元に、設定値を内部キャッシュ(Map)に設定します。 142 * 143 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 144 * @og.rev 6.4.3.3 (2016/03/04) キャッシュを、世代交代のWeakHashMapから、LinkedHashMap(固定容量) に変更します。 145 * @og.rev 8.1.2.0 (2022/03/10) getMD5 メソッドを getHash メソッドに変更 146 * 147 * @param val オリジナルの設定値 148 * 149 * @return ハッシュキー 150 * @og.rtnNotNull 151 */ 152 public static String makeHashKey( final String val ) { 153 if( val == null ) { return ""; } 154 155 // 必殺技:最後に "X" を付けることで、ハッシュ処理されたことを示す。 156// final String key = HybsCryptography.getMD5( val ) + "X"; // 8.1.2.0 (2022/03/10) Modify 157 final String key = HybsCryptography.getHash( "MD5", val ) + "X"; 158 URLMAP.put( key,val ); // 6.4.3.3 (2016/03/04) 159 return key; 160 } 161 162 /** 163 * 引数の設定値を暗号化したときの値を作成します。 164 * 暗号化なので、ハッシュ化の時の用に、内部キャッシュ(Map)に設定しません。 165 * 処理時間等を考慮すると、外部URLの暗号化や、長期にわたるURL(一旦、システムの 166 * 外部にURLを渡す:例えば、メールでリンク先を送信するなど)の作成に使用します。 167 * 168 * @param val オリジナルの設定値 169 * 170 * @return 暗号化キー 171 * @og.rtnNotNull 172 */ 173 public static String makeEncrypt( final String val ) { 174 if( val == null ) { return ""; } 175 176 // 必殺技:最後に "Y" を付けることで、暗号化処理されたことを示す。 177 final HybsCryptography crypt = new HybsCryptography(); 178 return crypt.encrypt( val ) + "Y"; 179 } 180 181 /** 182 * 指定のキーに対応した設定値を取り出します。 183 * ここでは、ハッシュ化 または 暗号化されているキーに対して、 184 * ハッシュキーであれば、関連付けられた設定値を取り出し、暗号化キーで 185 * あれば、復号化処理を行います。 186 * 187 * @og.rev 6.4.1.1 (2016/01/16) URLMAP2 を、static finalにするため、代入せずに、clear() + putAll(Map) で処理します。 188 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 189 * @og.rev 6.4.3.3 (2016/03/04) キャッシュを、世代交代のWeakHashMapから、LinkedHashMap(固定容量) に変更します。 190 * 191 * @param key ハッシュ化/暗号化されたキー 192 * 193 * @return オリジナルの設定値 194 */ 195 public static String getValue( final String key ) { 196 if( key == null || key.isEmpty() ) { return null; } 197 198 String rtn = null; 199 200 final char ch = key.charAt( key.length()-1 ); 201 if( ch == 'X' ) { 202 rtn = URLMAP.get( key ); 203 } 204 else if( ch == 'Y' ) { 205 final HybsCryptography crypt = new HybsCryptography(); 206 rtn = crypt.decrypt( key.substring( 0,key.length()-1 ) ); 207 } 208 209 return rtn ; 210 } 211}