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}