まえがき
長期間の勉強の結果、ついに目標を達成することができました。ハルビン省9月のアマチュア無線A級免許試験に参加し、満点を取得。念願だったアマチュア無線オペレーターの資格を手に入れることができました。
アマチュア無線愛好家として、あるいは常に学び続ける人として、最も重要なことは、あらゆる情報を収集し、閉鎖的な考え方にならないことです。このことを深く理解した私は、[組織名]に入りました。HamCQ コミュニティ、同じ趣味を持つ人たちと親しくなろうと思っていました。
私が、このコミュニティで「運用資格」の認証機能があることを知ったとき、迷うことなく氏名と個人IDを登録して認証手続きを行い、すぐに認証が完了しました。認証にかかる時間が短いことから、この認証が人工操作ではないかと疑念を持ち始めました。もしそうでない場合、どのようにして対応する資格を確認し、認証を行うのでしょうか? この疑問から、この記事で調査を開始しました。
情報収集
調査の結果、現時点で確認できたのは以下の通りです。アマチュア無線局運営技術能力情報プラットフォームおよびアマチュア無線局運営技術能力の検証および情報管理システム証明書の照会は可能ですが、経済産業省の行政サービスプラットフォームにおけるアマチュア無線局運営技術能力に関する情報については、氏名と携帯電話番号を照会条件として使用し、認証コードを受け取った場合にのみ照会が可能であるため、コミュニティでの照会に必要な条件を満たしていません。
したがって、アマチュア無線局の運用技術能力の検証と情報管理システムの研究対象となりました。
このシステムにおける検索条件は、氏名、証明書番号(任意)、身分証明書番号です。これはコミュニティの検索条件に合致しており、コミュニティによる認証プロセスは、コミュニティ管理者による手動での確認や、検索インターフェースを通じての情報処理と判断であると考えられます。興味があるので、詳しく調べてみたいと思います。
研究のプロセス
フロントエンドとバックエンドを分離したウェブサイトにおいて、特にデータの追加、削除、更新、検索などの操作を行う場合、APIインターフェースを通じてネットワークリクエストを送信することで実現されます。
研究に関する注意点「観察、聴取、問診、触診」このような問題に対処する際には、焦らず慎重に状況を観察し、弱点を突くことが重要です。
まず、「見方」から始め、ウェブページを観察し、基本的な検索の流れを確認します:「情報入力」、「クエリ実行」、「結果表示」。一般的な流れは、情報を入力し、「クエリ実行」ボタンをクリックしてバックエンドにGetまたはPostリクエストを送信することです。サーバーがリクエストを受け取り、データベースを照会し、一定形式のデータ(例:JSON)を返します。ページ上の各リスト項目は「item」として扱われ、返された情報はJavaScriptによって解析され、ページ上に表示されます。この仕組みを理解したら、「分析」に進みます。
えっ、そんなことあるんですか? 詳しく説明しますが、本当に理解するには、ある程度のネット知識が必要になります。
「問」とは、送信されたリクエストと返ってきたデータから、インターフェースのアドレスを特定し、使用されている技術を判断し、必要なデータを分析することです。まず、正しい情報を入力して正常な操作をシミュレーションし、ネットワークリクエストの構造を確認します。開発者コンソールを開き、検索条件を入力して検索を実行すると、幸いにも唯一のリクエストが見つかりました。それが今日の注目すべきリクエストです。

分析リクエスト
リクエストヘッダー
ヘッダーをクリックすると、このリクエストがPOSTメソッドを使用していることがわかります。APIのURLは以下です。http://82.157.138.16:8091/CRAC/app/businessSupport/cracOperationCert/getOperCertByParamWeb、リクエストヘッダーを確認し、リクエストに暗号化されたパラメータは含まれていないこと、また、Cookieもセッションの有効期限を延長するための一般的な方法であることを確認します。JSESSIONIDこれにより、このインターフェースは任意の要求を送信でき、認証は不要です。
accept:
*/*
accept-encoding:
gzip, deflate
accept-language:
zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
connection:
keep-alive
content-length:
114
content-type:
application/json; charset=UTF-8
cookie:
JSESSIONID=74D062BFB81FCD71961AD181B4C9D2D5
host:
82.157.138.16:8091
mm:
null
origin:
http://82.157.138.16:8091
qm:
null
referer:
http://82.157.138.16:8091/CRAC/crac/pages/list_cert.html
user-agent:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0
x-requested-with:
XMLHttpRequest
リクエストボディ
POSTリクエストとGETリクエストの違いは、POSTリクエストがリクエストボディを持ち、URLの後にパラメータを追加するのではなく、リクエストボディにデータを送信することです。そのため、ボタンをクリックした後にどのようなデータが送信されたのかを知るには、リクエストボディを確認する必要があります。

{"req":{"ページ番号":"1", "ページサイズ":"100", "名前":"***", "証明書番号": "", "ID車番":"******************"}}
このリクエストボディはJSON形式で記述されており、以下のフィールドを簡単に分析します。page_no:ページ番号、page_size:ページで表示できる最大件数を指定します。name:氏名、certificateNo:証明書番号、idCarNumber:身分証明書番号
リクエストの内容と形式が分かったので、次の手順に進むことができます。
「切」という操作は、研究の中で最も想像力を発揮できる場面です。この研究では、インターフェースの技術を様々な方法で活用しますが、私はPythonという、習得しやすいプログラミング言語を使って、インターフェースへのアクセスを行います。
Pythonには、ネットワークリクエストを扱うためのライブラリが多数存在し、その中でも特に使いやすいものはいくつかあります。requests、数回のステップで簡単にシミュレーションされたネットワークリクエストを完了できます。このスクリプトでは、コードの堅牢性を高めるために、入力時に自動的に入力検証を行い、スクリプトが入力の有効性を自動的に検出し、適切なメッセージを表示します。また、リクエストの成功/失敗を自動的に判断します。リクエストが成功した場合、取得した情報をインターフェースに表示します。
誤った入力結果

正しい回答

ソースコードは以下のとおりです。
```python
import re
import requests
import json
# 中国人名正規表現
chinese_name_pattern = r'^[\u4E00-\u9FA5]{2,4}$'
# 運転免許証番号の正規表現
_IDRe18 = re.compile(r'^([1-6][1-9]|50)\d{4}(18|19|20)\d{2}((0[1-9])|10|11|12)(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$')
_IDre15 = re.compile(r'^([1-6][1-9]|50)\d{4}\d{2}((0[1-9])|10|11|12)(([0-2][1-9])|10|20|30|31)\d{3}$')
# 入力プロンプト
page_no = 1
page_size = 100
while True:
name = input("氏名を入力してください:")
if re.match(chinese_name_pattern, name):
break
print("氏名は2-4文字の漢字で入力してください。")
while True:
certificateNo = input("証明書番号(任意):")
if not certificateNo:
break
pattern = r'^[ABC]\d{9}$'
if re.match(pattern, certificateNo):
break
print("証明書番号の形式は、A, B, または C の文字に続けて 9 桁の数字で入力してください。")
while True:
idCarNumber = input("運転免許証番号を入力してください:")
if _IDRe18.match(idCarNumber) or _IDre15.match(idCarNumber):
break
print("運転免許証番号の形式が正しくありません。再入力してください。")
# APIリクエスト
url = "http://82.157.138.16:8091/CRAC/app/businessSupport/cracOperationCert/getOperCertByParamWeb"
headers = {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Connection": "keep-alive",
"Content-Type": "application/json; charset=UTF-8",
"Cookie": "",
"Host": "82.157.138.16:8091",
"Origin": "http://82.157.138.16:8091",
"Referer": "http://82.157.138.16:8091/CRAC/crac/pages/list_cert.html",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0",
"X-Requested-With": "XMLHttpRequest",
"mm": "null",
"qm": "null"
}
data = {
"req": {
"page_no": page_no,
"page_size": page_size,
"name": name,
"certificateNo": certificateNo,
"idCarNumber": idCarNumber
}
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status() # リクエストが成功したか確認
except requests.exceptions.RequestException as e:
print(f"リクエスト送信中にエラーが発生しました:{e}")
exit()
try:
prc_list = response.json()['res']['prcList']
except json.JSONDecodeError:
print("レスポンスデータは有効な JSON 形式ではありません。")
exit()
if not prc_list:
print("関連する証明書の情報は見つかりませんでした。")
exit()
# 必要な情報を抽出し、フォーマットして出力
for item in prc_list:
print(f"身分 ID:{item.get('id', '')}")
print(f"氏名:{item.get('name', '')}")
print(f"性別:{item.get('sex', '')}")
print(f"操作カテゴリ:{item.get('type', '')}")
print(f"証明書番号:{item.get('certificateNo', '')}")
print(f"有効期限:{item.get('passDate', '')}")
print(f"発行場所:{item.get('passAddr', '')}")
print(f"発行日:{item.get('issueDate', '')}")
print(f"発行機関:{item.get('addr', '')}")
print("---------------------------")
```
結末
今回の分析を通じて、このクエリインターフェースには暗号化措置が施されておらず、呼び出しも容易であるため、コミュニティが独自にこのインターフェースを利用し、二次開発によって証明書の検証を行っている可能性が高いことがわかりました。今回の分析を通して、ページタイプの判断から、ネットワークリクエストのシミュレーション、そしてネットワークリクエストの分析とテストを行うプロセスを改めて体験することができ、非常に有益でした。HamCQ コミュニティ初めての投稿です。今後、より多くの仲間と交流できたらと思っています。73!またね!