pdfファイルにパスワードをつけて、そのパスワードを破る実習をしてみましょう。
※犯罪を助長するものではないです。そのあたりの指導はしっかりお願いします。
▼次に読んで欲しい記事▼
pythonプログラミング実習
pythonでの処理の概要
プログラミングしていく手順は下記です。
- pdfファイルをインターネットからダウンロードしてくる
- pdfファイルにパスワードロックをかける
- pdfファイルにブルートフォース攻撃をしかける
コードを写してOKです。
エラーがでたら間違い探しをして、自己解決能力を養いましょう。
pdfファイルをダウンロードする
「応用情報技術者試験の午前問題の答え」をダウンロードすることにします。
# pdfファイルをダウンロード
### 設定 ################################################
url='https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2022r04_1/2022r04h_ap_am_ans.pdf'
save_name='pdftest.pdf'
#########################################################
import urllib.request
urllib.request.urlretrieve(url, save_name)
pdftest.pdfというファイルが作成されていれば大丈夫です。
あとは今後のためにライブラリのインストールが必要です。
- google colaboratoryの場合はコマンド先頭に「!」が必要です。
- anacondaやVisual studio codeなどであれば「!pip」ではなく「pip~~」でOKです。
# ライブラリのインストール
!pip install PyPDF2
試しに、pdfファイルのページ数を確認してみましょう。
# pdfを読み込み頁数を表示する。
import PyPDF2
### 設定 ################################################
FILE_PATH = '/content/pdftest.pdf'
#########################################################
with open(FILE_PATH, mode='rb') as f:
reader = PyPDF2.PdfFileReader(f)
print(f"Number of pages: {reader.getNumPages()}")
表示の結果は下記のとおり、1ページですね。
Number of pages: 1
上記の通り、ダウンロードするファイル・ダウンロードした後のファイル名は、後々変えたくなることがあるので、設定する変数として目立つところに書いておくと良いですね。
「定数を格納する変数名は、全て大文字にする」ってのも教えておくと良いですね。
パスワードロックをかける
今回は簡単にパスワードは数値とします。
何桁でも大丈夫です。
ただし数「値」なので「01」のように0埋めはされないので注意してください。
今回は140にしました。
4570とかでも53401でも大丈夫ですが、このあとのブルートフォース攻撃でべらぼーに時間かかるので、3桁ぐらいが良いと思いますが、学生に言わないで好きに設定してもらってOKです。
ブルートフォース攻撃でパスワード特定に間がかかって「終わらない」「止まりました」思うかもですが、動いてますのでなんでか考えてみましょう。
# pdfにパスワード付与し暗号化する。
import PyPDF2
### 設定 ################################################
PWDLOCK="140"
src_pdf = PyPDF2.PdfFileReader('/content/pdftest.pdf')
#########################################################
dst_pdf = PyPDF2.PdfFileWriter() # PyPDF2(機械の設計図)をdst_pdf(製品)としてインスタンス化
dst_pdf.cloneReaderDocumentRoot(src_pdf) # pdfファイルを読み込む
dst_pdf.encrypt(PWDLOCK) # パスワードロックをかける
with open('/content/pdftest_pwd.pdf', 'wb') as f: # パスワードロックしたデータをpdfファイルとして書き出す。
dst_pdf.write(f)
print("PDF-FILE has been LOCKED @ PWD = "+str(PWDLOCK))
パスワードを入力せずに開こうとしてみる
さっき打ち込んだコードと同じなのでコピペでも再利用でもOKです。
ただし読み取るファイルはパスワードロック済みのファイルです。
# PDFの読み込みに失敗するコード(パスワードなしで開こうとするから)
# 「File has not been decrypted」がでれば正解。
# ファイルは復号されていない(暗号化を解除していない)よ!
### 設定 ################################################
FILE_PATH = '/content/pdftest_pwd.pdf' ######### パスワードロック済みのファイルにする。
#########################################################
import PyPDF2
with open(FILE_PATH, mode='rb') as f:
reader = PyPDF2.PdfFileReader(f)
print(f"Number of pages: {reader.getNumPages()}")
下記のようなエラーがでれば正解です。
PdfReadError: File has not been decrypted
この辺りでは、学生にエラーを読み解くように指導もすると良いです。
「なんてエラーがでたの?」「何行目にエラーがあるって言ってるの?」を考えましょう。
学生が一度立ち止まって解釈したり探そうとする動きを養うと良いと思います。
解釈する能力を養う時間を設け、時間間近になったら全員ができるように「ケツを持つ」ことが、「やり甲斐のある授業」だと思います。
正しいパスワードを入力して開いてみる
# pdfファイルを開く(ただしいパスワード)
### 設定 ################################################
PWD="140" # 設定した正しいパスワード
ENCRYPTED_FILE_PATH = '/content/pdftest_pwd.pdf'
#########################################################
import PyPDF2
with open(ENCRYPTED_FILE_PATH, mode='rb') as f: # pdfファイルを開く
reader = PyPDF2.PdfFileReader(f)
if reader.isEncrypted: # pdfファイルが暗号化されていたら
reader.decrypt(PWD) # パスワードを使って復号する
print(f"Number of page: {reader.getNumPages()}") #パスワードロックを解除したpdfファイルの頁数を表示する。
ちゃんとパスワードが外せたら、ページ数が表示されます。
Number of page: 1
try文
これから「あるパスワードを試して失敗したら、次のパスワードを試す」というプログラムを作ります。
しかし、パスワードが合っていなかったら、「開けませんでした」エラーでプログラムが止まってしまいます。
そこで、「try文」を教えないといけないですね。
下のプログラムの変更点です。
- PWDに139を代入。間違ったパスワードですね。
- try~except文を追加。
- try文の中にgetNumPages()を追加。
これにて、
- try文でdescrypt()で暗号が外れれば、getNumPages()もエラーなく実行できて、「OK」が表示され、プログラム最後のページ数表示が実行される。
- try文でdescrypt()で暗号が外れなかったら、getNumPages()がエラーがでてしまうので、exceptに行き、「NG」が表示され、プログラム最後のページ数表示が実行されるが、エラーがおきて表示されない。
という実行結果になります。
PWDの値を140とか、139で動作が変わるか確認してください。
# pdfファイルを開く(ただしいパスワード)
### 設定 ################################################
PWD="139" #設定した間違ったパスワード
ENCRYPTED_FILE_PATH = '/content/pdftest_pwd.pdf'
#########################################################
import PyPDF2
with open(ENCRYPTED_FILE_PATH, mode='rb') as f: # pdfファイルを開く
reader = PyPDF2.PdfFileReader(f)
if reader.isEncrypted: # pdfファイルが暗号化されていたら
try:
reader.decrypt(PWD) # パスワードを使って復号する
reader.getNumPages()
print("OK")
except:
print("NG")
print(f"Number of page: {reader.getNumPages()}") #パスワードロックを解除したpdfファイルの頁数を表示する。
パスワードにブルートフォース攻撃を仕掛けてみる
「パスワードに1,2,3,4,,,,,,100,101,,,,,,500と入力しては開いてみるを繰り返してみましょう。」
これだけでプログラムが組める学生さんもいると思います。ぜひ挑戦させてあげてください。
# pdfファイルのパスワードにブルートフォースをしかける。
### 設定 ################################################
ENCRYPTED_FILE_PATH = '/content/pdftest_pwd.pdf'
pwdSt=0
pwdEd=100
#########################################################
import PyPDF2
from tqdm import tqdm
flag =0
cntP =0
print("### START PASSWORD-CRACK ########################")
print("# SEARCH RANGE ( "+str(pwdSt)+" - "+str(pwdEd)+" )")
with open(ENCRYPTED_FILE_PATH, mode='rb') as f:
reader = PyPDF2.PdfFileReader(f)
if reader.isEncrypted:
for pwd in tqdm(range(pwdSt,pwdEd+1)):
try:
flag=1
reader.decrypt(str(pwd))
reader.getNumPages() # EXCEPTに飛ばすために必要
break
except:
flag=0
if (pwd-pwdSt)%((pwdEd-pwdSt)/10)==0 and (pwd-pwdSt)!=0:
cntP=cntP+1
print("")
if flag==1:
print("PDF-FILE has been UNLOCKED @ PWD = "+str(pwd))
else:
print("PDF-FILE could not be UNLOCKED @ PWD = "+str(pwdSt)+" to "+str(pwdEd))
print("### E N D PASSWORD-CRACK ########################")
pdfファイルの取り扱いや暗号化についてのプログラムは別サイト様を参考にさせて頂きました。
▼次に読んで欲しい記事▼