AZ204に落ちたので足りないとこメモ

AZ204とは

AZ-204: Developing Solutions for Microsoft Azure

この試験の受験者には、1〜2 年の専門的な開発経験と Microsoft Azure の経験が必要です。さらに、このロールには、Azure でサポートされている言語でのプログラミング能力と、Azure SDK、Azure PowerShell、Azure CLI、データ ストレージ オプション、データ接続、API、アプリの認証と承認、コンピューティングとコンテナのデプロイ、デバッグ、パフォーマンスの調整、および監視に関する能力が必要です。

docs.microsoft.com

Azureの開発者向けの試験となっています。

出題範囲

  • Azure コンピューティングソリューションの開発(20~25%)
  • Azure Storageのための開発(15~20%)
  • Azure Securityの実装(15~20%)
  • Azureソリューションの監視、トラブルシューティング、最適化(10~15%)
  • Azure サービスおよびサードパーティ製サービスへの接続と使用(20~25%)

結果

  • Azure コンピューティングソリューションの開発(20~25%) 要努力
  • Azure Storageのための開発(15~20%) 要努力
  • Azure Securityの実装(15~20%) 普通
  • Azureソリューションの監視、トラブルシューティング、最適化(10~15%) 普通
  • Azure サービスおよびサードパーティ製サービスへの接続と使用(20~25%) 要努力

結果からわかる通り、全然勉強が足りませんでした。
セキュリティ周りは業務で触れる部分も多かったので、何とか点数は取れてる感じでした。

足りないとこ

  • Azure Service Bus、Azure Queue Storage
  • 具体的なコード実装
  • JSON形式?での設定

Azure Service Bus、Azure Queue Storage ここら辺はラーニングパスでしか触らなかったので、全然わかってなかったですね。 コード実装だったり、各種設定周りの名称だったりは、ラーニングパスにこんなのあったか?と試験中は思ってましたが、改めて見返しえてみると、ラーニングパスで説明されていました。
演習のところをコピペで済ませて勉強した気になってた感じですね。
やはり実際にリソースを作って、コードを書かないと身につかないなと思いました。

今後

もう少し深いとこまで勉強してから、再受験しようと思います。ただ、試験料を無駄にしないためにちゃんと準備してから挑もうと思います。

OWASP ZAPでの脆弱性診断の実施

モードの設定

モードは以下の4種類ありますが、常にプロテクトモードで運用してください。
利用者が意図したウェブサイトのみに脆弱性診断を実施するように制限することができます。

  • セーフモード
  • プロテクトモード
  • 標準モード
  • 攻撃モード

コンテキストの設定

コンテキストとは診断対象サイトやURLを登録したものです。コンテキストにURLを登録するとスコープも設定されます。スコープとは診断対象範囲のことです。
プロテクトモードでは脆弱性診断作業を実施するためには、診断対象をコンテキストおよびスコープに登録する必要があります。

コンテキストの登録方法は、OWASP ZAPを起動した状態でFirefoxで診断対象のサイトを表示します。そうするとサイトツリーにサイトのURLが表示されます。 登録対象サイトやURLを右クリックして「コンテキストに含める」の「New Context」をクリックします。 f:id:kopai:20201202163754p:plain

「セッション・プロパティ」ダイアログが表示されたら、右側の「Regex」を確認して「OK」をクリックします。

f:id:kopai:20201202163832p:plain

以上でコンテキストへの登録は完了です。

診断対象の調査/確定

OWASP ZAPには「スパイダー(Spider)」という診断対象を自動的に調査するツールが標準で実装されています。
一般的に「クローリング」や「スクレイピング」と呼ばれる手法で、対象となるウェブサイトのディレクトリやファイルを収集します。

スパイダーを実行する前に設定すべき項目があります。メニューの「ツール」→「オプション」でオプションダイアログを開き、「スパイダー」を選択してください。
スパイダーで重要なのは「クロールする最大の深さ」および「並列スキャンスレッド数」です。
スパイダーの推奨設定は以下の通りです。
f:id:kopai:20201202164019p:plain

スパイダーの実行手順は以下の通りです。

  • 診断対象ウェブサイトをコンテキストに登録します
  • サイトツリーで診断対象ウェブサイトを右クリックし、「攻撃」→「スパイダー」を選択します
  • 表示された「スパイダー」ダイアログで、「開始位置」および「コンテキスト」が正しく設定されていることを確認します
  • 再帰的」にチェックを入れます
  • 「詳細オプションを表示」にチェックを入れ、「詳細オプション」タブでクロールする最大の深さが設定通りになっているか確認します
  • 「スキャン開始」をクリックしてスパイダーを実行します。

実行後にアラートタブを表示すると、静的スキャンの結果を確認することができます。アラートタブがない場合はタブの+ボタンで追加できます。 f:id:kopai:20201202164205p:plain

また、サイトツリーにはクローリングした結果のURL等が増えています。
次に紹介する「動的スキャン」はここで取得した結果に対して行われることになります。

スパイダーを使った自動クローリングの機能はありますが、クローリングは手動で実施したほうがより正確なものになります。クローリングの基本はすべてのページを閲覧することです。
OWASP ZAPはクローリング時に各ページのパラメータも記憶し、そのパラメータが診断対象となるので、すべてのパラメータが出現するように意識して巡回する必要があります。

脆弱性診断の実施

OWASP ZAPには「動的スキャン」という自動的に脆弱性診断を実行するツールが標準で実装されています。
動的スキャンでは実際にSQLインジェクション攻撃や悪意のあるパラメータを送るなどして、脆弱性診断を行います。
実際の攻撃であり、データベースやウェブサイトを破壊してしまう可能性があるので、基本的には本番環境での動的スキャンは実施しないほうがいいです。

動的スキャンを実行するためには「スキャンポリシー」を設定する必要があります。
スキャンポリシーを作成するには、メニューの「ポリシー」で「スキャンポリシー」を選択します。
推奨設定は以下の通りです。
f:id:kopai:20201202164429p:plain

動的スキャンの実行手順は以下の通りです。

  • 診断対象ウェブサイトをコンテキストに登録します
  • クローリングを行います
  • サイトツリーで診断対象ウェブサイトを右クリックし、「攻撃」→「動的スキャン」を選択します
  • 表示された「動的スキャン」ダイアログで、「開始位置」および「コンテキスト」が正しく設定されていることを確認します
  • 再帰的」にチェックを入れます
  • 「詳細オプションを表示」にチェックを入れ、「ポリシー」タブの「ポリシー」プルダウンリストで作成したスキャンポリシーを選択します
  • 「スキャン開始」をクリックして動的スキャンを実行します
  • 動的スキャンの進捗率は動的スキャンタブの上にあるプログレスバーに表示されます

まとめ

OWASP ZAPの脆弱性診断を実施する方法のメモです。
OWASP ZAPの各パラメータはあくまでも例ですので、過信しすぎないようにしてください。
OWASP ZAPのモードを「プロテクトモード」にし、脆弱性診断の対象サイト(URL)を正しくコンテキストに登録することで、誤って自分の管轄外のウェブサイトに攻撃をしかけることはないです。
ここさえ間違えなければ、他人に危害を加えることはないと思います。

OWASP ZAPの導入方法

必要なアプリケーションの導入

必要なツールは以下の通りです。

Java

OWASP ZAPを実行するにはJavaのバージョン8以降が必要です。まずJavaがインストールされているか、どのバージョンかを確認します。コマンドプロンプトから「java -version」というコマンドを実行します。「64-Bit」と表示されていればインストールされています。
64ビットWindowsなのに32ビット版がインストールされている場合や上記のコマンドがエラーになる場合は以下のURLから「Windowsオフライン」をダウンロードし、インストールしてください。

https://java.com/ja/download/manual.jsp

OWASP ZAP

次のリンクにアクセスします。

https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project

Download OWASP ZAPに移動します。対象OSのインストーラーをダウンロードします。
インストーラーを起動します。基本的に全て「次へ」で進めて大丈夫です。
「ライセンス契約」ダイアログでは「承認する」を選択して「次へ」をクリックします。
インストール形式は「標準インストール」を選択します。
以上でOWASP ZAPのインストールは完了です。

Firefox

https://www.mozilla.org/ja/firefox/new/

脆弱性診断を実施する場合、ウェブサイトの閲覧にブラウザの制約がなければ、Mozilla Firefoxを使用することをお勧めします。以下はお勧めの理由です。

  • プロキシ設定がOSから独立している
  • 暗号化通信用のSSL/TLS証明書の設定がOSから独立している
  • クロスサイト・スクリプティング脆弱性をブラウザ上で検証しやすい

OWASP ZAPとFirefoxの連携

FirefoxがOWASP ZAP経由でウェブサイトにアクセスできるように設定します。

OWASP ZAPの設定

ブラウザと連携させるためには、OWASP ZAPのホスト(IPアドレス)とポートの設定が必須です。
OWASP ZAPを起動すると次のダイアログが表示されます。ここでは「現在のタイムスタンプでファイル名をつけてセッション保存」を選択して「開始」をクリックしてください。
この設定は毎回ログを日時のファイル名で保存するものになります。
起動したら、「ツール」メニューから「オプション」をクリックし、ダイアログの左側のリスト内の「ローカル・プロキシ」を選択します。
一番上の「Address」欄に「127.0.0.1」を手入力するか、プルダウンリストで選択します。
「ポート」欄にはOS上で使われていない、任意のポートを指定します。
ポートの例には「8080」とありますが、既に使用されている確率が高いです。OWASP ZAPでは使用しないことを強く推奨します。推奨するポートの範囲は49152~61000です。今回は「58888」とします。

Firefoxの設定

Firefoxを起動し、画面右上「三」アイコンをクリックして「オプション」をクリックします。
左側のメニューで「一般」を選択し、右側のペインを一番下までスクロールさせます。「ネットワーク設定」の「接続設定…」をクリックします。
インターネット接続」の画面が表示されます。以下の設定をします。

  • 「インターネット接続に使用するプロキシの設定」で「手動プロキシを設定する」を選択します。
  • 「HTTPプロキシー」にOWASP ZAPに設定したのと同じホスト名またはIPアドレスを入力します。「127.0.0.1」としてください
  • 「ポート」にOWASP ZAPに設定したのと同じポートを入力します。例と同じ設定にしている場合は「58888」としてください
  • 「プロキシーなしで接続」に入力されている文字列はすべて削除してください。 設定や入力が完了したら「OK」をクリックしてオプションを閉じます。

暗号化通信用設定

メニューの「ツール」→「オプション」から「ダイナミックSSL証明書」を選択します。
「保存」をクリックして「owasp_zap_root_ca.cer」を任意の場所に保存します。正常に保存されたらOWASP ZAP側の作業は終了です。

保存したOWASP ZAPの証明書をFirefoxにインポートします。Firefoxを起動し、画面右上「三」アイコンをクリックして「オプション」をクリックします。
左側のメニューで「プライバシーとセキュリティ」を選択し、右側のペインを一番下までスクロールさせて「証明書」の「証明書を表示…」をクリックします。
証明書マネージャーが表示されたら、「認証局証明書」タブを選択して「インポート…」をクリックします。
保存したOWASP ZAPのダイナミックSSL証明書を選択して「開く」をクリックします。
「証明書のインポート」ダイアログが表示されるので、「この認証によるウェブサイトの識別を信頼する」にチェックを入れて「OK」をクリックします。
以上で設定は完了です。

まとめ

OWASP ZAPの導入方法メモです。
これで、OWASP ZAPを利用できるところまで設定できます。

Angularチュートリアルをやってみて -3-

チュートリアルを一通りやってみたが、よく分からなかった。 理解を深めるために、やった内容を書き出してみる。
今回はサービスについて復習する。

サービス

そもそも「サービス」とはなんなのか。チュートリアルでは、下記のように説明されている。

サービスは、お互いを知らない クラスの間で情報を共有する最適な方法です。

Angularではコンポーネントでデータの取得や更新をすべきではないので、データの取得や更新を行う「サービス」を使うらしい。
サービスを作成してみる。

ng generate service message

作成された雛型はこうなった。

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MessageService {

  constructor() { }
}

ここで、@Injectableが大事そう。チュートリアルでは、下記のように説明されている。

クラスを依存関係注入システムに参加するものとしてマークします。

意味が分からないので、調べてみると下記のように説明されていた。

サービスクラスであることの条件は、@Injectableデコレーターで修飾されていることです。@Injectableデコレーターを付与することで、クラスをサービスとしてコンポーネントに引き渡せるようになります。

つまり、@Injectableがあれば「サービス」になると解釈することができる。@InjectableprovidedInがあり、'root'の場合は、ルートでのインスタンスとなる。

ボタン「メッセージ」で「msg1」、ボタン「メッセージ2」で「msg2」をテキストボックスに入力した文字列に追加する機能を作成する。

//message.service.ts
import { Injectable} from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  private message: string = "";

  public add(intext: string): void{
    this.message = intext + "_msg1";
  }

  public add2(intext: string): void{
    this.message = intext + "_msg2";
  }

  public get(): string{
    return this.message
  }
  constructor() { }
}

add()により「msg1」を追加し、add2()により「msg2」を追加する。get()は表示のためにmessageを返すメソッドとなっている。
呼び出すコンポーネントは次のように作成した。

//app.component.ts
import { Component, OnInit } from '@angular/core';
import { MessageService } from './message.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  public text: string = "";
  public outtext: string = "";

  constructor(private mService: MessageService) { }

  ngOnInit(): void{
  }

  public show(){
    this.mService.add(this.text);
    this.outtext = this.mService.get();
  }

  public show2(){
    this.mService.add2(this.text);
    this.outtext = this.mService.get();
  }
}
<!--app.component.html-->
<input type="text" [(ngModel)]="text" />
<br>
<label>{{text}}</label>
<br>
<input type="button" (click)="show()" value="メッセージ" /><br>
<input type="button" (click)="show2()" value="メッセージ2" /><br>
<label>{{outtext}}</label>

HTMLでは2つのボタンを設定し、それぞれshow()show2()をクリック時に呼び出す。これで、ボタンを押すとテキストボックスの入力値に対して、文字列を追加する。
f:id:kopai:20190218115956p:plain
これを変更して、ボタンを押すたびに増えていくように変更する。

//message.service.ts
import { Injectable} from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MessageService {
  private message: string = "";

  public add(intext: string): void{
    this.message = this.message + intext + "_msg1";
  }

  public add2(intext: string): void{
    this.message = this.message + intext + "_msg2";
  }

  public get(): string{
    return this.message
  }
  constructor() { }
}

サービスの内容を変更した。サービスの変数に対して、テキストボックスの入力内容を追加していくようにした。
次に、このサービスを2つのコンポーネントから利用する。コードを以下のように変更した。

<!--app.component.html-->
<app-a-component></app-a-component>
<app-b-component></app-b-component>
//a-component.component.ts
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';

@Component({
  selector: 'app-a-component',
  templateUrl: './a-component.component.html',
  styleUrls: ['./a-component.component.css']
})
export class AComponentComponent implements OnInit {

  constructor(private mService : MessageService) { }
  public text: string= "";
  public outtext: string="";

  ngOnInit() {
  }

  public show(){
    this.mService.add(this.text);
    this.outtext = this.mService.get();
  }

  public show2(){
    this.mService.add2(this.text);
    this.outtext = this.mService.get();
  }
}
<p>
  a-component works!
</p>

<input type="text" [(ngModel)]="text" />
<br>
<label>{{text}}</label>
<br>
<input type="button" (click)="show()" value="メッセージ" /><br>
<input type="button" (click)="show2()" value="メッセージ2" /><br>
<label>{{outtext}}</label>
//b-component.component.ts
import { Component, OnInit } from '@angular/core';
import { MessageService } from '../message.service';

@Component({
  selector: 'app-b-component',
  templateUrl: './b-component.component.html',
  styleUrls: ['./b-component.component.css']
})
export class BComponentComponent implements OnInit {

  constructor(private mService : MessageService) { }
  public text: string= "";
  public outtext: string="";

  ngOnInit() {
  }

  public show(){
    this.mService.add(this.text);
    this.outtext = this.mService.get();
  }

  public show2(){
    this.mService.add2(this.text);
    this.outtext = this.mService.get();
  }
}
<p>
  b-component works!
</p>

<input type="text" [(ngModel)]="text" />
<br>
<label>{{text}}</label>
<br>
<input type="button" (click)="show()" value="メッセージ" /><br>
<input type="button" (click)="show2()" value="メッセージ2" /><br>
<label>{{outtext}}</label>

aとbの2つのコンポーネントを用意し、それぞれにボタンを配置した。実行すると、出力される文字列はaとbで共有されていることが分かる。これは、サービスの宣言部分で、

@Injectable({
  providedIn: 'root'
})

上記のように宣言しているためである。この宣言では、'root'つまり、最上位での宣言を行っているため、aとbのそれぞれでサービスを作成していることにはなっていない。
これをaとbで独立したサービスとするためには、次のように宣言する。

//a-component.component.ts
@Component({
  selector: 'app-a-component',
  templateUrl: './a-component.component.html',
  styleUrls: ['./a-component.component.css'],
  providers: [MessageService]
})
//b-component.component.ts
@Component({
  selector: 'app-b-component',
  templateUrl: './b-component.component.html',
  styleUrls: ['./b-component.component.css'],
  providers: [MessageService]
})

これにより、別々の独立したサービスとすることができる。

Angularチュートリアルをやってみて -2-

チュートリアルを一通りやってみたが、よく分からなかった。 理解を深めるために、やった内容を書き出してみる。
今回はデータバインディングの基本を復習する。

データバインディング

データバインディングは、コンポーネントとテンプレート(ビュー)を紐付ける仕組み。Angularのデータバインディング構文は、データの流れる方向によって、大きく4種類に分類できる。

No. データ方向 種類 記法
1 コンポーネント=>ビュー Interpolation(補間 {{...}}
2 コンポーネント=>ビュー プロパティ/属性バインディング [property]="Value"
3 ビュー=>コンポーネント イベントバインディング (event)="handler"
4 コンポーネント<=>ビュー 双方向バインディング [(target)]="Value"

チュートリアル内のヒーローエディタの章で使ったのは、1と4である。違いとしては、双方向でデータのやり取りができるかどうかである。双方向バインディングでは、コンポーネントのプロパティとテキストボックスを同期させることで、テキストボックスの更新により、プロパティ値に反映させることができる。

Interpolation(補間)構文

最も初歩的なバインド記法であるInterpolation(補間)は、 {{...}} で表現される。
サンプルコード

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>`,//--------------------2
})

export class AppComponent {
  name = 'Angular'//----------------------------------------1
}

1 で定義されたnameプロパティを 2{{name}}として参照している。

{{...}}式の注意点

1.利用できない演算子がある

主に副作用を伴う演算子は、{{...}}式では禁止されている。

2.グローバル名前空間のオブジェクトを参照できない

{{...}}式のコンテキストは、コンポーネントインスタンスである。window、Mathのようなグローバル名前空間上のオブジェクトに{{...}}式からアクセスすることはできない。

3.{{...}}式を利用する上でのガイドライン

  • 式が副作用を伴わないこと(=他の値に影響がでないこと)
  • 短時間で実行できること
  • シンプルであること
  • 同じ操作を何度実行しても同じ結果を返すこと

Angularでは、{{...}}式を何度も評価する。時間のかかる式は、そのままアプリの速度低下につながる可能性があるので、注意が必要。

「?.」演算子

{{...}}式だけで利用できる演算子に「?.」演算子がある。これはオブジェクトが空であるか判断し、空でない場合にだけプロパティにアクセスする。

双方向バインディング

コンポーネントクラスから画面へ、そして画面からコンポーネントクラスへのデータフローを作る。 <input>フォーム要素とプロパティとの間に双方向データバインディングの設定をする。サンプルコードは以下のようになる。

<!--app.component.html-->
<input type="text" [(ngModel)]="text" />

<label>{{text}}</label>
//app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
    public text: string = "hoge";
}

[(ngModel)]は双方向データバインディング構文。
ngModelはデフォルトでは使用できない。理由は、オプションのFormsModuleに属しており、使用するにはモジュールをオプトインする必要がある。

//app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';// <---追加

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule// <---追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

まとめ

[(ngModel)]を使い、双方向データバインディングをすると、データのやり取りできる。単純に参照するだけなら、{{...}}の形で書けばよいのではないか。

Angularチュートリアルをやってみて -1-

チュートリアルを一通りやってみたが、よく分からなかった。 理解を深めるために、やった内容を書き出してみる。
チュートリアルのアプリケーションシェルに該当する部分をまとめた。

新しいワークスペースと初期アプリケーションリンクを作成する

コマンドにより、プロジェクトを作成

ng new angular-test

アプリケーションの起動

コマンドにより、サーバーを起動

ng serve

オプションにより、ブラウザで開くことも可能

ng serve --open

ng serveコマンドはアプリケーションをビルド、開発用サーバーを起動し、ソースファイルを監視します。 あなたが監視されているファイルに変更を行ったときには、変更があったファイルに対し再ビルドを行います。 --openフラグを指定すると、http://localhost:4200がブラウザで開かれます。

Angularのコンポーネント

コンポーネントは、ページを構成するUI部品のこと。
app.component.tsは、アプリで最初に呼び出されるコンポーネントであり、 これをルートコンポーネント(メインコンポーネント)と呼ぶ。

//app.component.ts
//コンポーネントを定義するために必要となるオブジェクトをインポート
//@1
import { Component } from '@angular/core';

//コンポーネントの構成情報を宣言
//@2
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

//コンポーネントの本体
//@3
export class AppComponent {
  title = 'Tour of Heroes';
}

1. コンポーネントを定義するために必要となるオブジェクトをインポート

2. コンポーネントの構成情報を宣言

パラメーター名 概要
selector コンポーネントを適用すべき要素を表すセレクター式
templateUrl コンポーネントに適用するビュー(テンプレート)

3. コンポーネントの本体

コンポーネント側でデータ(ビュー変数)を用意しておき、 テンプレート側でデータを埋め込む場所や表示方法を定義する。
外部から参照できるようにexportキーワードで修飾する

コンポーネントのテンプレートファイル

<!--app.component.html-->
<h1>{{title}}</h1>

二重の括弧はAngularの補間バインディングの構文。
この補間バインディングコンポーネントのtitleプロパティの値をHTMLタグに渡す。

まとめ

テスト公開

Markdownの練習

  • リスト
    • リスト1
    • リスト2
* リスト
  * リスト1
  * リスト2

改行の練習
改行された。

改行

引用

Markdown(マークダウン)は、文書を記述するための軽量マークアップ言語のひとつである。本来はプレーンテキスト形式で手軽に書いた文書からHTMLを生成するために開発されたものである。しかし、現在ではHTMLのほかパワーポイント形式やLATEX形式のファイルへ変換するソフトウェア(コンバータ)も開発されている。各コンバータの開発者によって多様な拡張が施されるため、各種の方言が存在する。

>引用
>>引用

太字
斜体

**太字**
*斜体*
Left align Right align Center align
センター
| Left align | Right align | Center align |
|:-----------|-------------:|:-------------:|
|右|左|センター|

シンタックスハイライト確認

<script>
    function gate(){
        var UserInput = prompt("パスワードを入力して下さい:","");
        // 入力内容をチェック
        if( /\W+/g.test(UserInput) ) {
            // 半角英数字以外の文字が存在したらエラー
            alert("半角英数字のみを入力して下さい。");
            }
        // キャンセルをチェック
        else if( UserInput != null ) {
            // 入力内容からファイル名を生成して移動
            location.href = UserInput + ".html";
            }
    }
 </script>

良く使いそうなMarkdown記法を列挙してみました。