クリップボードにコピーさせないimgタグの作り方

2 min read読了の目安(約1900字

このツイートをみて、imgタグで同じようなことしたいなと思って調べて実装してみたら思いの外ヘビーだったので知見を共有します。

目的

ナビゲーション用のイメージと実際のコンテント画像を区別し、アクセシビリティを変えて不正なダウンロードや引用を防ぐ。

画像へのリンクを貼っていなくても長押しや右クリックで画像のURLが漏れる件をフォロー。
ここまですると、ソースコードをみない限りはURLはわからなくなる(はず)[1]

なお、PCやソースの読めるモバイルアプリ(ってあるよね?)を使われたら一発で突破されるので、プライベートな画像を表示する際には表示前にユーザー認証を挟んでる前提です。

実装例

HTML

ざっくりとこういう感じで並んでいたとして。

profile.html
<p><img src="/private/..." alt="プロフィール画像" /></p>   <!-- URLを漏らしたくない -->
<p>     <!-- 一方、こちらはタップできないと困る -->
    <a href="..."><img src="/public/..." alt="ナビゲーション用画像" /></a>
</p>

CSS

untappable.css
/* まずは全ての画像タグに対して不便を強いる */
IMG {
    user-select: none; /* 範囲指定で選べなくなる */
    -webkit-user-select: none;
    pointer-events: none; /* クリックしても反応しなくなる */
}

/* クリックしてもらう前提での画像は設定を元に戻す */
A IMG {
    user-select: auto;
    -webkit-user-select: auto;
    pointer-events: auto;
} /* この例は乱暴。特定のclassは戻すとかにした方が良いです。 */

JavaScript (今回はjQuery)

ctrlImages.js
$(function () { // 画像の操作を可能な限り禁止する
    $('img').each(function (i, element) {
        let src = $(element).attr('src'); // srcの場所を取得
        if (/^\/public/.test(src)) { // '/public/....'だったら別にいいですやん?
            return true; // each内で next するにはこう
        }
        $(element).on('contextmenu', function (event) { // 右クリック禁止
            event.preventDefault();
            return false;
        });
        $(element).on('click', function (event) { // クリック禁止
            event.preventDefault();
            return false;
        });
        $(element).on('copy', function (event) { // コピー禁止
            event.preventDefault();
            return false;
        });
    });
});

終わりに

プライベートな画像を慎重に扱うためにBlowfish暗号を利用したリクエストができるPerlコード書いたけど、そっちの方は需要ありますか?
/privateImg?key=U2FsdGVkX1_Vsn9SPVlkYXiiGX7tef9rb5XTLvZGHEB5UvbkPMY3j
のようなURLで画像を提供して、これをサーバーが復号するのは簡単だけど、悪意のある第三者にはキーの生成が難しい、という理屈です。

あ、やっぱりPerlはいいか。

脚注
  1. 突破できる方は方法を指摘してください。 ↩︎