質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.01%

ユーザーがPOSTしたURLリンクを安全に表示する方法がわかりません。

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 3
  • VIEW 582

yayak

score 40

ユーザーが掲示板などにURLを投稿した際、URLに使用できない文字を削除するなどして、安全な状態に整形してからWEB上に表示したいと考えております。

===追記===

ユーザーが投稿したURLは、リンクするために使用します。

========

以前はWordpressを使っていたので、Wordpressのesc_url()という関数を使用していました。

esc_url()という関数にURLの文字列を通すと、URLに使用できない文字などを一括で削除してくれて非常に便利だったのですが、今回はWordpressを使用せずに生phpでサイトを作ろうと思っているため、esc_url()関数を使用することができません。

POSTされてきたURLを安全にWEB上に表示するため、一般的にどのような方法が使われているのかわからず、この度質問してみました。

初歩的な質問で大変恥ずかしいのですが、ご存知の方がいらっしゃいましたら、ご教授いただけましたら幸いです。

何卒、宜しくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2019/10/17 16:37

    タイトルと目的が合ってないように見受けられますのでタイトルを調整されたほうが良いかと思います

    キャンセル

  • yayak

    2019/10/17 16:40

    アドバイスいただき有難うございます。
    修正いたしました><

    キャンセル

回答 4

checkベストアンサー

+4

単に表示するだけであればhtmlspecialcharsでHTMLエスケープすればよいですが、追記で示されたようにA要素等を用いてリンクの形にする場合は、スキーム(プロトコル)の確認は必須です。
通常は、前方一致で、http: か https: で始まっていることを確認すればよいと思います。esc_urlはtelnetやgopherなどもデフォルトで許容していますが、通常これらは不要かと思います。
スキームのチェックをしていないと、以下のようなリンクが作られてしまい、クロスサイトスクリプティング脆弱性になります。

<a href="javascript:alert(1)">外部へのリンク</a>

上記の例では、リンクを選択するとJavaScriptとしてalert()関数が実行します。

したがって手順としては下記の通りとなります。

  • まずURLが http: または https: で始まっていることを確認する
  • 前記検査がOKならば、URL全体をhtmlspecialcharsでエスケープする
  • URLをhrefなどの属性値として表示する場合はダブルクォートで囲むことを忘れないこと

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/18 11:48 編集

    まさか、ockeghem様にご回答いただけるとは思っておらず、本当に感謝しかございません。
    ものすごく勉強になりました。実践してみようと思います。
    有難うございます。心より感謝申し上げます。

    キャンセル

+4

WordPress のディレクトリ内で、

grep -r "function esc_url(" ./

で検索すると、定義部分が見つかります。
同じように実装すればよろしいのではないかと思います。

/**
 * Checks and cleans a URL.
 *
 * A number of characters are removed from the URL. If the URL is for displaying
 * (the default behaviour) ampersands are also replaced. The {@see 'clean_url'} filter
 * is applied to the returned cleaned URL.
 *
 * @since 2.8.0
 *
 * @param string $url       The URL to be cleaned.
 * @param array  $protocols Optional. An array of acceptable protocols.
 *                          Defaults to return value of wp_allowed_protocols()
 * @param string $_context  Private. Use esc_url_raw() for database usage.
 * @return string The cleaned $url after the {@see 'clean_url'} filter is applied.
 */
function esc_url( $url, $protocols = null, $_context = 'display' ) {
    $original_url = $url;

    if ( '' == $url ) {
        return $url;
    }

    $url = str_replace( ' ', '%20', $url );
    $url = preg_replace( '|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url );

    if ( '' === $url ) {
        return $url;
    }

    if ( 0 !== stripos( $url, 'mailto:' ) ) {
        $strip = array( '%0d', '%0a', '%0D', '%0A' );
        $url   = _deep_replace( $strip, $url );
    }

    $url = str_replace( ';//', '://', $url );
    /* If the URL doesn't appear to contain a scheme, we
     * presume it needs http:// prepended (unless a relative
     * link starting with /, # or ? or a php file).
     */
    if ( strpos( $url, ':' ) === false && ! in_array( $url[0], array( '/', '#', '?' ) ) &&
        ! preg_match( '/^[a-z0-9-]+?\.php/i', $url ) ) {
        $url = 'http://' . $url;
    }

    // Replace ampersands and single quotes only when displaying.
    if ( 'display' == $_context ) {
        $url = wp_kses_normalize_entities( $url );
        $url = str_replace( '&amp;', '&#038;', $url );
        $url = str_replace( "'", '&#039;', $url );
    }

    if ( ( false !== strpos( $url, '[' ) ) || ( false !== strpos( $url, ']' ) ) ) {

        $parsed = wp_parse_url( $url );
        $front  = '';

        if ( isset( $parsed['scheme'] ) ) {
            $front .= $parsed['scheme'] . '://';
        } elseif ( '/' === $url[0] ) {
            $front .= '//';
        }

        if ( isset( $parsed['user'] ) ) {
            $front .= $parsed['user'];
        }

        if ( isset( $parsed['pass'] ) ) {
            $front .= ':' . $parsed['pass'];
        }

        if ( isset( $parsed['user'] ) || isset( $parsed['pass'] ) ) {
            $front .= '@';
        }

        if ( isset( $parsed['host'] ) ) {
            $front .= $parsed['host'];
        }

        if ( isset( $parsed['port'] ) ) {
            $front .= ':' . $parsed['port'];
        }

        $end_dirty = str_replace( $front, '', $url );
        $end_clean = str_replace( array( '[', ']' ), array( '%5B', '%5D' ), $end_dirty );
        $url       = str_replace( $end_dirty, $end_clean, $url );

    }

    if ( '/' === $url[0] ) {
        $good_protocol_url = $url;
    } else {
        if ( ! is_array( $protocols ) ) {
            $protocols = wp_allowed_protocols();
        }
        $good_protocol_url = wp_kses_bad_protocol( $url, $protocols );
        if ( strtolower( $good_protocol_url ) != strtolower( $url ) ) {
            return '';
        }
    }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/17 16:02

    有難うございます!
    おっしゃていることを以前にやろうとして、wp_allowed_protocols()など、esc_url()の関数の中身にたくさんのWordpress独自の関数がでてきているため、断念してしまった経緯がございます。
    皆様は、どのような方法でURLを安全に表示しているのかと思い質問させていただいたのですが、URLのエスケープは難しいのですね><

    キャンセル

  • 2019/10/17 16:10

    それが面倒なら、wordpress から wp_includes/format.php をとり出してrequireしちゃえばいいんじゃないですか?

    キャンセル

  • 2019/10/17 16:42

    有難うございます!
    format.phpが、その他のファイルの関数もたくさん使用しており、Wordpressの関数をまとめたファイルの大半を取り込むことになりそうなので、もう少し頑張ってみて、最終手段としてやってみようと思います><

    キャンセル

+3

補足的なところを別途回答します。

基本的にユーザーの入力情報は加工してはならず、表示時は登録した通りに出すべきです。
なので対策をするのであれば「表示時に削除する」のではなく「登録時にバリデーションをする」にとどめてください。
URLの形式として正しいかのチェックを行えば「使えない文字列」が登録されるのを防ぐことができますし、パラメータ(クエリストリング)を不可としたチェックをすれば、パラメータ付与により何かしらの処理を実行させないようにすることも可能です。

XSS対策でHTMLエスケープはすべきですが、画面上は登録した通りに表示させられるので可です。

ということで、おそらくシステム的にできるのはここまでと考えられます。

あとは「安全である」基準ですね。サイトとしてどこまでを許容するのか。ここはブラックリスト方式になってしまうかもしれませんが…。
それなら「URLとして正しい」のみで受け入れても良いかと思います。あとはパラメータ付加を許容するかどうかですね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/17 16:54

    mts先生、いつも本当に有難うございます。
    自身のできなさ加減に失望しそうなとき、mts先生の回答は参考になるだけなくて、頑張る力をくれます。
    本当に、有難うございます。

    キャンセル

  • 2019/10/17 16:59

    参考になったようで何よりです。そのように評価してもらえるとアドバイスした甲斐があったと嬉しくなりますね。
    誰だって始めは何も分からないとこらからきています。私の初学者のときと比べればアドバイスを得られる機会も人も多いので、それだけ成長に繋がりやすいと言えます。
    考え方を学ぼうとする姿勢があるので、成長は見込めると思っています。過信はよくないですが失望するのもよくありません。

    ※細かいですがユーザー名は要約しないほうが無用なトラブルを避けられます

    キャンセル

  • 2019/10/18 11:45

    m.ts10806先生、かしこまりました。
    本当に、有難うございます。
    もっともっと挑戦と勉強をします!

    キャンセル

+2

POSTされてきたURLを安全にWEB上に表示するため、一般的にどのような方法が使われているのかわからず

単に文字列として表示するだけなら、どんな文字列を「URL」に混ぜようと、表示したこと自体でなにか起きることはありません。

逆に、リンク先の安全性は、チェックすることはほぼできないと考えて間違いありません(著名なサイトの広告に、「あなたのパソコンは危険にさらされています」系の広告が混入していたことがありました)。

つまり、一般的な対応としては、

  • URL自体をNGワードにして完全排除
  • URLを単なる文字列として表示するだけ

のどちらかで十分です。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/17 15:56 編集

    質問の仕方がよくなかったです、すみません><
    ユーザーがプロフィールを登録できるサイトなのですが、そこにユーザーが自身のサイトのURLを登録できるようにしたいのですが、万一悪意のあるユーザーが悪意のあるコードが紛れたURLを登録しても、それを無効化できるような方法を探しておりました。
    現状、それは難しいということなのですね。。

    キャンセル

  • 2019/10/17 15:58

    「悪意のある」の意味にもよりますが、飛び先が(ユーザーの設定どおりだけれど)不適切なものが置かれているページになるのを、コードだけで防ぐことは現実的に無理です。

    キャンセル

  • 2019/10/17 16:36 編集

    結局は一般的な対応と同じで、規約への明示と監視対応ですね。
    URLとして正しい以上は一度受け入れるしかありません(できてもそのURLの先が存在するかどうかくらい。その先の情報を解析となると別のルールに抵触する可能性もないわけではないのです)

    キャンセル

  • 2019/10/17 16:42

    なるほどです。。地道に頑張ってみます!有難うございます<(_ _)>

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.01%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる