CuteKeWebサイトの開発とセキュリティ5-実際のIPアドレスと苦痛な経験

    この章では、javaを使用して実際のIPアドレスと関連するセキュリティを取得する方法について説明し、次にMongoDBの不適切な構成によって引き起こされるセキュリティの問題について説明します。

1.実際のIPアドレスを取得します

1.1IPアドレスを取得するタイミング

    私のアプローチは、URLを取得するときと同じようにController実行の前に要求されたIPアドレスを取得することです。同様に、各要求に対応するものを取得しHttpServletRequestます。コードは次のとおりです。

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

org.springframework.web.context.request.ServletRequestAttributesソースコードはパッケージを使用してThreadLocal各リクエストのHttpServletRequestオブジェクト保存します。後で空いた場合は詳細に説明できます

1.2getRemoteAddr与x-forwared-for

HttpServletRequestオブジェクトを    取得した後、getRemoteAddr()内部の関数を呼び出してリモートクライアントのIPを取得できますが、同時にhttpプロキシのシナリオでは、プロキシの後、クライアントとサービスの間に中間層が追加されているため、サーバーオブジェクトを直接取得できません。クライアントのIPに対して、サーバー側のアプリケーションは、要求されたアドレスを転送してクライアントに直接戻ることはできません。ただし、転送要求のHTTPヘッダー情報には、情報が追加されX-FORWARDED-FORます。元のクライアントのIPアドレスと元のクライアントによって要求されたサーバーアドレスを追跡するために使用されます。

RFC2616によると、HTTPメソッドは大文字と小文字を区別しますが、ヘッダーはそうではないことに注意してください。URIのプロトコル名とドメイン名は大文字と小文字を区別せず、パスはサーバーによって異なります。

    したがって、我々は最初に決定することができるHttpServletRequesthttp header対応するフィールドがあれば、そこにあるか否かをそのx-forwared-for値、および複数のIP値は文字列であり、その後、採取x-forwared-for以外に最初unknownの有効なIP列コードとして以下れます。

public static String getClinetIpByReq(HttpServletRequest request) {
        // 获取客户端ip地址
        String clientIp = request.getHeader("x-forwarded-for");

        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getHeader("WL-Proxy-Client-IP");
        }
        if (clientIp == null || clientIp.length() == 0 || "unknown".equalsIgnoreCase(clientIp)) {
            clientIp = request.getRemoteAddr();
        }
        /*
         * 对于获取到多ip的情况下,找到公网ip.
         */
        String sIP = null;
        if (clientIp != null && !clientIp.contains("unknown") && clientIp.indexOf(",") > 0) {
            String[] ipsz = clientIp.split(",");
            for (String anIpsz : ipsz) {
                if (!isInnerIP(anIpsz.trim())) {
                    sIP = anIpsz.trim();
                    break;
                }
            }
            /*
             * 如果多ip都是内网ip,则取第一个ip.
             */
            if (null == sIP) {
                sIP = ipsz[0].trim();
            }
            clientIp = sIP;
        }
        if (clientIp != null && clientIp.contains("unknown")){
            clientIp =clientIp.replaceAll("unknown,", "");
            clientIp = clientIp.trim();
        }
        if ("".equals(clientIp) || null == clientIp){
            clientIp = "127.0.0.1";
        }
        return clientIp;
    }
 public static boolean isInnerIP(String ipAddress) {
        boolean isInnerIp;
        long ipNum = getIpNum(ipAddress);
        /**
         私有IP:A类  10.0.0.0-10.255.255.255   
         B类  172.16.0.0-172.31.255.255   
         C类  192.168.0.0-192.168.255.255   
         当然,还有127这个网段是环回地址   
         **/
        long aBegin = getIpNum("10.0.0.0");
        long aEnd = getIpNum("10.255.255.255");

        long bBegin = getIpNum("172.16.0.0");
        long bEnd = getIpNum("172.31.255.255");

        long cBegin = getIpNum("192.168.0.0");
        long cEnd = getIpNum("192.168.255.255");
        isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd)
                || ipAddress.equals("127.0.0.1");
        return isInnerIp;
    }

    private static long getIpNum(String ipAddress) {
        String[] ip = ipAddress.split("\\.");
        long a = Integer.parseInt(ip[0]);
        long b = Integer.parseInt(ip[1]);
        long c = Integer.parseInt(ip[2]);
        long d = Integer.parseInt(ip[3]);

        return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d;
    }

    private static boolean isInner(long userIp, long begin, long end) {
        return (userIp >= begin) && (userIp <= end);
    }

もちろん、すべてのプロキシサーバーがx-forwared-for各ホップの値のみを記録するわけではなく、別のヘッダーを使用する場合もあれば、まったく記録しない場合もあります。

1.3IPアドレスに基づいて実際のアドレスを取得する

    場合によっては、IPアドレスに基づいて対応する地理的位置を照会することもあります。このとき、Baiduのインターフェイスを参照できます。コードは次のとおりです。

 public static String getAddressByIP(String strIP) {
        if(strIP.equals("127.0.0.1")||strIP.equals("0:0:0:0:0:0:0:1"))
            return "本地局域网";
        try {
            URL url = new URL("http://api.map.baidu.com/location/ip?ak=F454f8a5efe5e577997931cc01de3974&ip="+strIP);
            URLConnection conn = url.openConnection();
            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line = null;
            StringBuffer result = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            reader.close();
            String ipAddr = result.toString();
            try {
                JSONObject obj1= new JSONObject(ipAddr);
                if("0".equals(obj1.get("status").toString())){
                    JSONObject obj2= new JSONObject(obj1.get("content").toString());
                    JSONObject obj3= new JSONObject(obj2.get("address_detail").toString());
                    return obj3.get("province").toString()+ obj3.get("city").toString()+obj3.get("district").toString()+obj3.get("street").toString();
                }else{
                    return "国外";
                }
            } catch (JSONException e) {
                e.printStackTrace();
                return "读取失败";
            }

        } catch (IOException e) {
            return "读取失败";
        } 
    }

1.4x-forwared-練習用

    実際のIPアドレスを認識できるかどうかをローカルで確認してみましょう。Webサイトのローカルアドレスは、引き続きhttp://localhostマシンのローカルIPアドレスです。10.59.13.225次のように構成します。

  • 10.59.13.240次のように構成プロキシサーバ、nginx.conf断面プロファイルを逆nginxの:
server
      {
        listen          80;
        server_name     10.59.13.240;   
        location / {
                proxy_pass              http://10.59.13.225;    
                #proxy_rediect          off;
                proxy_set_header        X-Real-IP       $remote_addr;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
         }
      }
  • 10.59.13.221ホストアクセスhttp://10.59.13.240、結果は図1に示されています。

realIp-1

図1.アクセスIPアドレス
  • 同時に、localhost図2に示すように、データベースに記録たIPアドレス確認します。

realIp-2

図2.データベースストレージのIPアドレス

10.59.13.221リバースプロキシサーバーのIPアドレスの代わりに、    サーバーが最初送信された要求を記録できることがわかります。10.59.13.240

1.5x-forwared-for安全

    x-forwared-for偽造される可能性があります。アプリケーションのシナリオは通常、IPスワイプです。たとえば、1つのIPは1日に1つの投票にしか投票できず、Cookieの検証、検証コードの検証などのテクノロジーは使用されないため、対応する攻撃を実行できます。実際のIPアドレスを取得するように設計する一方x-forwared-forで、偽造を防ぐために、Cookie検証、検証コード検証などの他のテクノロジーも使用する必要があります

2.苦い経験

    私のCuteKeWebサイトアーキテクチャをまだ覚えていますか?Tencent CloudにデプロイされたサービスはすべてMongoDbを使用してファイルデータを記録しますが、MongoDbの構成が正しくないとセキュリティの問題が発生する可能性があります。

2.1まだデフォルト設定を使用していますか?

    MongoDbのデフォルト構成(何も変更しない場合)は安全ではありません。CuteKeWebサイトのファイルサーバーは、いくつかの自動ツールによってスキャンされることが多く、MongoDbもスキャンされて盗まれます。ハッカーは新しく作成された警告データベースには、このデータベースの下にReadmeテーブルがあります。スクリーンショットは次のとおりです。

MongoDbが攻撃されました

図3.MongoDbが攻撃された

トゥカオは0.2ビットコインを送ろうとしています、ライオンは口を開けました!しかし、それは私自身の過失だったので、MySQLの構成をすぐに変更しました

2.2MongoDbを安全に構成する

    この経験の後、セキュリティ構成の重要性に気づきました。MongoDbを安全に構成するために段階的に見ていきましょう。

  1. 外部IPを監視しないでください
  2. デフォルトのポート27017度には適用されません
  3. 認証モードをオンにする

    mongod.cfgファイルの内容は次のとおりです。

systemLog:
  destination: file
  path: e:\mongoDate\log\mongod.log
storage:
  dbPath: d:\mongoDate\db
net:
   bindIp: 127.0.0.1
   port: 27001
security:
  authorization: enabled

    同時に、ファイルサーバーapplication.properties構成のuriも変更されます。

spring.data.mongodb.uri=mongodb://use1:123456@localhost:27001/test

ユーザー追加の具体的なプロセスについては、リファレンス5を参照してください。同時に、データの損失がまだ心配な場合は、mongodbデータを毎日バックアップすることを忘れないでください。

3.参考文献

[1] Javaはクライアントユーザーの実際のIPアドレスを取得します
[2] javaのIPアドレスはそれが属するエリアを決定します(baiduインターフェイス)
[3] WEBセキュリティ-偽造X-転送-サーバーのIPアドレスフィルタリングをバイパスするため
[4]なぜ自己構築mongodb突然データが失われました
[5] mongodbは外部ネットワークへのアクセスとアカウントの追加を禁止しています

おすすめ

転載: blog.csdn.net/u012397189/article/details/80559066