風柳メモ

ソフトウェア・プログラミング関連の覚書が中心。

【覚書】他社で取得した独自ドメインのメールアカウントをさくらのレンタルサーバーで使う場合のDNS設定方法

さくらのレンタルサーバーのメールサーバー機能を利用して、(さくら外で取得した)独自ドメインのアカウントでメールを送信したら、Gmail で「未認証メール」扱いとなった(「?」マークがついた)。
調べてみると、独自ドメインの DNS 設定(SPFレコード)が誤っていた。
再発防止のため、メモ書きとして恥をさらしておく。





メール用の DNS 設定例

さくらのレンタルサーバーの設定が以下の様になっている場合、

項目 値(例) 備考
初期ドメイン myaccount.sakura.ne.jp さくらのアカウント名が頭に付くドメイン名
ホスト名 wwwNNN.sakura.ne.jp さくらのサーバーのホスト名(IPv4アドレス=YYY.YYY.YYY.YYY)
※サーバ情報の表示>サーバに関する情報の「ホスト名」
SPFチェック対象となる(メールヘッダのReceived: from ~に記載される)のはこちら
IPアドレス XXX.XXX.XXX.XXX 初期ドメインに紐づいたIPv4アドレス
※サーバ情報の表示>サーバに関する情報の「IPアドレス」
※共有アドレスか専用アドレスかはプランによって異なる
サーバーのホスト名に紐づいたもの(YYY.YYY.YYY.YYY)とは異なる場合があることに注意

以下の様に独自ドメインを取得し、

項目 値(例) 備考
独自ドメイン mydomain.com (さくら外で取得した)独自ドメイン名

"user@mydomain.com" のようなメールアドレス(アカウント)を、さくらのレンタルサーバーで使用することを想定する。
さくらのコントロールパネルから、ドメイン/SSL設定>[新しいドメインの追加] にて、「5. 他社で取得したドメインを移管せずに使う」により、mydomain.com を追加してあるものとする。

このとき、メール用として、(他社の)DNSサーバーには以下の様に MX レコードと TXT レコード(SPFレコード)を登録する。

mydomain.com.          3600    IN      MX      10 myaccount.sakura.ne.jp.
mydomain.com.          3600    IN      TXT     "v=spf1 a:wwwNNN.sakura.ne.jp mx ~all"

SPFレコードの記述は、さくらのドメイン詳細設定を使用した際に「□SPFレコードを利用する」にチェックを入れた場合に設定されるものに準拠している(SPFディレクトリ中、'mx' は無くても良い。また、'a:wwwNNN.sakura.ne.jp' の代わりに 'ip4:YYY.YYY.YYY.YYY' と直接 IP アドレスを記載しても動作はするが、IPv6アドレスが使用されることもあるため、ドメイン名にしておいた方が無難)。

失敗例

SPF レコードを

mydomain.com.          3600    IN      TXT     "v=spf1 ip4:XXX.XXX.XXX.XXX ~all"
mydomain.com.          3600    IN      TXT     "v=spf1 a:myaccount.sakura.ne.jp ~all"
mydomain.com.          3600    IN      TXT     "v=spf1 mx ~all"

といった様に、許容するIPアドレス/ドメインとして初期ドメインを設定してしまうと、認証に失敗してしまう(場合がある)。
ビジネスプラン等では、初期ドメイン(myaccount.sakura.ne.jp)とサーバーのホスト(wwwNNN.sakura.ne.jp)が示すIPアドレスが異なっているため、失敗(softfail)する。スタンダードプラン等では同じIPアドレスを示すので、結果的に通る(pass)場合もある。

例えば Gmail アドレス宛にメールを送信したとき、認証に失敗していると、メールヘッダで

ARC-Authentication-Results: i=1; mx.google.com;
       spf=softfail (google.com: domain of transitioning user@mydomain.com does not designate YYY.YYY.YYY.YYY as permitted sender) smtp.mailfrom=user@mydomain.com

のように SPF チェックの結果が "softfail" であったことが記載され、Gmail で当該メールを見たときに「未認証メール」として、「?」マークが付く。
メールを開き、「?」マークにマウスオーバ(スマートフォン等の場合にはタップ)すると、「Gmail では、このメールが(スパム発信者からではなく)本当に mydomain.com から送信されたメールであることを確認できませんでした。」といった文言が表示される。

SPF レコードの妥当性チェック方法

設定した SPF レコードが有効かどうかは、port25が提供しているサービスを使うのが手っ取り早い。

check-auth@verifier.port25.com

宛にメールを送信すると、「Authentication Report」という Subject がついて、

==========================================================
Summary of Results
==========================================================
SPF check:          pass
"iprev" check:      pass
DKIM check:         none
SpamAssassin check: ham

のように SPF チェックの結果などを含むメールが返信されてくる。

Firefox のアドオン(content_scripts)でXMLHttpRequestやfetchを使う場合の注意

先日、Firefox で Twitter メディアダウンローダが正常に動作しないという報告を受け(その後、近傍ツイート検索も異常な動作をすることが判明)、原因を突き止めるのに時間がかかったので、覚え書き。

結論としては、

ということ。





現象

Firefox 上で、Twitter 用のアドオン(Twitter メディアダウンローダ(0.1.1.2801以前)/近傍ツイート検索(0.2.6.1200以前))を動かすと、jQuery.getJSON() や jQuery.ajax() で、本来 JSON が返されるべきところが HTML が返ってきてしまい、正常に動作しない。
基本的には同一のスクリプトであるにも関わらず、Google Chrome 拡張機能版や、Firefox であっても Tampermonkey 上のユーザースクリプトとしてであれば問題なく動作する。

原因

当該関数による GET リクエストの HTTP Request Header に

  • Origin: null がセットされる
  • Referer はセットされない

という動作になっていた。

なお、この現象が発生した際には、当該通信は開発者ツールのネットワークモニタ上に表示されない、という困った不都合もある(こちらは、未だに表示させる方法が不明のまま)。

Twitter 側で(リクエストの正当性検証のため?おそらく 最近になって)Referer等をチェックするようになったことが原因で、正常な応答が返されなくなっていたものと思われる。

追記(2018/07/19)

SWSが本脅威を対策するためには、Refererなどを確認してリクエストの正当性を検証したり、CookieにSameSite属性を付与する手法などが考えられます。

ソーシャルウェブサービスにおける新たなプライバシー脅威「Silhouette」を発見:NTT持株会社ニュースリリース:NTT HOME

実は、このプライバシー脅威(Silhouette)への対策の影響だった、とか?

対策

This is accomplished by exposing more privileged XHR and fetch instances in the content script, which has the side-effect of not setting the Origin and Referer headers like a request from the page itself would, this is often preferable to prevent the request from revealing its cross-orign nature. From version 58 onwards extensions that need to perform requests that behave as if they were sent by the content itself can use content.XMLHttpRequest and content.fetch() instead. For cross-browser extensions their presence must be feature-detected.

Content scripts - Mozilla | MDN

バージョン58以上の Firefox においては、アドオンで content_scripts 内で XMLHttpRequest や fetch (およびそれらを使用している jQuery 等のライブラリ)を使用するときには、window.XMLHttpRequest や window.fetch の代わりに、content.XMLHttpRequest や content.fetch を用いればよいらしい。
これにより、普通にページ(コンテンツ)上から発行されたのと同様に、Origin や Referer がセットされるようになる。

注意事項

content_scripts 内では、window.XMLHttpRequest や window.fetch は read-only になっているため、content.* で上書きしようとしてもできない。

自作スクリプト内ならば、使用するスコープの最初で、

const XMLHttpRequest = ( typeof content != 'undefined' && typeof content.XMLHttpRequest == 'function' ) ? content.XMLHttpRequest  : window.XMLHttpRequest;
const fetch = ( typeof content != 'undefined' && typeof content.fetch == 'function' ) ? content.fetch  : window.fetch;

のようにしておけばよいかも。

問題なのは、jQuery のようなライブラリを使用する場合で、作りによってはソースコードを直接書き換えるしか無いかもしれない。
なお、Firefox のアドオンを AMO にアップする際には、既知のライブラリを使う場合、改変されたものは同梱出来ない、という制約がある。レビューでチェックされ、改変が見つかった場合は当該バージョンが無効化されてしまう(既に経験済み(汗))。

幸い、jQuery の場合には、読み込んだ後にjQuery.ajaxSettings.xhrを次のような感じにオーバーライドしてやればよさげ。
追記と訂正:直接書き換えるよりも、jQuery.ajaxSetup() を介するべきだった。

if ( ( typeof content != 'undefined' ) && ( typeof content.XMLHttpRequest == 'function' ) ) {
    /*
    //jQuery.ajaxSettings.xhr = function () {
    //    try {
    //        return new content.XMLHttpRequest();
    //    } catch ( e ) {}
    //};
    */
    jQuery.ajaxSetup( {
        xhr : function () {
            try {
                return new content.XMLHttpRequest();
            } catch ( e ) {}
        }
    } );
}

WordPressをhttps化したら、Contact Form 7 の設定が消えてしまった⁉

WordPress を設置してあるサイトを SSL/TLS(http → https)化し、記事なども含めて http://~ となっていたサイトの URL を https://~ に置換したところ、Contact Form 7 の一部の設定が消えてしまった。
今後も似たような失敗をしそうなので、覚え書きとして記事にしておく。




現象

f:id:furyu-tei:20180607212208p:plain

のような、コンタクトフォーム(Contact Form 7)のメールテンプレート設定がなされていたところに、Search Regexを使用して、Post meta value 中のサイトの URL を http://~ から https://~ に置換した。

f:id:furyu-tei:20180607212720p:plain

すると、なぜか、コンタクトフォームのメールテンプレート設定が無くなってしまっていた。

f:id:furyu-tei:20180607212804p:plain

原因

バックアップして置いたデータベースのファイルを調べてみたところ、コンタクトフォームのメールテンプレートは、

(9, 5, '_mail', 'a:9:{s:6:\"active\";b:1;s:7:\"subject\";s:35:\"WordPressてすと \"[your-subject]\"\";s:6:\"sender\";s:42:\"[your-name] \";s:9:\"recipient\";s:18:\"xxxxxxxx@xxxxxxxxx\";s:4:\"body\";s:238:\"差出人: [your-name] <[your-email]>\n題名: [your-subject]\n\nメッセージ本文:\n[your-message]\n\n-- \nこのメールは WordPressてすと (http://xxxxxxxxxxxxxxxxxxxxxxxxx) のお問い合わせフォームから送信されました\";s:18:\"additional_headers\";s:22:\"Reply-To: [your-email]\";s:11:\"attachments\";s:0:\"\";s:8:\"use_html\";b:0;s:13:\"exclude_blank\";b:0;}'),

のような形で格納されていた。

勘のいい方はお気づきだろう……そう、つまり、PHPでシリアル化(serialize)された形式になっているため、http → https へと単純に置換してしまうと、文字数が1文字増えて、シリアル化データとしては壊れてしまい、コンタクトフォームでは不正な値として設定が無効化されてしまったのである。

対策

今回たまたま当方が気付いたのがコンタクトフォームだっただけで、似たような現象は他のプラグインでも発生しうると思われる。
Search Regex による Post meta value の機械的な置換は実施せず、各プラグインの設定にサイトの URL が含まれる場合には、逐次確認して手動で変更するのが無難だろう。
コンタクトフォームに限れば、データベースファイル中の '_mail' や '_mail_2' といった行の "body" 中にサイトの URL が含まれる場合、 "body" の長さをひとつ増加させるようなスクリプトでも組んでやれば対応可能ではあろうが。

より効率の良い方法やプラグインなどをご存知の方は、ご教示願いたい。

追記

はけた氏(@)にもツイートでご指摘いただいたが、こういう場合は
interconnectit.com
を使うのが定石の模様。

Search Replace DB を使うと、
f:id:furyu-tei:20180608090145p:plain
このように、Post meta value 等のシリアル化されたデータについても、それに配慮した変換を行ってくれる。

なお、実際に不具合で発生したサイトでも最初はこれを試そうとしたのだが、WordPress のバージョンの問題でうまく実行できなかったのであった……環境はなるべく最新のものを使おうね、というお話(自分が制作・管理していない場合、いかんともしがたいが・苦笑)。

【覚書】viewport指定について

モバイルデバイス用のviewport設定についてまじめに調べようとしたら、深みにはまりそうだったので、とりあえず整理を兼ねて覚え書きを残しておく。
ついでに、viewportの簡易的な表示テストができるページも作ってみた




viewport の指定方法

viewportは、HTMLのhead内のmeta要素として指定する

<meta name="viewport" content="(viewportのパラメータ)" />

viewportのパラメータはカンマ(",")区切りで指定。

なお、これは主としてモバイルデバイス(スマートフォン・タブレットなど)用の設定であり、基本的にはデスクトップ用ブラウザの表示には影響を与えない。
ブラウザが常に全画面で表示されるために、基本、ウィンドウの幅=デバイスの表示幅となるモバイルデバイスにおいて(余白が出来たり見切れたりすることなく)適切なページを表示を行うための指定という認識。

viewportの主要なパラメータ

<meta name="viewport"> の content の値
設定可能な値 説明
width 正の整数または文字列 device-width ウェブサイトを描画したいビューポートの幅をピクセル数で定義します。
height 正の整数またはテキスト device-height ビューポートの高さを定義します。どのブラウザーでも使用されていません。
initial-scale 0.0 から 10.0 までの、正の数値 デバイスの幅 (ポートレートモードでの device-width またはランドスケープモードでの device-height) とビューポートの寸法との比率を定義します。
maximum-scale 0.0 から 10.0 までの、正の数値 ズームの最大値を定義します。この値は minimum-scale と同じまたはより大きくしなければなりません。そうではないときの動作は未定義です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。
minimum-scale 0.0 から 10.0 までの、正の数値 ズームの最小値を定義します。この値は maximum-scale と同じまたはより小さくしなければなりません。そうではないときの動作は未定義です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。
user-scalable yes または no no を設定すると、ユーザーはページのズームができなくなります。既定値は yes です。ブラウザーの設定でこの規則を無視できます。また、iOS 10 以降は既定で無視します。

<meta>: 文書レベルメタデータ要素 - HTML: HyperText Markup Language | MDN
viewportを指定しないとどうなる?

viewport指定を省略した場合、モバイルデバイスでは、ブラウザが暗黙的にデフォルトのwidth値(ブラウザ毎に異なる、例えば980といった値)を用いて、これをウィンドウ(≒スクリーン)内に収めるように表示される。
例えば、デスクトップ向けのページなどで、横幅がこれより大きい値を前提として設計されていたら見切れてしまう。

device-width とは?

device-widthとは、モバイルデバイスが持つ画面(スクリーン)の論理解像度(ポイント実質解像度CSSピクセル)の横幅のことで、これはカタログに載っている解像度(画素数)とは異なる。
device-heightは同じく縦幅のことだが、ほとんど使うことはないと思われる。
例えば、iPhone X のディスプレイは5.8インチ・1125x2436ピクセルの解像度を持つが、device-widthは375(device-heightは812)となる。
すなわち、ブラウザのウィンドウで375px×812pxの画像を等倍で表示した場合、画面にピッタリと収まるサイズで表示されるということになる。

initial-scale とは?

基本的には、ページを表示した際の、最初の表示倍率(拡大率)のこと。
例えば、initial-scale=1 の時は等倍で、initial-scale=0.5の時は幅が半分に縮小(矩形としては4分の1)、initial-scale=2の時には幅が倍に拡大(矩形としては4倍)となる。

これだけならばよいのだが、ややこしいのが『条件によっては viewport の大きさに影響してしまう』ということ(後述)。

viewport が影響する主な要素

JavaScript

viewportの指定によりブラウザのウィンドウの大きさが決まることになるため、window.innerWidth・innerHeightといったパラメータに影響する。
なお、window.screen.widthやwindow.screen.availWidthは、viewportの指定によらず一定で、デバイス固有の値(≒device-width)を持つ。

viewport 設定テスト用ページについて

簡易的な viewport 設定テスト用ページを作成した。

viewport setting tester

  • radioボタンに応じて、meta[name="viewport"]を書き換えるようになっている。
  • 条件によっては、値を変更しても表示が変わらない場合がある。この場合、[Reload]を押すことで反映される。
    Mobile 版 Firefox では、[Reload]を押しても変わらない場合あり。この場合、ページをいったん閉じて、開きなおす必要がある。
  • Google Chrome の開発者ツールのモバイルデバイスエミュレータ(device toolbar)で、画面の拡大縮小(ピンチアウト/イン)の操作は、[Shift]+ドラッグ(スワイプ)で行う([Ctrl]+[+]/[-]では変化しないことに注意)。リセットは[Ctrl]+[0]。

具体的な viewport 指定の例

"width=device-width, initial-scale=1" で、万全!?

viewportについて調べていると、典型的な設定例として出てくるのは、

<meta name="viewport" content="width=device-width, initial-scale=1" />

のようなもの。

  • viewport(≒ブラウザのウィンドウ)幅を device-width に
  • 最初の表示倍率を 1 に

という指定。
f:id:furyu-tei:20180520095805p:plain

適切なレスポンシブ対応(メディアクエリのブレイクポイントの設定)が行われている場合には、この設定で問題は無い。

レスポンシブ対応がなされていない場合

レスポンシブ対応がなされておらず、『とりあえず、デスクトップ用の画面を、モバイルデバイスでも横幅が綺麗に収まる形で表示したい』という場合の設定。
例は、横幅1000pxで設計されているページの例

<meta name="viewport" content="width=1000, user-scalable=no" />

f:id:furyu-tei:20180520095818p:plain
本来ならば、width さえ指定しておけば、ブラウザが拡大率を適切に設定して、スクリーン内に収まるように調整してくれそうなもの。
ではなぜ user-scalable=no を設定しているのかというと、状況によってはこの自動調整がうまくいかないケース(等倍で表示されるケース)があったため
うまく自動調整されない場合の原因は不明。
いずれにしても、user-scalable=no にするとユーザーが拡大縮小できなくなってしまい不便なので、早期にきちんとレスポンシブ対応すべきではある。

レスポンシブ対応が不完全な場合

スマートフォンには対応したのだけれど、ある範囲(例えば横幅768px~1199px)のデバイス(タブレット)だけ表示が見切れる(デスクトップ用に1200px前提でページ設計)という場合の設定。

<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
( function () {
'use strict';

var min_screen_width_for_tablet = 768, // @media (min-width: <min_screen_width_for_tablet>px) { ... }
    viewport_width_for_tablet = 1200,
    screen_width = window.screen.width;

if ( min_screen_width_for_tablet <= screen_width && screen_width < viewport_width_for_tablet ) {
    document.querySelector( 'meta[name="viewport"]' ).setAttribute( 'content', 'width=' + viewport_width_for_tablet + ', user-scalable=no' );
}
} )();
</script>

デフォルトとして、meta[name="viewport"]には"width=device-width, initial-scale=1"を指定しておき、JavaScript で window.screen.width によりデバイスのスクリーン幅(≒device-width)を調べ、条件に当てはまる場合だけ書き換えを行う。

注意点・問題点など

initial-scale について

上記のとおり、initial-scaleは初期の表示倍率を示すほかに、条件によっては viewport の大きさに影響してしまう。
具体的には、

X = device-width / initial-scale
とするとき、
width < X となる場合には、viewport の幅として X が適用される(widthの指定は無視される)。

模様。
例えばdevice-widthが360のモバイルデバイスの場合に、
"width=640, initial-scale=0.5"
のような指定をすると、実際の viewport の幅としては 720(=360/0.5)が適用されてしまう(width=640の方は無視される)

また、initial-scale に 1 未満の値を指定しても、状況によってはうまく縮小表示されないケースもある。
具体的な条件は不明。ただし、この場合でも、user-scalable=no と併用すれば正しく縮小表示される模様。

ブラウザによってウィンドウサイズ(innerWidth/Height)の決定方法が異なっている?

f:id:furyu-tei:20180520095827p:plain

Google Chromeの場合
innerWidth/innerHeightは、ユーザーがscale(ピンチインで縮小表示)して表示可能な最大領域のピクセル数を示す模様。

この最大領域の横幅は、document.body の大きさと viewport の minimum-scale により、以下のように決まっている模様。

  • document.body の横幅が viewport の以下の場合には、viewport の幅
  • document.body の横幅が viewport より大きい場合、device-width / minimum-scale と document.body の横幅のうち、小さい方

あくまで最大領域に応じて決まるため、実際のユーザーの拡大縮小操作には影響されない。

Firefoxの場合
innerWidth/innerHeightは、ドキュメントのうち、ブラウザの表示領域に実際に表示されているピクセル数を示す模様。
ユーザーの拡大縮小操作に伴って変動する。

【未確認】Safariの場合
実機が無いため、確認できていない。

【未確認】Windows Tablet / Phone について

Windows 系のモバイルデバイスについては、meta[name="viewport"] による指定は効かないため、別途、CSS で @-ms-viewport による指定が必要
実機が無いこともあり、確認が取れていない。

【覚書】ノートン 360 のせいで、pip freeze がフリーズした件

Windows 10 上の Ubuntu で

$ pip freeze

を実行したら、文字通りフリーズしてしまった。
プロンプトに返ってこない。pip install 等を実行した場合も同様。

原因は、ノートン 360 Norton 360のファイアウォールだった。
同様の事が発生したときに焦らないように、メモ書きしておく。




原因と対策

Python 3(3.6.3)の python3 (実行ファイル) がブロックされていたのが原因。
ノートン 360 の
「設定」→「ファイアウォール」→「プログラム制御」
で確認すると、python3の『信頼』項目が『未確認』になっていた。
この状態で、アクセスが『自動』になっていると、ネットワークアクセスがブロックされてしまうため、これを手動で『許可』に変更して[適用する]必要があった。
ファイアウォールのプログラム制御画面

余談…というべきかどうか

ノートン 360 のライブアップデートで、インストール失敗

pip freeze や pip install が固まってしまう状態になった際、過去の経験


もあって、「どうせまた、ノートン 360 が原因だろう」と思い、軽い気持ちで手動でライブアップデートを実施したところ……再起動後に
ノートン 360のインストールでエラーがありました。続行できません。
と言われてしまい、往生した。
もちろん、ノートン 360 そのものも動作していないため、セキュリティ的にノーガード……。

その後の復旧

上記の状態で、どうしようもないのでそのまま強制的に再起動すると*1、今度は、
ライブアップデートエラー
のようなダイアログが現れた。
最初からこっちを出してくれ、と思わないでもない。
『サポートWebページに移動する』リンク先の指示に従って、『ノートン削除/再インストールツール』(NRnR.exe) をダウンロードして実行。
削除と再インストールを行うことで、なんとか復旧することができた。

*1:普通に再起動しようとすると、再起動をキャンセルしてとノートン360に言われてしまう……キャンセルせず、強制的に再起動する必要があった