Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
57
Help us understand the problem. What is going on with this article?

More than 5 years have passed since last update.

@kawasima

外部サイトへの遷移させるとき注意すべき1つのこと

SNSなどプライベートな記事をユーザが書けるページから、外部サイトへリンクする場合、リファラーによって秘密のURLが漏洩しないように、リダイレクト専用のアプリを用意することがあります。
また、自サイトからの外部サイトへの遷移を把握するためにも、そういうリダイレクト専用アプリを用意してログを落とすようにすることがあります。

これは、特に何もしないと格好のフィッシング詐欺の踏み台になります。

みたいなリンクを、どこかの掲示板などに貼られると、「おっ、フェイスブックのリンクじゃん」
ってことで、踏んでしまう人がいるからです。

対策です。

:skull: リファラチェック

HTTP_REFERERが自サイト以外だったら、エラーにする設計です。あまりないとは思いますが、こういう設計をされているサイトを見かけたことがあります。

当然ながらRefererはクライアントの設定で送らないように容易にできます。(auのガラケーブラウザはRefererを送らないってことも、昔はありました。)

そういう、ふつうのユーザが、外部サイトへの遷移ができなくなってしまうので、この設計はやめましょう。

:confused: ホワイトリスト

ある程度外部リンクをコントロールできる場合は、遷移可能なサイトへのURLをリスト化しておき、チェックする方式をとることがあります。

たいていリダイレクト専用のアプリケーションなんて汎用的なものは、時間が経つと、当初と違う用途で使われたりするようになります。そうすると、最初は大したことないから、と始めたホワイトリストのメンテナンスがエラい大変になってきた、なんてことになります。(みたいなことが以前ありました)

なので、そういう恐れがあれば、これもやめたほうが良いです。

:grinning: ハッシュコード

これがふつうの方式です。ロケーションURLを元にしたHMACを付けます。これがない、またはHMAC値が一致しない場合は、エラーにするか、"外部サイトに移動しようとしてますがよろしいですか?"みたいなページを挟むようにします。

image

Facebookだとこんな感じです。

Javaでの実装

HMACの鍵でURLを作ります。

    public String redirectUrl(String url) throws UnsupportedEncodingException {
        return "/redirect?u=" +  URLEncoder.encode(url, "UTF-8")
                + "&h="+ HmacUtils.hmacSha256Hex(hmacKey, url);
    }

そうすると、リダイレクト用のURLを作ることができます。

/redirect?u=http%3A%2F%2Fwww.yahoo.co.jp%2F&h=55fba8d6d44d8f2bd553cd76e018a06318860d5b748a4995292602ad615422de

リダイレクト用のアプリケーションは、それを突き合わせる以下のような感じになります。

public class RedirectServlet extends HttpServlet{
    private String key;
    private String confirmPath;

    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        key = config.getInitParameter("key");
        if (key == null) {
            key = UUID.randomUUID().toString();
        }
        confirmPath = config.getInitParameter("confirmPath");
    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        URL url;

        String userHash = request.getParameter("h");
        try {
            String u = request.getParameter("u");
            url = new URL(u);
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        try {
            String digestHex = HmacUtils.hmacSha256Hex(key, url.toString());
            if (userHash != null && userHash.equals(digestHex)) {
                response.sendRedirect(url.toString());
                return;
            } else if (confirmPath != null) {
                RequestDispatcher dispatcher = request.getRequestDispatcher(confirmPath);
                dispatcher.forward(request, response);
            } else {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
            }
        } catch (Exception e) {
            log("something wrong", e);
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
    }

}

これで、Facebookのl.phpと同じような機能を作ることができます。

まとめ

HMACによる改ざん検知は、Javaでは簡単に使えて、いろいろ役に立ちます1

57
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
57
Help us understand the problem. What is going on with this article?