🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

Q&A

解決済

1回答

2380閲覧

GASでダイジェスト認証を通過したい

gatten1217

総合スコア59

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ハッシュ

ハッシュは、高速にデータ検索を行うアルゴリズムのことです。

API

APIはApplication Programming Interfaceの略です。APIはプログラムにリクエストされるサービスがどのように動作するかを、デベロッパーが定めたものです。

1グッド

4クリップ

投稿2021/02/24 07:42

GASを使ってネットワークカメラから画像を取得したいと思っていますが、完全にハマってしまいました。

過去の質問↓
GASでネットワークカメラから静止画を取得したい
連想配列から値を取り出せない

色々な方にアドバイスを頂き、ようやくダイジェスト認証の2回めのリクエストにチャレンジするところまで来ているのですが、エラーが出てしまいます。

javascript

1function camera(){ 2 var url = "http://グローバルIP****************:ポート番号*********/snapshot.jpg" //ポート開放済み 3 var options = { 4 "method": "GET", 5 "muteHttpExceptions":true 6 }; 7 var firstRequest = UrlFetchApp.fetch(url,options).getHeaders(); 8 Logger.log(firstRequest) 9 //レスポンスヘッダー{Server=, Content-Type=text/html, Content-Length=351, Date=Wed, 24 Feb 2021 07:22:38 GMT, WWW-Authenticate=Digest realm="Network Camera", nonce="9e55a4cbae641acb2d0adb43890abf84", qop="auth"} 10 11 var data = firstRequest["WWW-Authenticate"] 12 var nonce = data.slice(data.indexOf("nonce")+7,-13) //nonceの文字列切り出し 13 var nc = "00000001" 14 var q = "auth" 15 var cNonce = cnonce() 16 var a1 = "admin:Network Camera:パスワード************" 17 var a2 = "GET:/snapshot.jpg" 18  19 //ハッシュ値計算 20 var a1digest = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, a1); 21 var a2digest = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, a2); 22 var responsestr = `${a1digest}:${nonce}:${nc}:${cNonce}:${q}:${a2digest}`; 23 var response = getMD5Hash(responsestr);//16進数32桁の文字列に 24 25 //ヘッダー 26 var headers = { 27 Authorization:"Digest username:admin", 28 realm:"Network Camera", 29 nonce:nonce, 30 uri:"/snapshot.jpg", 31 response:response, 32 qop:q, 33 nc:nc, 34 cnonce:cNonce, 35 algorithm:"MD5" 36 } 37 var options2 = { 38 "muteHttpExceptions":true, 39 "headers":headers 40 }; 41 var secondRequest = UrlFetchApp.fetch(url,options2); 42 Logger.log(secondRequest) //400 - Bad Request 43} 44 45function getMD5Hash(input) { 46 var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input); 47 var txtHash = ''; 48 for (j = 0; j < rawHash.length; j++) { 49 var hashVal = rawHash[j]; 50 if (hashVal < 0) 51 hashVal += 256; 52 if (hashVal.toString(16).length == 1) 53 txtHash += "0"; 54 txtHash += hashVal.toString(16); 55 } 56 return txtHash; 57} 58 59function cnonce(){ 60 // 生成する文字列の長さ 61 var l = 16; 62 // 生成する文字列に含める文字セット 63 var c = "abcdefghijklmnopqrstuvwxyz0123456789"; 64 var cl = c.length; 65 var r = ""; 66 for(var i=0; i<l; i++){ 67 r += c[Math.floor(Math.random()*cl)]; 68 } 69 return r 70}

もはや何がおかしいのか自分でもよくわからないのですが、特にハッシュ値の計算とヘッダーの記述部分に自信がありません。
他にもおかしいところがありましたらご指摘いただけないでしょうか。

よろしくお願いいたします。

NP3228👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

ネットワークカメラの実機がないので試せていませんが、とりあえず直せそうなところが3つあります。

コードの流れに沿って上から順に説明しますが、優先順位は、3=2>1です。

1.nonceの文字列切り出し

var nonce = data.slice(data.indexOf("nonce")+7,-13) //nonceの文字列切り出し

ですが、サーバーが返すレスポンスヘッダの中身は順番が固定ではないことがあるので、固定位置を前提とした切り出しだと、うまくnonceが切り出せない場合があります。

したがって、安全に切り出すならば、位置に関係なく抜き出せるように正規表現を使用して

var re=/nonce=\"([\x0-\xf]+)\"/ nonce = data.match(re)[1]

とするか、nonceが32文字であることが確定しているならば

var pos = data.indexOf("nonce") var nonce = data.slice(pos+7,pos+7+32) //nonceの文字列切り出し

とします。



2.Utilities.computeDigest()関数について
→この関数の戻り値は、バイト列です。したがって、

var a1digest = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, a1); var a2digest = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, a2); var responsestr = `${a1digest}:${nonce}:${nc}:${cNonce}:${q}:${a2digest}`;

としてしまうと、
3行目のresponsestr の中身は、

-121,26,67,-51,103,-96,25,108,-12,1,-8,-109,89,115,-34,-79:555a06e8db3c55837408acb1a7a69f5a:36a1333e1a98906f378dba493613942e...

というように、カンマで区切られた整数列が混ざってしまいます。

a1digest, a2digest は nonce と同じく16進数表現で渡す必要があるので、getMD5Hash()関数を使いましょう。

var a1digest = getMD5Hash(a1); var a2digest = getMD5Hash(a2);

 


3.ヘッダーの組み立て方について
ヘッダーのAuthorizationをオブジェクトを使って組み立てていますが、
掲題のコードでは400 Bad requestが返ってきているとのコメントから察するに、サーバーがヘッダーの内容を正しく解析できていないと考えられます。
(もしサーバーがヘッダの内容を解析できていていて認証に失敗しているならば、401 Unauthorizedが返ってくるはず)

調べた結果、Authorizationは、オブジェクトではなく、平文で組み立てて渡さなければならないようです。

http://x68000.q-e-d.net/~68user/net/http-auth-2.htmlの「クライアントが返すべき Authorization ヘッダ」を見ると、項目によっては二重引用符を付ける必要があったり、逆につけてはいけなかったりします。

したがって、ヘッダーの組み立ての部分は下記のようになります。

var user = "admin"; var realm = "Network Camera"; var uri= "/snapshot.jpg"; // nc, algorithm, qopには二重引用符を付けない。 var headers = { Authorization : `Digest username="${user}", realm="${realm}", nonce="${nonce}", uri="${uri}", cnonce="${cNonce}", nc=00000001, algorithm=MD5, response="${response}", qop=auth` }

最初に述べたように、実機で検証できていないため、他の要因で認証失敗するかもしれません。
その場合は、curlで

curl --digest -o test.jpg -u <UserName>:<Password> http://<IPAddress>:<HttpPort>/snapshot.jpg -v

-vオプションを付けて出力された結果を、質問文に掲載してもらうと、より解析が進められるかもしれません。

・参考にしたサイト他
68user's pageさん
python-requestsのソースコード
http://jigsaw.w3.org/HTTP/Digest/ (ダイジェスト認証の検証に使用)

投稿2021/02/24 15:10

編集2021/02/24 15:18
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

gatten1217

2021/02/25 02:16

qnoir様のアドバイスを頂いて試したところ、一発で成功しました! 神!
退会済みユーザー

退会済みユーザー

2021/02/25 02:59

よかったです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問