ASP.NET MVC アプリの脆弱性対策について

対象

ASP.NET MVC 5 (.NET Framework) + Azure Web Apps を対象とする。

不要なレスポンスヘッダーの削除

既定のままだと、レスポンスヘッダーには以下のような ASP.NET を示唆する情報が出力される。

  • Server: Microsoft-IIS/10.0
  • X-AspNet-Version: 4.0.30319
  • X-AspNetMvc-Version: 5.2
  • X-Powered-By: ASP.NET

それぞれ、以下のように対応する。

  • X-AspNet-Version
  • X-Powered-By
  • Server

Web.config

<system.web>
  <!-- enableVersionHeader 属性を追加 -->
  <httpRuntime enableVersionHeader="false" targetFramework="4.6.1"/>
</system.web>

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <remove name="X-Powered-By"/>
    </customHeaders>
  </httpProtocol>

  <security>
    <requestFiltering removeServerHeader="true"/>
  </security>
</system.webServer>
  • X-AspNetMvc-Version
  • Server

Global.asax.cs

protected void Application_Start()
{
    MvcHandler.DisableMvcResponseHeader = true;
}

protected void Application_PreSendRequestHeaders()
{
    HttpContext.Current.Response.Headers.Remove("Server");
}

Server については、web.config と Global.asax.cs のどちらでも削除可能。 web.config の場合は VS がなぜか警告してくるが、VS の問題のようで実際の動作は問題ないらしい (はずだが、web.config の方だと削除できない時があった。自分の環境で有効かはちゃんと実際に確認する事)。

なお、Server の削除については、上記対応方法だと正確には正常レスポンス時しか削除されない (web.config と Global.asax.cs どちらも)。 例えば、FireFox などでリクエストヘッダーの Host の値を改変して再送するとエラーとなるが、その場合は Server が出力されてしまう。

IHttpModule を継承したモジュールを使用したり、Web.config で runAllManagedModulesForAllRequests 属性を使用するなどの方法はあるが、完全に削除できるようには見えないため、完全削除は諦めた方が無難と思われる。

特に後者の runAllManagedModulesForAllRequests 属性を使用する方法は、パイプラインの動きを変えて静的なファイルについてもパイプラインを通るようになってしまうため、この対策のためだけに使用する事はお勧めできない。

セキュリティに関するレスポンスヘッダーの追加

既定のままだと、セキュリティに関する以下のレスポンスヘッダーが出力されない。

  • X-Content-Type-Options
  • X-Frame-Options
  • X-XSS-Protection
  • Strict-Transport-Security
  • Content-Security-Policy
  • X-Download-Options

それぞれ、以下のように対応する。

Web.config

<system.webServer>
  <httpProtocol>
    <customHeaders>
        <add name="X-Content-Type-Options" value="nosniff"/>
        <add name="X-Frame-Options" value="SAMEORIGIN"/>
        <add name="X-XSS-Protection" value="1"/>
        <add name="Strict-Transport-Security" value="259200"/>

        <!-- CSP ヘッダーは、何を適用する必要があるのか把握した上で適切な設定を行う必要がある。インラインスクリプトの許可なども含まれるので、プロジェクト全体の実装方針に影響する -->
        <add name="Content-Security-Policy" value="default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; "/>

        <add name="X-Download-Options" value="noopen"/>
    </customHeaders>
  </httpProtocol>
</system.webServer>

追加ヘッダーの値についてはサイト毎に要求される内容も異なるため、どの設定値が適切かは検討する必要がある。上記はただの一例。

特に CSP ヘッダーは、何を適用する必要があるのか把握した上で適切な設定を行う必要がある。 インラインスクリプトの許可なども含まれるので、プロジェクト全体の実装方針に影響する。

上記は、CSS はインライン許可、スクリプトもインライン及び eval() 許可する場合の設定。 iframe で他サイトを読み込む場合は frame-src が必要だし、他にも色々種別がある。

なお、類似するヘッダーとして X-WebKit-CSP および X-Content-Security-Policy ヘッダーが存在するが、共に 2012 年前後の古いブラウザで使用されていたヘッダーなので、現在は不要。

CSRF について

CSRF は、MVC の AntiForgeryToken で対応する。メソッドは HttpPost が対象。Get はそもそも登録系の処理には使用しないので・・・。

注意点として、Html.AntiForgeryToken() を使用したページのレスポンスヘッダーには、MVC が自動で X-Frame-Options: SAMEORIGIN を追加する動作がある。 上述した web.config での X-Frame-Options ヘッダー追加を行っている場合、同ヘッダー出力が重複する場合があるので、web.config で常に出力する設定にしている場合は、AntiForgeryToken の自動出力は抑制しておく。

Global.asax.cs

protected void Application_Start()
{
    AntiForgeryConfig.SuppressXFrameOptionsHeader = true;
}

Session Fixation 問題について

ASP.NET のセッション情報を使用する限り、Session Fixation 問題への完全な対応は、難しい様子。 認証処理はセッションを使用するのではなく、Session Fixation 問題が発生しないフォーム認証を使って対応する。

クッキーのセキュア化

クッキーのセキュア化は、主に 2 つ。HttpOnly および Secure 属性の付与となる。

HttpOnly 属性は以下の設定を行う。

Web.config

<system.web>
  <httpCookies httpOnlyCookies="true"/>
</system.web>

Secure 属性の付与は、Global.asax.cs の Application_EndRequest とかで動的に設定した方が良いと思われる (特に問題なければ全クッキー一律に)。

localhost での実行時は、基本的に http での実行になるはずなので、一律に Secure 属性を適用すると、ローカルで実行できなくなる。 その為、ローカル以外での実行時に動的に適用した方が良いかと思われる。

なお、Secure 属性が適用されているかブラウザの F12 開発者ツールで確認する場合は、リクエストクッキーではなく、レスポンスクッキーを見る必要があるので、注意。

電動ノコギリのすすめ

普通のノコギリで庭の木の枝を切るのに疲れてしまって、電動ノコギリを買ったら非常に便利だったので、その紹介。

高儀 EARTH MAN 14.4V充電式電気のこぎり

電動ノコギリはいくつもあるが、自分はこの電ノコが使うの初めてで他のは使ったことないので、それ前提で参考にしてもらえれば。

f:id:poke_dev:20190302162718j:plain

普通のノコギリで大変なこと

  • 切るのに時間がかかる
  • 体力が削られる
  • なかなか切れないので精神力も削られる

電動ノコの場合

  • 切るのに時間がかからない。チェーンソーのようにズバズバ切れるわけではないけど、大体 1 分で 1cm ぐらい切れる(個人差はあると思います)
  • 体力が削られない。ただ、全く体力が要らないわけではなく、動作時の振動が凄いのでそれに耐える必要はある。けど、ノコギリを自分で引くのに比べたら楽
  • 振動さえ耐えていれば後は電ノコおまかせで切れるので、精神面でも健全
  • 上位タイプとしてチェーンソーとかあるが、あっちはガソリン使ったりもするので、それに比べたら動力が電気の電ノコはお手軽
  • ノコギリと比べるとズバズバ切れるので、切る作業が楽しくなる(個人的な感想です)

電源ケーブルタイプか、バッテリータイプか

たぶん、電ノコを初めて買う場合、迷う点の一つに電源ケーブルタイプを買うか、バッテリータイプを買うかがあると思う。 自分の場合はバッテリータイプを買ったが、今の所、満足しかない。

バッテリーは、大体 2, 30 分使えて、充電には 3, 40 分ぐらいかかる感じ。 ちょっとした作業だったら 2, 30 分使えれば大抵問題ないと思うが、自分が買った電ノコはバッテリーが取り外し出来るタイプなので、必要に応じて、もう一つバッテリーパックを購入するというのも可能(本体の熱が大丈夫なのかは不明)。

バッテリータイプの電ノコでも、取り外しが出来ないタイプがあるらしく、その場合はそういう予備バッテリー方式が使えないと思うので、注意が必要と思う。

電源ケーブルタイプと比べてパワーが弱いのかなと買う前は思っていたが、自分としては特に問題なかった。ただ、電源ケーブルタイプを使ったことがないので、実際どれ位の差異があるかは不明。

使い方とか

基本的には電動の振動に耐えるだけで後は勝手に切れる感じだが、手動でも軽くノコギリ的な動きをさせた方が、切れやすい感じがある。 如何に同じ方向に刃を当て続けるかがキモ。

他、電ノコの特記事項とか

  • 比較するのもどうかとは思うが、ガソリン使うチェーンソーと比べると、流石に非力だと思う。10cm ぐらいまでの枝とかであれば問題ないと思うが、それ以上だと用途として外れると思う
  • 仕組み上、切る対象が固定されていないと、切れない。なぜかと言うと、電ノコの動きに合わせて対象も動いてしまうから。なので、長く飛び出てる枝とかは切れないと言うか、そういうのは用途違うので、普通に枝切りバサミ使う必要がある
  • 上述したように、振動が結構凄い。なので、電動だから非力な高齢者でも楽に切れるかと言うと、そううまくはいかないと思う。少なくとも普通のノコギリで作業ができないような人だと、電ノコも厳しいと思われる

自分が買った電ノコの特記事項とか

安全装置として、親指の辺りに配置されているボタンを押しながらじゃないと、トリガーを引いても作動しない。この作りが、右手で持ってトリガーを引く前提になっているっぽいので、左利きだと使うの厳しそう。

アマゾンとかのレビューでは、そもそも右利きでもこの安全装置のボタンを押しながらだと長時間作業はキツイというコメントを見かけるが、安全やコストとのトレードオフな面もあるので、個人的には仕方ないかなぁと思った。(長時間押し続けるのがキツイと言う感想自体は同意)

結論

ノコギリを使う場面が定期的にあるのであれば、絶対電ノコ持っていた方が良い。価格も安いやつであれば、1 万 5 千円もしない。

現場からは、以上です。

タニタのデジタル温湿度計買ったけど、壁掛けはどうするのってなった時

柱に温湿度計かけようと思ってこんなのを買って、

壁掛けするための穴もこんな感じに開いてるのに、

f:id:poke_dev:20190227233704p:plain

肝心の穴にかけるネジが付属してないやんけって話。 木の柱であれば、以下のネジでうまく壁掛けできました。

八幡ねじ 壁掛用ねじ 3.5×20mm

八幡ねじ 壁掛用ねじ 3.5×20mm

  • メディア: Tools & Hardware

実際に木の柱にネジを入れる時は、キリやら電動ドライバやらがないと辛いけど、それはまた別の話。

現場からは以上です!

自分用確定申告メモ

来年の自分へ・・・

自分の属性

  • 派遣として働いてる
  • 社会保険は基本的に厚生年金+健保だが、たまに無職の期間あるので、その場合は国民年金+任意継続保険に切り替わる
  • 株メインで投資やってる
  • ふるさと納税も使う
  • 年末調整は使わない

基本

  • 確定申告は全て Web サイトで行う。入力したデータの一部は来年以降も引き続き使えるので、非常に便利
  • 作成する申告書の種類は、所得税
  • e-tax も便利だけど証明書の更新が必要なこともあり、最近は Web からダウンロードした PDF をコンビニのプリンタでプリントアウトして、郵送
  • e-tax は基本的に添付書類不要(手元に保管しておく必要があるらしい)なので、状況によって利用を検討

確定申告時に必要となる書類とか

  • 給与所得の源泉徴収票。最近は Web からのダウンロードになったので、この場合手元でとっておく必要がある書類はない
  • 生命保険、医療保険個人年金保険などの控除証明書。基本的に年末近くにハガキで届くので、捨てることはないと思う
  • 国民年金の納付領収書
  • 国民健康保険や任意継続保険の納付領収書。派遣健保の任意継続保険は再発行の依頼が可能
  • 証券会社から送られる特定口座年間取引報告書。基本的に年明けてから郵送されるので、捨てることはないと思う
  • ふるさと納税の寄付金受領証明書

株式関係

  • 繰越損失は、来年以降使用する残がある限り、毎年提出が必要になるので注意。もし提出し忘れた場合、まだ繰り越せる損失が残っていたとしても、その時点で全てリセットされる
配当所得について
  • 配当所得については、総合課税か申告分離課税かを選ぶ必要がある
  • 所得税

    • (確定申告なし)
    • 総合課税
      • 課税総所得 695 万以下: 10%
      • 課税総所得 900 万以下: 13%
    • 申告分離課税
      • 一律 15% (株と損益通算可能)
  • 住民税

    • 申告不要制度
      • 源泉徴収済みの 5%
      • 保険料の算定対象にならない
    • 総合課税
      • 課税総所得 1000 万以下: 7.2%
      • 保険料の算定対象になる
    • 申告分離課税
      • 一律 5% (株と損益通算可能)
      • 保険料の算定対象になる
  • 住民税に関しては、配当所得への課税は、総合課税で 7.2%、申告分離課税で 5% (+株と損益通算可能)なので、総合課税にするメリットは全くない

  • 基本的には住民税の課税方式は確定申告時の所得税に対する課税方式に合わせられるので、確定申告で総合課税を選ぶと、住民税も自動的に総合課税となる
  • 但し、住民税の通知が来る前に別途申告を行うことで、所得税で選択した課税方式とは別の方式を住民税で適用できるらしい(申告不要制度の利用も含む)
  • 株式売却側で損失がある場合、配当金の課税方法は、申告分離を選択。総合だと株式側との合算ができないので、株式側の損失で埋め合わせることができなくなる
  • 株式売却側で損失がない場合、且つ、課税総所得が 900 万以下の場合は、所得税は総合課税を選んだ方が配当所得に対する税金が低くなる (695 万以下は 10%、900 万以下は 13% の配当所得課税)
  • 但し、住民税では総合課税が 7.2% となるため、申告しない(=住民税も自動的に総合課税)場合に有利となるのは、課税総所得 695 万以下となる (20% > 17.2%)
  • 総合課税、申告分離課税共に、その時の納税額のみでなく、国民健康保険等の支払い金額算出にも影響するため、注意。申告不要制度が適用された時のみ、この算出対象から外れる
  • まとめると、基本的には、確定申告では分離課税を選択した方が、余計な心配や手間がなくなると思う。もし確定申告で総合課税を選択するのであれば、出来れば住民税は申告不要に切り替える申告を別途したい (7.2% -> 5%+公的保険料の算定から除外)。とは言え、恐らく住民税の申告は紙ベースで、情報も少ないと思うので、手間を考えると住民税別途申告もかなりハードルが高いと思われる(別フォーマットの確定申告書を紙で書いて提出するようなもの)。そもそも配当金はもらっても数十万程度なので、それに対する課税額を数%抑えるのに、どこまで手間を掛けるのかという問題がある
  • 総合課税と申告分離課税の差は 2.2% なので、配当金にその率をかけることで住民税への影響を判断することが出来る。配当金が 10 万なら、申告分離課税の方が住民税が 2,200 円低くなる。確定申告時に総合課税選んだ時の還付金額と照らし合わせて、ある程度の判断は可能(+公的保険等の考慮)

参考

社会保険料関係

  • 医療費の総額、自分が窓口で支払った金額、及び支給された高額療養費については、基本的には健保から送られてくる医療費のお知らせに全て記載されているので、そちらを使用できる。ただ、期間が 1 月から 10 月の診療までと全期間ではないので、注意
  • 高額療養費と同様、個人的に入っている医療保険から支給された給付金も補填金として差し引く必要がある。そのため、医療保険の給付に関する書類もとっておく
  • 高額療養費と医療保険からの給付金を考慮すると、10 万円を超えるのは基本的にほとんど発生しないように思える

ふるさと納税関係

郵送料

郵送は定形外郵便物の規格内 (A4 でも 34cm x 24cm)。 ふるさと納税で 6 枚ぐらい受領証あると、定形内最大の封筒に入れて 90g ぐらいなので、ギリギリ 100g までの 140 円。

ただ、封筒が相当パンパンになるので、無理しないで A4 の封筒(角 2)に入れた方が無難。その場合重量は 100g 前後になるので、切手は 100g までの 140 円か、もしくは 150g までの 210 円となる。

Dialogflow API v1, v2 の webhook リクエスト JSON 例

参考

プロパティの簡単な説明

  • [originalDetectIntentRequest] - [payload] - [surface] - [capabilities]: リクエストを送信してきたデバイスの有効機能
  • [originalDetectIntentRequest] - [payload] - [availableSurfaces] - [capabilities]: リクエストを送信してきたデバイスが連携可能な機能 (スマホへの転送など)

Dialogflow の設定

  • Agent 名: HelloDialogflowAPI2
  • Intent 名: TestIntent1
  • Training phrases:
    • I like green
    • I like red
    • I like blue
  • Parameters:
    • Parameter name: color
    • Entity: @sys.color

呼び出し元

  • Device: Simulator
  • Surface: Phone
  • Language: en-us
  • Location: Googleplex, Mountain View, CA 94043, United States

Webhook リクエスト時に送られる JSON

I like *** をキーボードで入力して呼び出し

v1 API

{
  "originalRequest": {
    "source": "google",
    "version": "2",
    "data": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          }
        ]
      },
      "requestType": "SIMULATOR",
      "inputs": [
        {
          "rawInputs": [
            {
              "query": "I like green",
              "inputType": "KEYBOARD"
            }
          ],
          "arguments": [
            {
              "rawText": "I like green",
              "textValue": "I like green",
              "name": "text"
            }
          ],
          "intent": "actions.intent.TEXT"
        }
      ],
      "user": {
        "lastSeen": "2019-02-11T09:04:57Z",
        "locale": "en-US",
        "userId": "ABwppHGQGr22Z3CnjL-7Hnww_VTTiG9h8n-q9aNsdNlm0OnXPmnX5O8mBi0xJtP5GrhYpQ9gCjJh8khWcmBPuMgnwQ"
      },
      "conversation": {
        "conversationId": "ABwppHH85J9F5oe1VxwSp5TvgkfR-W6DT0Rb_vzAWROIahZIFkKoZHghfajfLrnwSAQqNWYCGVWol7fP1pRRVcVp4A",
        "type": "ACTIVE",
        "conversationToken": "[]"
      },
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "id": "baa9ed27-e99b-41b0-9d09-35fe3af565ea",
  "timestamp": "2019-02-11T11:26:09.201Z",
  "lang": "en-us",
  "result": {
    "source": "agent",
    "resolvedQuery": "I like green",
    "speech": "",
    "action": "",
    "actionIncomplete": false,
    "parameters": {
      "color": "green"
    },
    "contexts": [
      {
        "name": "actions_capability_screen_output",
        "parameters": {
          "color": "green",
          "color.original": "green"
        },
        "lifespan": 0
      },
      {
        "name": "actions_capability_audio_output",
        "parameters": {
          "color": "green",
          "color.original": "green"
        },
        "lifespan": 0
      },
      {
        "name": "google_assistant_input_type_keyboard",
        "parameters": {
          "color": "green",
          "color.original": "green"
        },
        "lifespan": 0
      },
      {
        "name": "actions_capability_web_browser",
        "parameters": {
          "color": "green",
          "color.original": "green"
        },
        "lifespan": 0
      },
      {
        "name": "actions_capability_media_response_audio",
        "parameters": {
          "color": "green",
          "color.original": "green"
        },
        "lifespan": 0
      }
    ],
    "metadata": {
      "matchedParameters": [
        {
          "dataType": "@sys.color",
          "name": "color",
          "value": "$color",
          "isList": false
        }
      ],
      "isFallbackIntent": "false",
      "intentName": "TestIntent1",
      "isResponseToSlotfilling": false,
      "intentId": "43e7fce7-6325-4087-968d-6a65ed66b34a",
      "webhookUsed": "true",
      "webhookForSlotFillingUsed": "false",
      "nluResponseTime": 23
    },
    "fulfillment": {
      "speech": "",
      "messages": [
        {
          "type": 0,
          "speech": ""
        }
      ]
    },
    "score": 1.0
  },
  "status": {
    "code": 200,
    "errorType": "success"
  },
  "sessionId": "ABwppHH85J9F5oe1VxwSp5TvgkfR-W6DT0Rb_vzAWROIahZIFkKoZHghfajfLrnwSAQqNWYCGVWol7fP1pRRVcVp4A"
}

v2 API

{
  "responseId": "acb3e31d-6d01-47e0-a862-4724a11049af",
  "queryResult": {
    "queryText": "i like blue",
    "parameters": {
      "color": "blue"
    },
    "allRequiredParamsPresent": true,
    "fulfillmentMessages": [
      {
        "text": {
          "text": [
            ""
          ]
        }
      }
    ],
    "outputContexts": [
      {
        "name": "projects/hellodialogflowapi2/agent/sessions/ABwppHFWb_kxnN18WEBdA9fzj55t2I1kIs88kEAwhj2vY4J0xDAEfw9iU-2t48aI3MSIxVEAIfxNZNt-EJvfUibRfA/contexts/actions_capability_screen_output",
        "parameters": {
          "color": "blue",
          "color.original": "blue"
        }
      },
      {
        "name": "projects/hellodialogflowapi2/agent/sessions/ABwppHFWb_kxnN18WEBdA9fzj55t2I1kIs88kEAwhj2vY4J0xDAEfw9iU-2t48aI3MSIxVEAIfxNZNt-EJvfUibRfA/contexts/actions_capability_audio_output",
        "parameters": {
          "color": "blue",
          "color.original": "blue"
        }
      },
      {
        "name": "projects/hellodialogflowapi2/agent/sessions/ABwppHFWb_kxnN18WEBdA9fzj55t2I1kIs88kEAwhj2vY4J0xDAEfw9iU-2t48aI3MSIxVEAIfxNZNt-EJvfUibRfA/contexts/google_assistant_input_type_keyboard",
        "parameters": {
          "color": "blue",
          "color.original": "blue"
        }
      },
      {
        "name": "projects/hellodialogflowapi2/agent/sessions/ABwppHFWb_kxnN18WEBdA9fzj55t2I1kIs88kEAwhj2vY4J0xDAEfw9iU-2t48aI3MSIxVEAIfxNZNt-EJvfUibRfA/contexts/actions_capability_media_response_audio",
        "parameters": {
          "color": "blue",
          "color.original": "blue"
        }
      },
      {
        "name": "projects/hellodialogflowapi2/agent/sessions/ABwppHFWb_kxnN18WEBdA9fzj55t2I1kIs88kEAwhj2vY4J0xDAEfw9iU-2t48aI3MSIxVEAIfxNZNt-EJvfUibRfA/contexts/actions_capability_web_browser",
        "parameters": {
          "color": "blue",
          "color.original": "blue"
        }
      }
    ],
    "intent": {
      "name": "projects/hellodialogflowapi2/agent/intents/43e7fce7-6325-4087-968d-6a65ed66b34a",
      "displayName": "TestIntent1"
    },
    "intentDetectionConfidence": 1.0,
    "languageCode": "en-us"
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.WEB_BROWSER"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          }
        ]
      },
      "requestType": "SIMULATOR",
      "inputs": [
        {
          "rawInputs": [
            {
              "query": "i like blue",
              "inputType": "KEYBOARD"
            }
          ],
          "arguments": [
            {
              "rawText": "i like blue",
              "textValue": "i like blue",
              "name": "text"
            }
          ],
          "intent": "actions.intent.TEXT"
        }
      ],
      "user": {
        "lastSeen": "2019-02-11T05:33:45Z",
        "locale": "en-US",
        "userId": "ABwppHGQGr22Z3CnjL-7Hnww_VTTiG9h8n-q9aNsdNlm0OnXPmnX5O8mBi0xJtP5GrhYpQ9gCjJh8khWcmBPuMgnwQ"
      },
      "conversation": {
        "conversationId": "ABwppHFWb_kxnN18WEBdA9fzj55t2I1kIs88kEAwhj2vY4J0xDAEfw9iU-2t48aI3MSIxVEAIfxNZNt-EJvfUibRfA",
        "type": "ACTIVE",
        "conversationToken": "[]"
      },
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.WEB_BROWSER"
            },
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            }
          ]
        }
      ]
    }
  },
  "session": "projects/hellodialogflowapi2/agent/sessions/ABwppHFWb_kxnN18WEBdA9fzj55t2I1kIs88kEAwhj2vY4J0xDAEfw9iU-2t48aI3MSIxVEAIfxNZNt-EJvfUibRfA"
}

Actions on Google で位置情報を毎回使用したい場合

Actions on Google では、ユーザーの位置情報や名前を取得して使用することが出来る。 が、無制限に使用できるわけではなく、情報を得たい場合はユーザーに許可を申請し、ユーザーがその許可を承認して初めて取得できる。 つまり、その為だけにユーザーとのやりとりが一度増えてしまう。

更に、この承認は当該やりとりのみ有効なので、次回のやりとりではまた許可の申請から行う必要がある。 ユーザー名やスマートスピーカーの位置情報の場合は基本的に変わることがないので、毎回承認作業が追加で必要になるのが、お互いに非常にネックとなる。

この問題の対応方法としては、下記

  • 毎回ユーザーに許可申請をする
  • インテント内に位置情報 (位置名) を含めて送ってもらうようにする (送ってもらった名称から必要な位置情報にサーバー側で変換できる必要がある)
  • 最初に取得した位置情報を userId に紐づけて保存しておき、次回以降は保存しておいた情報を使用する

参考

UWP アプリ開発メモ

証明書の有効期間が切れて申請用パッケージの作成に失敗する時の、更新方法

  1. VS で当該プロジェクトを開く
  2. [ソリューション エクスプローラー] で当該プロジェクトを右クリックし、プロパティを表示する
  3. [アプリケーション] タブを選択し、[パッケージ マニフェスト] ボタンをクリックする
  4. [パッケージ化] タブをクリックし、[発行者] テキストボックスに表示されている文字列をコピーしておく(後で使用する)
  5. [証明書の選択...] ボタンをクリックする
  6. [証明書の構成] ボタンをクリックし、[テスト証明書の作成...] ボタンをクリックする
  7. [発行者共通名] テキストボックスに、コピーしておいた [発行者] の文字列のうち「CN=」から後ろの文字列を貼り付け、[OK] ボタンをクリックする
  8. 有効期限が更新されていることを確認して、[OK] ボタンをクリックする

補足: 公開中アプリの [発行者共通名] については、Partner Center ダッシュボードで当該アプリを選択し、[App Management] - [App identity] - [Package/Identity/Publisher] の値でも確認できる

「'ValidateAppxPackage' に失敗しました。ファイル '...\Package.StoreAssociation.xml' が見つかりませんでした。」エラーが表示される場合の対処方法

  1. VS で当該プロジェクトを開く
  2. [ソリューション エクスプローラー] で当該プロジェクトを右クリックし、プロパティを表示する
  3. [アプリケーション] タブを選択し、[パッケージ マニフェスト] ボタンをクリックする
  4. [パッケージ化] タブをクリックし、[パッケージ名] テキストボックスに表示されている文字列を控えておく(この画面を表示したまま以降の作業実施でも可)
  5. [ソリューション エクスプローラー] で当該プロジェクトを右クリックし、[ストア] - [アプリケーションをストアと関連付ける] をクリックする
  6. [アプリケーション名を選択] 画面で、[既にパッケージに使用されているアプリケーション名を含める] チェックボックスにチェックを入れ、[既存のアプリケーション名] リストから目的のアプリを選択する。[Windows ストア内のパッケージ ID] 情報が、事前に確認していた [パッケージ名] の ID と合っているか、確認しておく
  7. 本当にこのアプリで合っているかなどの確認が表示されるので、問題なければ [次へ] ボタンをクリックする
  8. [関連付け] ボタンをクリックして、関連付けを行う。"Package.StoreAssociation.xml" ファイルが生成される

Release ビルドでの実行時に System.Reflection.MissingMetadataException が発生する場合の対処方法

Debug ビルドでは問題ないが、Release ビルドで実行した際に以下のようなエラーが発生する場合がある。

System.Reflection.MissingMetadataException
  HResult=0x80131543
  Message=Reflection_InsufficientMetadata_NoHelpAvailable: EETypeRva:0x00022f50. For more information, visit http://go.microsoft.com/fwlink/?LinkId=623485

JsonConvert を使ったリフレクションなどが行われる箇所で発生している可能性があるかも。 この場合、xxx.rd.xml と言う名前のファイルをプロジェクトに追加する必要がある。

ただ、xxx.rd.xml ファイルには発生しているエラーに沿った内容を記述する必要があり、これが結構困難。ここでは自分が解決できた方法を例として載せるが、本当にこれで適切に記述できているかは正直不明。

  1. Release ビルドでは例外がスローされても具体的に何を処理しようとしてエラーとなったかが不明なので、Debug ビルドで例外を起こす必要がある。ただしこのエラーは Release ビルド特有の最適化が影響しており、Debug ビルドでは既定で無効になっているので、これを有効にした上で Debug 実行を行い、例外の詳細を確認する必要がある。
    アプリのプロジェクトのプロパティを表示し、[ビルド] - [.NET ネイティブ ツール チェーンでコンパイルする] チェックボックスにチェックを付けてからビルドする

  2. 今度は例外スロー時に、以下のようにもう少し詳細な情報が出力される

  System.Reflection.MissingMetadataException
    HResult=0x80131543
    Message='Microsoft.CSharp.RuntimeBinder.CSharpGetMemberBinder' is missing metadata. For more 
  information, please visit http://go.microsoft.com/fwlink/?LinkID=392859

上記の Microsoft.CSharp.RuntimeBinder.CSharpGetMemberBinder が問題を解決すべき対象となる。

  1. 上記のように missing metadata とか出たら、完全に xxx.rd.xml が必要なパターンなので、以下の処理を行う
  2. アプリのプロジェクトに xxx.rd.xml みたいなファイルが含まれない場合は、プロジェクトにファイルを追加する。名前は default.rd.xml とかにする。また、ファイルのビルドアクションは "コンテンツ" とする
  3. default.rd.xml の内容を以下のようにする
<?xml version="1.0" encoding="utf-8"?>
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
  <Application>
    <!--<Type Name="Microsoft.CSharp.RuntimeBinder.CSharpGetMemberBinder" Dynamic="Required All" />-->
    <Namespace Name="Microsoft.CSharp.RuntimeBinder" Dynamic="Required All" />
  </Application>
</Directives>

上記では初め Type 指定で Microsoft.CSharp.RuntimeBinder.CSharpGetMemberBinder を指定したのだが、Microsoft.CSharp.RuntimeBinder 名前空間の別の型でまた同じ例外が起きてしまったので、型レベルではなく、名前空間で指定している。自分の場合は上記内容で本例外を回避することが出来た。