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

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

新規登録して質問してみよう
ただいま回答率
85.48%
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

SSL

SSL(Secure Sockets Layer)とは、暗号化されたプロトコルで、インターネット上での通信セキュリティを提供しています。

Q&A

解決済

2回答

1616閲覧

SSL証明書の有無を取得したいが「証明書が無効」というエラー.証明書の有効性を判定しないようにする方法を知りたい.

harunoyo

総合スコア1

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

SSL

SSL(Secure Sockets Layer)とは、暗号化されたプロトコルで、インターネット上での通信セキュリティを提供しています。

0グッド

0クリップ

投稿2022/12/06 13:59

編集2022/12/07 05:19

前提

プログラミング初心者です.
pythonで主にrequestライブラリ,BeautifulSoupライブラリを用いてWEBスクレイピングを作っています.
その中で,SSL証明書の有無を取得しようとしていますが,うまくいっていません.

質問

・下の方法で証明書の有無を確認することは可能か
・もし可能なのであれば,(証明書が無効であるURLを入力したときにNoneと返してもらいたいので)証明書の有効性を確かめないようにしたらいいのではと考えているので,それを実現するにはどのようにコードを修正すればよいか教えていただきたいです.

現状やったこと・コード内容・実行結果について

・getpeercert()を用いて証明書の有無の確認を試みました.公式ドキュメントによると,接続の相手側の証明書がない場合は None を返す関数ということで,Noneがかえってきたら証明書なし,それ以外はありと判別しようと考えました.

・このメソッドはSSLSocketオブジェクトに作用するとのことで,下に記載したコードの4~6行目でまずSSLSocketオブジェクトを作りました.これに対しgetpeercert()を用いて証明書の有無をcert_t,certに代入しました.(このあたりはあまり理解できていません,参考URLの二つ目のコードをコピペしました)

・これを実行すると,SSL証明書のあるURLだとエラーが発生せず証明書が返ってくるのですが,証明書のないURLで試すと以下のエラーがでてしまいます.「証明書が無効だ」という内容のエラーだと思うのですが,そもそも証明書の有効性を確かめるようなコードは書いていないのになぜこのようなエラーが出ているかわかりません.

エラー内容

SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1129)

該当のソースコード

python3

1#SSLの取得 2 3import socket 4import ssl 5 6def get_server_certificate(hostname): 7 context = ssl.create_default_context() # デフォルトの設定を持つ新しい SSLContext オブジェクトを返します 8 with socket.create_connection((hostname, 443)) as sock: # host と port で指定されたアドレスへの接続をオープン 9 with context.wrap_socket(sock, server_hostname=hostname) as sslsock: 10 # 既存のPythonソケット(sock)をコンテキストでラップして、 11 # SSLContext.sslsocket_classのインスタンス(デフォルトはSSLSocket)を返します。 12 13 cert_t = sslsock.getpeercert(True) 14 return (cert_t) 15 16 17# cert = get_server_certificate('www.google.com') # SSL証明書のあるページのURL 18cert = get_server_certificate('www.hibishippo.jp') # SSL証明書のないページのURL 19 20cert 21

試したこと

ブラウザのディベロッパーツールに何かしらの方法でアクセスし、Securityのタブに「valid」の文章があるか判別する方法も検討しましたが,検索してもそういった方法を見つけることができませんでした。

補足情報(FW/ツールのバージョンなど)

Jupyter Notebookを使用しています.

参考にしたURL

・Python公式ドキュメント・getpeercert ()について:
https://docs.python.org/3/library/ssl.html#ssl.SSLSocket.getpeercert
・参考にしたコード
https://teratail.com/questions/269727

追記:改変したソースコード

「sslで接続を保ててかつ,証明書が有効期限内の場合にTrueを返し,sslで接続を保てない場合にFalseを返す」コードを書いてみたところ,欲しい結果を得ることができました.

python3

1import socket 2import ssl 3import OpenSSL 4 5valid=False 6 7def get_server_certificate(hostname): 8 context = ssl.create_default_context() 9 try: 10 with socket.create_connection((hostname, 443)) as sock: 11 with context.wrap_socket(sock, server_hostname=hostname) as sslsock: 12 der_cert = sslsock.getpeercert(True) 13 return ssl.DER_cert_to_PEM_cert(der_cert),True 14 except: 15 return 0,False 16 17cert = get_server_certificate('qiita.com') 18 19valid=cert[1] 20 21if valid==True: 22 x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert[0].encode('utf-8')) 23 24 import datetime 25 from datetime import datetime as dt 26 not_before = dt.strptime(str(x509.get_notBefore())[2:16],'%Y%m%d%H%M%S') + datetime.timedelta(hours=9) 27 not_after = dt.strptime(str(x509.get_notAfter())[2:16],'%Y%m%d%H%M%S') + datetime.timedelta(hours=9) 28 print(not_after) 29 30 period=not_after - dt.now() 31 32 if period.days>0: 33 valid=True 34 else: 35 valid=False 36valid

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

poto568

2022/12/07 00:11

目的は「SSL証明書の有無を取得」ですか? そうではなくて「セキュリティはとりあえず無視して とりあえずコンテンツが取得できれば良い」という 要件であれば requests.get(url, verify=False) でうまくいくかもしれません。
bsdfan

2022/12/07 02:45

「SSL証明書の有無を取得」というのが、どういうことをやりたいのかがわかりにくいです。 そもそも証明書がなければ、サーバーはsslで接続を待てないので、sslで接続できるサーバーは、なんらかの証明書は(中身を問わなければ)絶対持っているはずなので、証明書が無いというのはどういう状況のことを言っているのでしょうか? (クライアント側が証明書をもっていないことはよくあることですが)
harunoyo

2022/12/07 05:19

poto568さん,ご返信いただきありがとうございます. 目的としては,厳密には「SSL証明書の有無を判定」できればおそらく十分です. 私は現在「指定されたレストラン予約サイトのページで店舗名,住所,...,およびSSL証明書の有無を取得しなさい」というスクレイピングの課題に取り組んでおり,指定されたページにはすべてSSL証明書があります.SSL証明書がなかった場合もコンテンツを取得するか否かについては指定されておらず,今のところは取得しない予定です. 昨晩からいろいろ試してみたのですが,「sslで接続を保ててかつ,証明書が有効期限内の場合にTrueを返し,sslで接続を保てない場合にFalseを返す」コードを書くことができ,無事解決しました.(質問の最後の「追記」の部分にコードを書きました) ご協力いただきありがとうございました!
harunoyo

2022/12/07 05:19

bsdfanさん,ご返信いただきありがとうございます. 私は現在「指定されたレストラン予約サイトのページで店舗名,住所,...,およびSSL証明書の有無を取得しなさい」というスクレイピングの課題に取り組んでおり,私自身もどういうことがやりたいのかあまり分かっていない状況です. が,bsdfanさんの質問を読み,「sslで接続を保ててかつ,証明書が有効期限内の場合」と「sslで接続を保てない場合」を判別できればいいのではないかと思い,そのようなコードを書いてみました.そのコードで私が欲しい結果を得ることができ解決しました.(質問の最後の「追記」の部分にコードを書きました) ご協力いただきありがとうございました!
bsdfan

2022/12/07 05:31

解決できたならよかったですが、https で接続できるところが証明書ありで、http でしか接続できないところが証明書なしという風にとることもできそうで、課題が曖昧な気がしますね。
harunoyo

2022/12/07 05:35

そうですよね…!シンプルにURLにhttpsがあるかどうかを調べるだけでも十分に思えますよね.どうやら課題が達成できれば方法は問わないような雰囲気なので,その方法もOKかもしれません.ありがとうございます.
guest

回答2

0

自己解決

import socket
import ssl
import OpenSSL

valid=False

def get_server_certificate(hostname):
context = ssl.create_default_context()
try:
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as sslsock:
der_cert = sslsock.getpeercert(True)
return ssl.DER_cert_to_PEM_cert(der_cert),True
except:
return 0,False

cert = get_server_certificate('qiita.com')

valid=cert[1]

if valid==True:
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert[0].encode('utf-8'))

import datetime from datetime import datetime as dt not_before = dt.strptime(str(x509.get_notBefore())[2:16],'%Y%m%d%H%M%S') + datetime.timedelta(hours=9) not_after = dt.strptime(str(x509.get_notAfter())[2:16],'%Y%m%d%H%M%S') + datetime.timedelta(hours=9) print(not_after) period=not_after - dt.now() if period.days>0: valid=True else: valid=False

valid

投稿2022/12/07 06:58

harunoyo

総合スコア1

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

certificate verify failed: self signed certificate

証明書が無いわけではなくてオレオレ証明書だからですかね

投稿2022/12/07 02:39

yuma.inaura

総合スコア1453

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問