Docker版OWASP ZAPでForm based authenticationを実現する

<h1> 始めに

OWASP ZAPはWebアプリケーションの脆弱性診断ができるOSSです。
デスクトップアプリケーションとしての色が強く、ドキュメントはデスクトップ版のものが多く散見されます。

一方Docker版もリリースされており、特にCI/CD環境で簡単にZAPを実行することで開発フェーズの中で脆弱性診断を自動化することができます。

今回診断したいサイトにログイン認証が必要となり、Desktop版のcontextファイルを持ってくることでDocker版でform based authenticationを実現しました。

<h1> Desktop版でログインする

Desktop版を利用可能な方はできる限りDesktop版で一度設定したほうが良いです。
GUI上でログインして、Flag as contextで自動的にパラメータを設定できます。
参考サイト：

https://chigusa-web.com/blog/owasp-zap-login/

https://www.zaproxy.org/docs/desktop/start/features/authentication/

<h1> コンテキストを設定する

Desktop版を利用できる方は、上記で設定したコンテキストファイルをエクスポートできます。
コンテキストファイルは例として以下になります。

```xml:sample.context
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration>
    <context>
        <name>sample</name>
        <desc/>
        <inscope>true</inscope>
        <incregexes>https://xyz/.*</incregexes>
        <excregexes>https://xyz/ignore/.*</excregexes>
        <tech>
            <include>Db</include>
            <include>Db.CouchDB</include>
            <include>Db.Firebird</include>
            <include>Db.HypersonicSQL</include>
            <include>Db.IBM DB2</include>
            <include>Db.Microsoft Access</include>
            <include>Db.Microsoft SQL Server</include>
            <include>Db.MongoDB</include>
            <include>Db.MySQL</include>
            <include>Db.Oracle</include>
            <include>Db.PostgreSQL</include>
            <include>Db.SAP MaxDB</include>
            <include>Db.SQLite</include>
            <include>Db.Sybase</include>
            <include>Language</include>
            <include>Language.ASP</include>
            <include>Language.C</include>
            <include>Language.JSP/Servlet</include>
            <include>Language.Java</include>
            <include>Language.Java.Spring</include>
            <include>Language.JavaScript</include>
            <include>Language.PHP</include>
            <include>Language.Python</include>
            <include>Language.Ruby</include>
            <include>Language.XML</include>
            <include>OS</include>
            <include>OS.Linux</include>
            <include>OS.MacOS</include>
            <include>OS.Windows</include>
            <include>SCM</include>
            <include>SCM.Git</include>
            <include>SCM.SVN</include>
            <include>WS</include>
            <include>WS.Apache</include>
            <include>WS.IIS</include>
            <include>WS.Tomcat</include>
        </tech>
        <urlparser>
            <class>org.zaproxy.zap.model.StandardParameterParser</class>
            <config>{"kvps":"&amp;","kvs":"=","struct":[]}</config>
        </urlparser>
        <postparser>
            <class>org.zaproxy.zap.model.StandardParameterParser</class>
            <config>{"kvps":"&amp;","kvs":"=","struct":[]}</config>
        </postparser>
        <authentication>
            <type>2</type>
            <strategy>EACH_RESP</strategy>
            <pollurl/>
            <polldata/>
            <pollheaders/>
            <pollfreq>60</pollfreq>
            <pollunits>REQUESTS</pollunits>
            <loggedin>\Qlogin\E</loggedin>
            <form>
                <loginurl>https://xyz/login/</loginurl>
                <loginbody>uesrName={%username%}&amp;password={%password%}</loginbody>
                <loginpageurl>https://xyz/login/</loginpageurl>
            </form>
        </authentication>
        <users>
            <user>1;true;c2FtcGxlVXNlcg==;2;c2FtcGxlQHNhbXBsZS5jb20=~c2FtcGxl~</user>
        </users>
        <forceduser>1</forceduser>
        <session>
            <type>0</type>
        </session>
        <authorization>
            <type>0</type>
            <basic>
                <header/>
                <body/>
                <logic>AND</logic>
                <code>-1</code>
            </basic>
        </authorization>
    </context>
</configuration>

```

contextファイルの中で確認するべき点は、以下です。
`name` : コンテキスト名を指定します。
`incregexes` : 診断に含めるURLを指定します。正規表現使用可能
`excregexes` : 診断に含めないURLを指定します。正規表現使用可能

`authentication.type` : 認証タイプを選択します。今回はForm based authenticationなので2を選択。
`authentication.loggedin` : ログインしたときに表示されるパラメータを指定する。これをもとにログインの可否を判断している
`authentication.strategy` : 上記のパラメータをどのタイミングで判定するかを指定。`EACH_RESP, EACH_REQ, EACH_REQ_RESP`のいずれかを指定する
`authentication.form.loginurl` : ログイン時にPOSTする先のURL。
`authentication.form.loginbody` : ログイン時にPOSTするデータ。
例えば `uesrName={%username%}&amp;password={%password%}` を指定する。`{%username%}`と`{%password%}`には`users.user`で指定した`username`と`password`が自動補完される。
`authentication.form.loginpageurl` : ログインフォームのあるURL。

`users.user` :
`index;enable;設定名;認証方法(Form based authenticationは2);ユーザーネーム~パスワード~`の順に記載。なお設定名、ユーザーネーム、パスワードはbase64エンコードが必要。
例えば`username=sample@sample.com, password=sample, 設定名:sampleUser`とすると内容は以下のようになる。
```
1;true;c2FtcGxlVXNlcg==;2;c2FtcGxlQHNhbXBsZS5jb20=~c2FtcGxl~
```
`forceduser` :  自動でログインするときのユーザーindexを指定する。
`session.type` : `0:Cookie based`, `1:http session based`, `2:script based`の中から選択。
ログインしているという判定をcookieとして持つ場合0, HTTPレスポンスに含める場合は1, それ以外の場合は2としてスクリプトを書く必要がある。

<h3> Formのパラメータを確認する

Desktop版が利用できない方は、まず診断したいサイトのログインフォームでソースを確認し、
・POSTで送信する先のパス
・ユーザーネームのname属性
・パスワードのname属性
を確認します。

```login.html
<form id="loginForm" name="loginForm" action="/login/" method="post">

                    <table>
                        <tr>
                            <th>
                                <div>ユーザーネーム</div>
                            </th>
                            <td>
                                <input type="text" name="userName" id="userName">
                            </td>
                        </tr>
                        <tr>
                            <th>
                                <div>パスワード</div>
                            </th>
                            <td>
                                <input type="password" name="password" id="password">
                            </td>
                        </tr>
                    </table>
```
例として上記のようなフォームの場合、
POST先URL：`/login/`
Username: `userName`
password: `password`
が該当します。

ゆえに、
```xml
            <form>
                <loginurl>https://xyz/login/</loginurl>
                <loginbody>uesrName={%username%}&amp;password={%password%}</loginbody>
                <loginpageurl>https://xyz/login/</loginpageurl>
            </form>
```
とcontextを修正すればOK。

<h1> Docker版ZAPでコンテキストを使用する

実行フォルダにcontextファイルを置いておきボリュームマウントする。
`-n`オプションでコンテキストファイルを指定できるのでマウントしたファイルを指定。
`-U`オプションで上記で作成したユーザーの設定名を指定するとそのユーザーでログインしてくれる。 
```sh
sudo docker run -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable zap-full-scan.py \
       -t "https://xyz/" -r report.html \
       -n /zap/wrk/sample.context -U sampleUser
```

これでサイトにログインしながら脆弱性診断を行うことができました。

<h1> 終わりに

OWASP ZAPを理解し慣れるにはDesktop版を触ってみて、それからDockerで自動化するという方法をとるのが一番よいと思います。
ただ、環境によりDesktop版を触れない場合（組織の設定でプロキシを変更できない場合やGUI環境がない場合）はいきなりcontextファイルを弄るのも手かなと。。