50619


Python3の`urllib.request.Request`のheadersでhttp
headerを指定してもheaderが送信されないのですがなぜこのようなことがおこるのでしょうか?

mnctf2017というctfサイトの情報照会というお題に取り組んでいるのですが、この問題では、FLAGをとるためにAPIサーバに問い合わせる必要があります。  
APIにアクセスするためにはAPI tokenのようなものを`X-TOKEN`ヘッダに追加しなければなりません。  
X-TOKENヘッダを追加するためにurllib.request.Requestの引数のheadersに`{"X-TOKEN":
token}`としてリクエストを送信するのですが、X-TOKENをヘッダに追加したにもかかわらず、X-TOKENがないというエラーが返ってきます。

なぜでしょうか?

ちなみに他の方の答えを見たところrequestsをつかうとうまくいくようです。

urllibを使ってアクセスするコード:

[code]

    
    import urllib.request as ur
    import urllib.parse as up
    import time
    import json
    
    hashlist = open("minhashlist.txt").read().split("\n")[:-1]
    malware_name = "RAT.A.aa74e"
    apikey = "578459a056231ac6745fcb53e3304b3043bb7c9448863e84652764592d15b3d1"
    
    def get_apitoken():
        post_data = up.urlencode({"key": apikey}).encode("utf-8")
        res = ur.urlopen("http://157.7.53.197/intel/gettoken/", data=post_data)
        res_str = res.read().decode("ascii")
        res_json = json.loads(res_str)
        return res_json["expire"], res_json["token"]
    
    def search(token, hash):
        print(token)
        data = up.urlencode({"hash": hash}).encode("utf-8")
        req = ur.Request("http://157.7.53.197/intel/query/", data=data, headers={
            "x-token": token # ここで"X-TOKEN"を追加した
            })
        res = ur.urlopen(req)
        body = res.read().decode("utf-8")
        res_json = json.loads(body)
        if res_json["auth"] != "success": #認証に失敗したらエラーを発生させる
            raise Exception("auth error: " + res_json["reason"])
        if malware_name in detection_name:
            print("answer:", res_json["hash"])
    
    def main():
        expire, token = get_apitoken()
        expire = int(expire)
        for v in hashlist:
            search(token, v)
            now = time.time()
            if now >= expired:
                expire, token = get_apitoken()
                expire = int(expire)
    
    
[/code]

エラーメッセージ:

[code]

    Traceback (most recent call last):
      File "getinfo.py", line 41, in <module>
        main()
      File "getinfo.py", line 35, in main
        search(token, v)
      File "getinfo.py", line 27, in search
        raise Exception("auth error: " + res_json["reason"])
    Exception: auth error: no x-token header
    
[/code]

<http://mnctf.info/mnctf2017/top.php>  
[上のURLの"情報照会"という問題です]

このCTFで使用するAPIサーバの仕様書  
<http://mnctf.info/mnctf2017/task/MiNTEL_API_Reference.pdf>

環境:  
Windows 10  
Python 3.6.7


CC BY-SA 4.0
2018-11-25T12:42:56.257
50614
2018-11-26T13:32:57.063
2018-11-26T13:32:57.063
5246
5246
question
python python3
Pythonのurllib.request.Requestでheadersを指定しても反映されない?


python 3.7 ですが、httpbin
を使って確認したところ、"X-Token"ヘッダーは送信されていました。サーバ仕様の理解に認識違いがあったりはしませんか？

ソースコード

[code]

    import urllib.request as ur
    
    req = ur.Request("https://httpbin.org/post", data=b"foo", headers={"x-token": "token"})
    res = ur.urlopen(req)
    body = res.read().decode("utf-8")
    print(body)
    
[/code]

出力結果

[code]

    $ python3.7 request.py                                                                                                                                        
    {
      "args": {},
      "data": "",
      "files": {},
      "form": {
        "foo": ""
      },
      "headers": {
        "Accept-Encoding": "identity",
        "Connection": "close",
        "Content-Length": "3",
        "Content-Type": "application/x-www-form-urlencoded",
        "Host": "httpbin.org",
        "User-Agent": "Python-urllib/3.7",
        "X-Token": "token"
      },
      "json": null,
      "origin": "133.200.138.64",
      "url": "https://httpbin.org/post"
    }
    
[/code]


CC BY-SA 4.0
2018-11-25T14:43:11.667
50618
2018-11-25T14:43:11.667
5997
50614
answer


http.serverとsocketserverでKiYugadgeterさんと、ネットで見かけた同じ問題をrequestsで解いていたコードのPOSTヘッダを見比べてみましたがたしかにX-
TOKENも送信されてましたし、差はConnectionくらいでしたね。

と、思ったんですが、urllibでは`X-TOKEN`が `X-Token`にされていますね。  
（修正） これが原因だったようです。追記を参照してください。

[code]

    import urllib.request as ur
    import urllib.parse as up
    import requests
    
    def query(hash, token):
      # 参考用
      r = requests.post('http://127.0.0.1:8000/', headers={
        'X-TOKEN': token
      }, data={
        'hash': hash
      })
      return r.json()
    
    def search(token, hash):
        print(token)
        data = up.urlencode({"hash": hash}).encode("utf-8")
        req = ur.Request("http://127.0.0.1:8000/", data=data, headers={
            "X-TOKEN": token # ここで"X-TOKEN"を追加した
            })
        res = ur.urlopen(req)
        body = res.read().decode("utf-8")
    
    search('a', 'h')
    # 参考用
    query('h2', 'a2')
    
[/code]

urllib

[code]

    Accept-Encoding: identity
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 6
    Host: 127.0.0.1:8000
    User-Agent: Python-urllib/3.6
    X-Token: a
    Connection: close
    
[/code]

requests

[code]

    Host: 127.0.0.1:8000
    User-Agent: python-requests/2.18.4
    Accept-Encoding: gzip, deflate
    Accept: */*
    Connection: keep-alive
    X-TOKEN: a2
    Content-Length: 7
    Content-Type: application/x-www-form-urlencoded
    
[/code]

参考にさせていただいたサイト：  
<https://st98.github.io/diary/posts/2017-07-10-mnctf-2017.html>

python 3.6.4 (Anaconda)

* * *

追記：

参考のrequestsを使ったコードで、`X-Token`にして送信実行してみたところ、  
`no x-token header`エラーが返されましたので、urllibが小文字にしてしまうのが原因のようです。  
（最後まではやっていませんが、`X-TOKEN`の場合はしばらく動作してました。）

HTTPヘッダの仕様ではcase-insensitiveであるべきだったと思いますが、サーバープログラムの都合でしょうね。  
urllibだと別の文字クラスを用意するような結構手間なようなので、requestsを使うのが現実的かもしれません。

上記の「別の文字クラスを・・・」は以下のstackoverflowの回答を参考にしました。

<https://stackoverflow.com/a/18268226/2513010>


CC BY-SA 4.0
2018-11-25T15:14:31.707
50619
2018-11-25T15:30:03.877
2018-11-25T15:30:03.877
728
728
50614
answer