HTTPとHTTPSを併用するサイトでの安全なセッション管理


12/12 22:40 こっちが正解?自信なし・・・
考慮すべきは、

  1. HTTPのページにアクセスするとセッション情報が奪われてしまう可能性がある
  2. HTTPのページにアクセスするとセッション情報が操作されてしまう可能性がある
  3. 2重ログインでセッション情報が奪われてしまう可能性がある

の3点か?訳わかんなくなってきたので、また今度まとめなおし。。

HTTPページへのアクセス時

     ・・・
        HttpSession session = request.getSession();
        if (session.getAtribute("AuthTicket") != null) {
            Enumeration e = session.getAttributeNames();
            Map buff = new HashMap();
            while (e.hasMoreElements()) {
                String key = (String) e.nextElement();
                buff.put(key, session.getAttribute(key));
            }
            session.invalidate();
            session = request.getSession(true);
            Iterator ite = buff.keySet().iterator();
            while (ite.hasNext()) {
                String key = (String) ite.next();
                session.setAttribute(key, buff.get(key));
            }
        }
    ・・・

ログイン処理

     ・・・
        if (checkAccount(userId, password)) {
            HttpSession session = request.getSession();
            Enumeration e = session.getAttributeNames();
            Map buff = new HashMap();
            while (e.hasMoreElements()) {
                String key = (String) e.nextElement();
                buff.put(key, session.getAttribute(key));
            }
            session.invalidate();
            session = request.getSession(true);
            Iterator ite = buff.keySet().iterator();
            while (ite.hasNext()) {
                String key = (String) ite.next();
                session.setAttribute(key, buff.get(key));
            }
            session = request.getSession(true);
            String authTicket = generateAuthTicket();
            session.setAttribute("AuthTicket", authTicket);
            Cookie authCookie = new Cookie("AuthCookie", authTicket);
            authCookie.setSecure(true);
            authCookie.setPath(request.getContextPath());
            response.addCookie(authCookie);
            //認証成功
        } else {
            //認証失敗
        }
    ・・・
  }

    private boolean checkAccount(String userId, String password) {
        boolean result = アカウントチェック;
        return result;
    }

    private String generateAuthTicket() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        byte[] b = new byte[32];
        random.nextBytes(b);
        return toHexString(b);
    }

    private String toHexString(byte bytes[]) {
        StringBuffer sb = new StringBuffer(bytes.length * 2);
        for (int index = 0; index < bytes.length; index++) {
            int i = bytes[index] & 0xff;
    	   if (i < 0x10) {
                sb.append("0");
            }
            sb.append(Long.toString(i, 16));
        }
        return sb.toString();
    }

HTTPSページへのアクセス時(ログイン後)

     ・・・
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            Cookie authCookie = null;
            String authTicket= null;
            for (int i = 0; i < cookies.length; i++) {
                if (cookies[i].getName().equals("AuthCookie")) {
                    authCookie = cookies[i];
                    authTicket = authCookie.getValue();
                }
            }
            HttpSession session = request.getSession();
            if (authTicket != null && authTicket.equals(session.getAttribute("AuthTicket"))) {
                //認証成功
            }
        }
        //認証失敗
     ・・・



12/12 20:40 ん、、、これじゃ駄目かも。もうちょい考えないと・・・

HTTPとHTTPSを併用する必要があるサイトでのセッション管理手法について、
SSLの関係で2つのセッションIDを使い分ける場合について|freeml byGMO
この辺りの議論とかコードとかを参考にさせて頂きながら考えたことをコードに落としてみました。
前提条件として、ログイン後の画面遷移はHTTPSしか許可しないことにしています。ここを許可しちゃうと今のところ安全な実装方法が分からないので。
#12/12 9:30 ちょっとコード修正

ログイン前までの遷移

    普通にセッション変数を使用したりして実装

ログイン処理

     ・・・
        if (checkAccount(userId, password)) {
            HttpSession session = request.getSession();
            Enumeration e = session.getAttributeNames();
            Map buff = new HashMap();
            while (e.hasMoreElements()) {
                String key = (String) e.nextElement();
                buff.put(key, session.getAttribute(key));
            }
            session.invalidate();
            session = request.getSession(true);
            Iterator ite = buff.keySet().iterator();
            while (ite.hasNext()) {
                String key = (String) ite.next();
                session.setAttribute(key, buff.get(key));
            }
            session = request.getSession(true);
            String authTicket = generateAuthTicket();
            session.setAttribute("AuthTicket", authTicket);
            Cookie authCookie = new Cookie("AuthCookie", authTicket);
            authCookie.setSecure(true);
            authCookie.setPath(request.getContextPath());
            response.addCookie(authCookie);
            //認証成功
        } else {
            //認証失敗
        }
    ・・・
  }

    private boolean checkAccount(String userId, String password) {
        boolean result = アカウントチェック;
        return result;
    }

    private String generateAuthTicket() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        byte[] b = new byte[32];
        random.nextBytes(b);
        return toHexString(b);
    }

    private String toHexString(byte bytes[]) {
        StringBuffer sb = new StringBuffer(bytes.length * 2);
        for (int index = 0; index < bytes.length; index++) {
            int i = bytes[index] & 0xff;
    	   if (i < 0x10) {
                sb.append("0");
            }
            sb.append(Long.toString(i, 16));
        }
        return sb.toString();
    }

ログイン後の遷移

     ・・・
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            Cookie authCookie = null;
            String authTicket= null;
            for (int i = 0; i < cookies.length; i++) {
                if (cookies[i].getName().equals("AuthCookie")) {
                    authCookie = cookies[i];
                    authTicket = authCookie.getValue();
                }
            }
            HttpSession session = request.getSession();
            if (authTicket != null && authTicket.equals(session.getAttribute("AuthTicket"))) {
                //認証成功
            }
        }
        //認証失敗
     ・・・

副作用もないと思うので、業務でも使えるコードになっているはず・・・
結構長いコードになっちゃいますが、ここまでしないと安全なセッション管理は無理かなーと思っています。