質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.07%

SSL認証の検出プログラムを作成したが、例外が発生してしまった

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 161

study_111

score 58

前提・実現したいこと

SSL認証がされているサイトであるかどうかを検出する関数を作成してみたのですが、処理の過程で、例外が発生してしまいました。その為、その原因を特定し、改善したいです。

発生している問題

「input_url」にはhttps化されていないサイトのURL名を代入しましたのですが、その結果、コンソールには、「接続が拒否されました」と表示されました。
該当の例外は、「OSError」であるかと思いますが、公式リファレンスを参照してみたものの、原因を突き止められませんでした。

該当のソースコード

#SSL通信の検証
def get_server_certificate(hostname):
    context = ssl.create_default_context()
    # print('contextは ', dir(context))
    # exit()
    """
    address ((host, port) ペア) で listen しているTCPサービスに接続し、
    ソケットオブジェクトを返す
    """
    try:
        with socket.create_connection((hostname, 443)) as sock:
            """
            元のソケット通信の機能があるクラス(sock)を内包し、
            SSLを利用して暗号化通信をソケットを通じて行う機能のクラス(ssl.SSLSocke)
            がラッパークラス
            """
            with context.wrap_socket(sock, server_hostname=hostname) as sslsock:
                #証明書を取得(Falseの場合はdictで取得、Trueの場合はバイナリ形式で取得)
                der_cert = sslsock.getpeercert(True)
                sslsock.match_hostname(der_cert,hostname)
    except OSError:
        print('接続が拒否されました')
    except ssl.CertificateError:
        print('ホスト名とSSL証明書のコモンネームが一致しません')
    except ssl.SSLCertVerificationError:
        print('SSL証明書が有効ではありません。')
        print('TLSサーバー証明書の検証に失敗しました。')

    except socket.timeout:
        print('接続タイムアウトエラー:SSL接続が確認出来ませんでした')


以下は呼び出す際のコードになります。

get_server_certificate(input_url)

試したこと

エラー内容の検索、公式リファレンスの参照を行いました。

どなたか、こちらの現象解決の為、ご助言頂けましたら幸いです。

※追記です

OSErrorの例外をキャッチしている部分を、以下の形にし、エラーの詳細を表示させてみました。

except OSError as e:
        print('接続が拒否されました',e)


結果の方は、コンソールにて、以下のように表示されました

nodename nor servname provided, or not known

追記2

以下は、修正したSSL検証の関数になります。

#SSL通信の検証
def get_server_certificate(hostname):
    # socket.gethostbyname('hostname')
    context = ssl.create_default_context()

    # print('contextは ', dir(context))
    # exit()

    """
    address ((host, port) ペア) で listen しているTCPサービスに接続し、
    ソケットオブジェクトを返す
    """
    try:
        with socket.create_connection((hostname, 443)) as sock:
            print('コネクション成功')
            """
            元のソケット通信の機能があるクラス(sock)を内包し、
            SSLを利用して暗号化通信をソケットを通じて行う機能のクラス(ssl.SSLSocke)
            がラッパークラス
            """
            with context.wrap_socket(sock, server_hostname=hostname) as sslsock:
                #証明書を取得(Falseの場合はdictで取得、Trueの場合はバイナリ形式で取得)
                der_cert = sslsock.getpeercert(False)
                print(der_cert)
                print(ssl.match_hostname(der_cert,hostname))

    except ssl.CertificateError:
        print('ホスト名とSSL証明書のコモンネームが一致しません')
    except ssl.SSLCertVerificationError:
        print('SSL証明書が有効ではありません。')
        print('TLSサーバー証明書の検証に失敗しました。')

    except socket.timeout:
        print('接続タイムアウトエラー:SSL接続が確認出来ませんでした')
    except OSError as e:
        print('接続が拒否されました',e)
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • CHERRY

    2020/06/19 12:33 編集

    SSL 化がされてない場合、Webサーバー側は、443 ポートで待ち受けしていないので、通常は接続できないとおもいます。

    ポートに接続できないために例外が発生していると思うのですが、どのような動作をすることを期待しているのでしょうか?

    キャンセル

  • study_111

    2020/06/19 12:57 編集

    ご指摘ありがとうございます。
    >ポートに接続できないために例外が発生していると思うのですが、どのような動作をすることを期待しているのでしょうか?
    こちらですが、レンタルサーバーなどで、サーバー自体は証明書を所持しているものの、特定のサイトはssl証明書を実装していなかった場合や、証明書は所持しているものの、その証明書に該当サイトのURLがコモンネームに登録されていなかった場合なども考慮し、プログラムを組んでおりました。

    キャンセル

  • study_111

    2020/06/19 13:02 編集

    > SSL 化がされてない場合、Webサーバー側は、443 ポートで待ち受けしていないので、通常は接続できないとおもいます。
    こちらなのですが、そうなりますと、該当サイトの使用しているサーバー自体が、SSL証明書を実装していないということが想定されますでしょうか?

    キャンセル

回答 1

checkベストアンサー

0

def get_server_certificate(hostname): と、「ホスト名」を引数に取る ( その後そのホスト名を socket.create_connection に渡す ) 仕様で作っているところ、
get_server_certificate(input_url) と、URLを渡しているのはミスマッチではないでしょうか。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/06/20 09:44

    ついでですが、https://docs.python.org/ja/3/library/ssl.html を見るに、
    sslsock.getpeercert(True) だと、後の match_hostname() に困りそうなので、False か引数省略がよさそう、というのと、sslsock が match_hostname できるわけではないので、ssl.match_hostname() に書き換え、でしょうか。

    キャンセル

  • 2020/06/20 20:35 編集

    ご親切にご助言頂きまして、ありがとうございます。
    頂きましたご助言を元に書き直した関数を、本文に追記致しました。
    呼び出す際は、get_server_certificate('gundamsblog.net')として、ホスト名のみ入力するように致しました。
    しかし、ここで意図しない挙動となってしまったのですが、「print(ssl.match_hostname(der_cert,hostname))」この結果は、「None」となってしまいました。該当のホストは、アクセスするとアドレスバーに「保護されていない通信」と表示されています。
    そうだとした場合、例外が発生するかと思うのですが、何故、例外が発生しないかが分からない状況です...要因としましては、どんなことが考えられますでしょうか?

    キャンセル

  • 2020/06/20 22:59 編集

    度々、すみません。もしかしたら、「https://teratail.com/questions/271585#reply-387724」こちらの質問で、原因がわかったかもしれません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.07%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る