20200104のHTMLに関する記事は9件です。

herokuデプロイ 背景画像表示させる

記事投稿の理由

初めてherokuでデプロイした際に画像が表示されなくて苦しんだので
復習兼ねて記事書かせていただきます。
環境によって違う場合もある。参考程度にみてくださると幸いです。

今回のテーマと環境

rails 5.0.7.2

herokuでデプロイはできていて
画像のみが表示されないと仮定します。

変更箇所

assetファイルは事前にコンパイルしておきます。
しかしassetファイルが見つから無い場合にtrueにしておくとサーバー側でコンパイルしてくれるそうです。
ただし、コンパイルによりサーバー側の負荷も上がるのでここは環境によって変わると思います。

今回私の場合はconfig.assets.compile = falseのところをtrueに変更して対応しました。

production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
  config.assets.compile = true

config.assets.css_compressor = :sassのコメントアウトを外して
私の場合scssファイルで色付けなど行なっていたため
config.assets.css_compressor = :scssに変更

production.rb
# Compress JavaScripts and CSS.
config.assets.js_compressor = :uglifier
config.assets.css_compressor = :scss

背景画の設定しているところを
background-image: url("china.jpg");から下記に変更

index.scss
.contact{
  background-image: image-url("china.jpg");

asset_pathの追加
<img src="freemarket.png" alt="制作事例1" width=500px height=280px>を下記に変更

index.html.erb
<li class="portfolio">
        <a href="http://18.177.66.99/" class="portfolio-image">
          <img src="<%= asset_path "freemarket.png" %>" alt="制作事例1" width=500px height=280px >
        </a>
      </li>

あとherokuにファイル送付する前に開発環境のコンソールにて下記忘れないように。。

$ rake assets:precompile RAILS_ENV=production

最後に

僕はこのような方法でデプロイした際にうまく画像が表示されるようになりました。
環境によっても違うと思いますが参考になれば・・・・

参考にさしていただいたサイト

Rails4 asset pipeline関連設定まとめ
Rails4ではbackground:url("assets/hoge.png")の書き方は動かない話
Rails初学者がつまずきやすい「アセットパイプライン」
HerokuにRubyのデータをデプロイ後、画像が見れない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gulp v4 による Pug × SCSS × TypeScript テンプレ

はじめに

  • gulp v4 がだいぶ前にリリースされたけど、当方ずっとgulp v3のままだった
  • gulp v4 は新しい機能が増えたり書き方が変わったりとすごい変わってしまわれてる
  • 今まで自分の作ったものが化石になるのを恐れたのでgulp v4に適用させた
  • ついでにテンプレ作って今後楽しようと思った
  • どんどん改善していきたいからみんなに使ってもらいたい、ここで宣伝だ

概要

この記事の概要は以下になります?

gulp

gulp公式サイト
https://gulpjs.com/

  • JavaScript で書かれたビルドシステム
  • Node.js をベースとしている
  • SCSS のコンパイル、Pug のコンパイル、ファイルの圧縮など、様々な作業を自動化してくれる

pug

pug公式サイト
https://pugjs.org/api/getting-started.html

  • HTML を書くためのテンプレートエンジン
  • JavaScript のように変数が使える、楽

テンプレートを作成しました

gulp v4による Pug × SCSS × TypeScript のテンプレートを作成しました。
https://github.com/deren2525/gulp4-pug-scss-ts-template

使い方は上記リンクの README にも書きましたがこちらにも書きます。

ちなみに、 pug じゃなくていい、 HTML でいい人は
HTML × SCSS × TypeScript のテンプレもあります。
https://github.com/deren2525/gulp4-html-scss-ts-template

できること

まだ簡単なことしかできないです。今後機能追加していきたい。
Lint系は、私が VS Code の拡張機能に頼っているので gulp やるで必要あるのかなあと思い追加せず。

  • 保存時自動コンパイル
    • PugHTML
    • SCSSCSS
    • TypeScriptJavaScript
  • CSS にプリプロセッサ付与
  • ( Normalize.css がデフォで入っています)

pug ファイルをいくつか生成していくうちに HTML として出力してほしくないものが出てくると思います。
その時はファイル名の先頭に _ をつけることで HTML に出力されなくなります。

例: _hogehoge.pug

使い方

0. gulpPug のセットアップ

gulpPug がすでに入っている場合はここを飛ばしてください

nodenpm と (npx) が必要になるので入れていない場合入れてください
https://nodejs.org/ja/

# node チェック
$ node -v
v12.14.0
# npm チェック
$ npm -v
6.13.4
# npx チェック
$ npx -v
6.13.4

gulp をインストールします。

$ npm install --global gulp-cli

pug をインストールします。

$ npm install --global pug-cli

1. テンプレのインストール

こちらをクローンしてください。
https://github.com/deren2525/gulp4-pug-scss-ts-template

$ git clone git@github.com:deren2525/gulp4-pug-scss-ts-template.git
$ cd gulp4-pug-scss-ts-template
$ npm install

gulpPug がちゃんと入っているかのチェックも忘れずに?

# gulp チェック
$ gulp -v
CLI version: 2.2.0
Local version: 4.0.2
# pug チェック
$ pug --version
pug version: 2.0.0-rc.4
pug-cli version: 1.0.0-alpha6

2. 使う!

うまくいけていたら、gulp コマンドで起動されるはずです
http://localhost:3000 へ飛んでみてページが反映されていたら成功です。

# 起動
$ gulp

pug ファイルや scssts をいじって保存したらコンパイルされているはずです...!

テンプレのディレクトリ構成

基本的に触るのは src ディレクトリ配下のものたちになると思います。
dist ディレクトリ配下には自動コンパイルされたものが入るので直接手で変えても src ディレクトリ配下のファイル達によって上書きされてしまいます。

gulpfile.tsgulp によるコンパール設定が書いてます。

ディレクトリ構成.txt
...
├─ gulpfile.ts
├─ src
│  ├─ pug
│  │  ├─ include
│  │  │  └─ _base.pug
│  │  └─ index.pug
│  ├─ scss
│  │  └─ style.scss
│  └─ typescript
│     └─ main.ts
└─ dist // 保存時自動コンパイルされてこちらに格納されます。
   └─ assets
      ├─ css
      │  └─ style.css
      ├─ js
      │  └─ main.js
      └─ index.html

Pug ディレクトリの中身

src > pug > include >_base.pug
に メタ情報とか書いてます。各々設定してください。

_base.pug
doctype html
html(lang="ja")
  head
    meta(http-equiv="X-UA-Compatible", content="IE=edge")
    meta(charset="UTF-8")
    block title
      title gulp4-html-scss-ts-templete
    meta(name="description", content="")
    meta(name="author", content="")
    meta(name="viewport", content="width=device-width, initial-scale=1")
    link(rel="stylesheet", href="/css/style.css")
    //- [if lt IE 9]
    //- script(src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.2/html5shiv.min.js")
    //- script(src="//cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js")
    //- [endif]
    link(rel="shortcut icon" href="")
    block headVendor
    script(src="/js/main.js")
    block head
  body
    block body

それを src > pug > index.puginclude するといいかんじになります。
っていうところまでテンプレで実装されています。

index.pug
extends include/_base

block title
  title ここにタイトルを書くとHTML上では <title></title> 内に反映されます

block append body
  // Place your content here
  // written in the body tag
  // この中に書くと HTML上では <body></body> 内に反映されます
  h1.title gulp4-pug-scss-ts-templete?

おわりに

あくまでテンプレなので使っていただける方がもしいましたら、このテンプレから自分でよしなにカスタマイズしちゃってください?!
そして使ってみて、
「こんなやり方よりもこんな方法がありますぜ...」とか
「こんな機能があったらよき〜〜」とか
なにか意見をお持ちの方、どんどん改善していきたいので大募集です!!お待ちしております。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AdobeXDを使って開発したら地獄を見た件について

AdobeXDとは

Adobe XDは、共同作業を促進するパワフルで使いやすいプラットフォーム。webサイトやモバイルアプリ、音声インターフェイス、ゲームなどのデザイン制作をチーム全体でスムーズにおこなうことができます。

引用:https://www.adobe.com/jp/products/xd.html?sdid=19SCDRPN&mv=search&ef_id=CjwKCAiAjMHwBRAVEiwAzdLWGAVvSherY4n7ES1kvKK6kgpKy_Fb3dgE0USmjhwRWQsSpvJL-GEpgRoCGWIQAvD_BwE:G:s&s_kwcid=AL!3085!3!380840905165!e!!g!!adobexd

簡単に言うと、
GUI操作でWebサイトを作れちゃう
という優れものです。

なにをしたら地獄を見たのか

登場人物は三人。僕とH君とO君。

文化祭でLinuxの勉強ができるブラウザゲームっぽいの作ろうぜ!!

この一言がすべての始まりでした。

役割
H君:データベースの構築
O君:JavaScriptでゲームの作成
僕 :AdobeXDでゲームのタイトル画面、ユーザアカウント作成等の画面作成

このように役割分担をしました。

AdobeXDには、作成したページをHTML,CSSと書き出せる機能があります。(最強)

この機能を使い、僕の作成したWebデザインにO君の作ったゲームを入れ込んで、H君のデータベースを使って完成だ!!

なんかいけそうですよね。

しかし、この  入れ込む  という工程で地獄を見ました…

いざ、開発。

下のスクリーンショットが実際のAdobeXDの画面です。
image.png

このころの自分はHTMLを一切かけませんでしたが、ほんとに直感的に開発ができました。(ここはほんとにすごいと思う)

Webデザインも完成したし、ついに合体するぞ!!

実際に作ったWebページ
html.png

これがコード

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Linux Dungeon</title>
<style id="applicationStylesheet" type="text/css">

    .mediaViewInfo {
        --web-view-name: Web 1366  2;
        --web-view-id: Web_1366___2;
        --web-enable-deep-linking: true;
    }
    :root {
        --web-view-ids: Web_1366___2;
    }
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
        border: none;
    }
    #Web_1366___2 {
        position: absolute;
        box-sizing: border-box;
        background: url("ダンジョン背景.png"); 
        width: 1366px;
        height: 1022px;
        overflow: hidden;
        --web-view-name: Web 1366  2;
        --web-view-id: Web_1366___2;
        --web-enable-deep-linking: true;
    }
    .____3 {
        position: absolute;
        overflow: visible;
        width: 1366px;
        height: 123px;
        left: 0px;
        top: 0px;
    }
    .____4 {
        position: absolute;
        overflow: visible;
        width: 1110px;
        height: 440px;
        left: 128px;
        top: 193px;
    }
    #___ {
        position: absolute;
        left: 11px;
        top: 15px;
        overflow: visible;
        width: 106px;
        white-space: nowrap;
        text-align: left;
        font-family: PixelMplus12;
        font-style: normal;
        font-weight: normal;
        font-size: 35px;
        color: rgba(255,255,255,1);
    }
    #____A3_Text_2 {
        position: absolute;
        left: 11px;
        top: 70px;
        overflow: visible;
        width: 106px;
        white-space: nowrap;
        text-align: left;
        font-family: PixelMplus12;
        font-style: normal;
        font-weight: normal;
        font-size: 35px;
        color: rgba(255,255,255,1);
    }
    .____12 {
        position: absolute;
        overflow: visible;
        width: 1204px;
        height: 283px;
        left: 81px;
        top: 718px;
    }
    #_______ {
        position: absolute;
        left: 1110px;
        top: 70px;
        overflow: visible;
        width: 246px;
        white-space: nowrap;
        text-align: left;
        font-family: PixelMplus12;
        font-style: normal;
        font-weight: normal;
        font-size: 35px;
        color: rgba(255,255,255,1);
    }
    #_____ {
        position: absolute;
        left: 1180px;
        top: 15px;
        overflow: visible;
        width: 176px;
        white-space: nowrap;
        text-align: left;
        font-family: PixelMplus12;
        font-style: normal;
        font-weight: normal;
        font-size: 35px;
        color: rgba(255,255,255,1);
    }
    .____13 {
        position: absolute;
        overflow: visible;
        width: 122px;
        height: 51px;
        left: 116px;
        top: 751px;
    }
    .____14 {
        position: absolute;
        overflow: visible;
        width: 122px;
        height: 51px;
        left: 302px;
        top: 751px;
    }
    .____15 {
        position: absolute;
        overflow: visible;
        width: 122px;
        height: 51px;
        left: 488px;
        top: 751px;
    }
</style>
</head>
<body background ="ダンジョン背景.png">

        <div id="Web_1366___2" class="">
    <svg class="____3">
        <rect fill="rgba(0,0,0,1)" stroke="rgba(255,255,255,1)" stroke-width="6px" stroke-linejoin="bevel" stroke-linecap="butt" stroke-miterlimit="4" shape-rendering="auto" id="____3" rx="0" ry="0" x="0" y="0" width="1366" height="123">
        </rect>
    </svg>
    <svg class="____4">
        <rect fill="rgba(255,255,255,1)" stroke="rgba(112,112,112,1)" stroke-width="1px" stroke-linejoin="miter" stroke-linecap="butt" stroke-miterlimit="4" shape-rendering="auto" id="____4" rx="0" ry="0" x="0" y="0" width="1110" height="440">
        </rect>
    </svg>
    <div id="___">
        <span>名前:</span>
    </div>
    <div id="____A3_Text_2">
        <span>点数:</span>
    </div>
    <svg class="____12">
        <rect fill="rgba(111,111,111,1)" stroke="rgba(254,254,254,1)" stroke-width="5px" stroke-linejoin="bevel" stroke-linecap="butt" stroke-miterlimit="4" shape-rendering="auto" id="____12" rx="0" ry="0" x="0" y="0" width="1204" height="283">
        </rect>
    </svg>
    <div id="_______">
        <a href="#" class="btn-flat-logo">
            <i class="fa fa-chevron-right"></i> タイトルに戻る
          </a>
    </div>
    <div id="_____">
        <a href="#" class="btn-flat-logo">
            <i class="fa fa-chevron-right"></i> スコア表示
          </a>
    </div>
    <svg class="____13">
        <rect fill="rgba(255,255,255,1)" stroke="rgba(112,112,112,1)" stroke-width="1px" stroke-linejoin="miter" stroke-linecap="butt" stroke-miterlimit="4" shape-rendering="auto" id="____13" rx="0" ry="0" x="0" y="0" width="122" height="51">
        </rect>
    </svg>
    <svg class="____14">
        <rect fill="rgba(255,255,255,1)" stroke="rgba(112,112,112,1)" stroke-width="1px" stroke-linejoin="miter" stroke-linecap="butt" stroke-miterlimit="4" shape-rendering="auto" id="____14" rx="0" ry="0" x="0" y="0" width="122" height="51">
        </rect>
    </svg>
    <svg class="____15">
        <rect fill="rgba(255,255,255,1)" stroke="rgba(112,112,112,1)" stroke-width="1px" stroke-linejoin="miter" stroke-linecap="butt" stroke-miterlimit="4" shape-rendering="auto" id="____15" rx="0" ry="0" x="0" y="0" width="122" height="51">
        </rect>
    </svg>
</div>
</body>
</html>

ここまでの量のコードをGUIでパパパっとやるだけでできるのはかなりえぐいと思う…

この画面中央にある白い長方形。ここにゲーム画面が来る構成となっています。ちなみに完成したのは文化祭の一週間前
進行具合を二人に聞いてみました。

O君 H君:「終わってないよ」

・・・・  わろた

でも、あと少しで終わるようなので安心。二日前になってようやっと合体の作業に取り掛かります。

文化祭の二日前…

みんな構築が終わったようなのでついに合体しよう!と、H君が取り掛かります。
すると、

H君:「これ、むりじゃね?」

ΩΩΩ<な、なんだってー!?

絶望しました。
いったい何が原因なんでしょうか。

何がダメだったのか

原因

答え:AdobeXDのHTMLコードがやばかった

head タグのところをみると

#___ {
        position: absolute;
        left: 11px;
        top: 15px;
        overflow: visible;
        width: 106px;
        white-space: nowrap;
        text-align: left;
        font-family: PixelMplus12;
        font-style: normal;
        font-weight: normal;
        font-size: 35px;
        color: rgba(255,255,255,1);
    }

こんな感じになってます。
1. 場所がpx形式でpcによって変わってしまう。
2. こんなのが多いから意味わからん
3. 宣言のところ、これはどれ?状態
 #____ってなんだよ

この三つの問題点より、このHTMLコードが丸々没になりました。

結局どうしたのか

答え:H君が徹夜で一から作り直す

AdobeXD… 撃沈

結局、何とも言えない完成度で文化祭を終えました。

まとめ

・AdobeXDを信頼しすぎてはいけない

・Web開発を複数人でやると何かしらの問題が起こる

共同開発するなら、どうゆう風に設定するかとかを頻繁に話さないと死ぬと学びました。


AdobeXDというのは、すごく便利ですが、あくまでもざっくりとした枠組みを作るツールとしたらすごく便利だと思います。

ご利用は計画的に…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML

本日が初投稿です。
学習したことをメモ書き程度に書く場にしようと考えております。
多めに見てやってくださいm(__)m

HTMLとは

静的なwebサイト作成に使用される「マークアップ言語」の一つです。タグを使いテキストを囲むことで、「見出し」や「リンク」を作成することができます。

タグの例

<開始タグ>こんにちは</終了タグ>

このように入力すると画面上に「こんにちは」と表示される。

タグの種類
|タグ|意味|
|:--|--:|
|h1~h6|見出し|

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML学習

本日が初投稿です。
学習したことをメモ書き程度に書く場にしようと考えております。
多めに見てやってくださいm(__)m

HTMLとは

静的なwebサイト作成に使用される「マークアップ言語」の一つです。タグを使いテキストを囲むことで、「見出し」や「リンク」を作成することができます。

タグの例

<開始タグ>こんにちは</終了タグ>

このように入力すると画面上に「こんにちは」と表示される。
終了タグの前には必ず「/」を入力する。

主なタグ

h1~h6→見出し
a→リンク
p→段落

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML&CSS学習

本日が初投稿です。
学習したことをメモ書き程度に書く場にしようと考えております。
多めに見てやってくださいm(__)m

HTMLとは

静的なwebサイト作成に使用される「マークアップ言語」の一つです。タグを使いテキストを囲むことで、「見出し」や「リンク」を作成することができます。

タグの例

<開始タグ>こんにちは</終了タグ>

このように入力すると画面上に「こんにちは」と表示される。
終了タグの前には必ず「/」を入力する。

主なタグ

h1~h6→見出し
a→リンク
p→段落

CSSとは

HTMLで作成したファイルをデザインするためのファイルのこと。画面上で反映させるために、「link ren="stylesheet" href="cssファイル名"」をHTMLファイルのheadタグ内に記述している必要がある。

書き方

例;
h1{
(場所)
color : #ff0000
何を:どうする;
(色を:赤色にする)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでシークレットモードを検出する方法

最新の(Chrome76以降)のシークレットモード検出方法

2020年1月現在、Chromeブラウザのシークレットモードは下記のJSで検出できる。ただし、問題点も残っている。

function detectSecretMode () {
  if ('storage' in navigator && 'estimate' in navigator.storage) {
    navigator.storage.estimate().then(function (estimate) {
      var usage = estimate.usage;
      var quota = estimate.quota;

      if (quota < 120000000) {
        console.log('Incognito');
      } else {
        console.log('Not Incognito')
      }
    });
  } else {
    console.log('Can not detect');
  }

このシークレットモード検出方法は、ユーザーのストレージが少なすぎるなら、シークレットモードであるという仮定に基づいているが、この仮定は100%確実ではない。実際に自サイトで利用したところ、およそ一桁%の確率で誤検出が起きる。

これまでの誤検出状況を見る限り、写真等をたくさん保存するユーザーは、本当にストレージが少ないことがそれなりにある模様。

また、OSのバージョンが古い(Android4, 5, 6 等)ときも起きるようだが、ストレージ容量起因の誤検出と原因の分離ができていないためこちらは少し曖昧。

少し古い(Chrome76よりも前)シークレットモード検出方法

少し古いChromeでは、下記のJSでシークレットモードを検出できる。

function detectSecretMode () {
  var fs = window.RequestFileSystem || window.webkitRequestFileSystem;
  if (fs) {
    fs(window.TEMPORARY,
        100,
        function (fs) {
          console.log('Not Incognito');
        },
        function (fe) {
          console.log('Incognito');
        });
  } else {
    console.log('Can not detect');
  }
}

参考リンク

「Chrome」のシークレットモードを検知できる手法、研究者が指摘
Bypassing anti-incognito detection in Google Chrome

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaとJacksonでJSON その③HTMLにJSONを埋め込んでJavaScript から利用する

はじめに

JSONを使う場合、JavaScriptからAjax経由でデータの送受信を行うケースが多いと思う。しかしながら、サーバからHTMLを受信したタイミングで、JSONデータを受け取ってJavaScriptで利用したいケースもある。この場合、サーバから返却するHTMLの中にJSONデータを埋め込んで、それをJavaScriptのオブジェクトとして読み込むことになる。PHPを利用した場合は、HTML に JSON データを埋め込んで JavaScript から利用するに記載の事例があったが、我らがJava(Servlet/JSP)による事例がなかったため、悪戦苦闘した結果をここに残しておく。

環境

  • Java 1.8
  • Tomcat 8.0.53
  • Jackson 2.10.1

まずは何も考えずにやってみよう⇒失敗

サーバ側

サーバ側は以下の通りとした。"<"や">"については、前回同様、HTMLのタグとして解釈される恐れがあることから、Unicodeエスケープシーケンス変換はそのままとしている。前回までとの違いは、JSON文字列を、HttpRequestのパラメータとして保存し、それをJSPに処理させている点だ。詳しくはクライアント側の方で解説する。
また、意地悪データとして、"Programmer"⇒"Programmer\"のようにデータの最後に\を入れてみた。

ServletTest2.java
package servletTest;

import java.io.IOException;
import java.util.List;
import java.util.ArrayList;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

@WebServlet("/helloworld2")
public class ServletTest2 extends HttpServlet {
    private static final long serialVersionUID = 1L;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //Javaオブジェクトに値をセット
        JsonBean jsonBean = new JsonBean();
        jsonBean.setId(1);
        jsonBean.setName("kimisyo");
        List<String> datas = new ArrayList<>();
        datas.add("Programmer\\");
        datas.add("Data Scientist<script>alert('hello!')</script>");
        jsonBean.setDatas(datas);

        ObjectMapper mapper = new ObjectMapper();
        mapper.getFactory().setCharacterEscapes(new CustomCharacterEscapes());

        //JavaオブジェクトからJSONに変換
        String testJson = mapper.writeValueAsString(jsonBean);

        //JSON文字列をrequestにセット
        request.setAttribute("jsonStr", testJson);
        ServletContext sc = getServletContext();
        sc.getRequestDispatcher("/clientTest2.jsp").forward(request, response);

    }
}

クライアント側

クライアント側はjspに処理を記載している。HttpRequestのパラメータとして設定されたJSON文字列を一旦Javaの変数に保存し、それをJavaScriptの中のJSONのParseの引数に設定している。
これがうまくいけば、dataというオブジェクトを通して、JSONのハンドリングが可能となる。

clientTest2.jsp
<%@ page contentType="text/html; charset=UTF-8"%>
<%
    String jsonStr = (String)request.getAttribute("jsonStr");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World</title>
<script>
    var data = JSON.parse('<%=jsonStr%>');
     alert(data.datas);
</script>
</head>
<body>
</body>
</html>

失敗状況

上のServletを実行してみると、JSON.parseのところでSCRIPT1014: 文字が正しくありません。というJavaScriptのエラーがでる(IEのコンソールで確認)。最終的にブラウザに出力されたHTMLは以下の通りだ。
JSONに入れた\は、Jacksonによって\\にちゃんとエスケープされているし、"<"や">"はUnicodeエスケープシーケンスに変換されている。一見問題なさそうに見える。

さぁ、何がまずかったのか考えて見よう。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World</title>
<script>
    var data = JSON.parse('{"id":1,"name":"kimisyo","datas":["Programmer\\","Data Scientist\u003Cscript\u003Ealert(\u0027hello!\u0027)\u003C\u002Fscript\u003E"]}');
    alert(data.datas);
</script>
</head>
<body>
    <div id="test"></div>
</body>
</html>

失敗原因

失敗原因はJavaScript文字列のエスケープ漏れだ。JavaScriptでは、\はエスケープ用の文字として使われる。このため、"Programmer\\"はJavaScriptの文字列として"Programmer\"と認識される。これがJSON.Parseに引き渡されるが、JSONでも本来"\"の文字自体は、"\\"のようにエスケープしなければならないため、不正なJSONデータとして扱われるのだ。

対策

対策としては、JavaScript用のエスケープ処理をかました上でJSON.parseの引数に与えればよい。修正ソースを以下に記載しておく。ここではJavaScript文字列のエスケープとして、"\"と"'"をエスケープするための処理を入れている。

clientTest2.jsp
<%@ page contentType="text/html; charset=UTF-8"%>
<%
    String jsonStr = (String)request.getAttribute("jsonStr");
    jsonStr = jsonStr.replace("\\", "\\\\");
    jsonStr = jsonStr.replace("'", "\\'");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World</title>
<script>
    var data = JSON.parse('<%=jsonStr%>');
     alert(data.datas);
</script>
</head>
<body>
</body>
</html>

これにより、正しくJSON文字列がJSONオブジェクトに読み込まれた。
余談であるが、JavaScript用に"\"をエスケープした場合、Unicodeエスケープシーケンス変換した文字(例えば、"\u0027")に使われている"\"もエスケープされ"\\u0027"に変換されておかしくならないかという思うかもしれない。
実は、"\\u0027"はJavaScriptによって"\u0027"と解釈され、それがJSON.parseの引数に与えれるため、JSON側で、Unicodeエスケープシーケンスとして解釈されているのだ。つまり前回のXSS対策の場合と同じ形のものがJSONに読み込まれており、むしろこちらの方が意図した動作となっているのである。JavaScript用のエスケープをする前は、実はHTML側でUnicodeエスケープシーケンスとして解釈されていたのだ。うーん、奥が深い。

おわりに

結局JavaScriptエスケープだったという地味なオチ。この連載を始めたときに、この記事を書きたいと思っていたので、とりあえず完結としたい。

ちなみに本記事のサーバ側の例で、WEBフレームワークを使わずにServletを使って説明している理由は、別にServletしか使えない、Servletを使いたいわけではなく、本記事のテーマに関係ない要素は除外し、本質的な部分のみにフォーカスしたかったためである。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 203日目

100日チャレンジの203日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

203日目は

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む