0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Node.js で Azure BLOB オブジェクト を 30秒間 だけ Read 可能な SAS トークン を ライブラリを使わないで生成する

Last updated at Posted at 2021-01-29

tl;dr

SAS トークンの構造を学習するために、ライブラリを使わないで、Node.js で生成しました。
( Java 版は こちら )

公式ドキュメント : サービス SAS を作成する

コード

BLOB オブジェクト を 30秒 だけ Read 可能なURL

main.js
const SAS = require("./SAS");

const name = "ストレージアカウント名";
const key = "キー";
const container = "コンテナ名";
const blob = "ブロブ名";
const expiry = 30; // 30 秒間有効
const filename = "ファイル名";

const sas = new SAS(name, key);
const url = sas.getUrl(container, blob, expiry, filename);
console.log(url);
SAS.js
const crypto = require("crypto");

class SAS {
    // コンストラクタ
    // @param name ストレージアカウント名
    // @param key ストレージアカウントキー
    constructor(name, key) {
        this.account_name = name;
        this.account_key = key;
    }

    // トークン付きURLを生成する
    // @param container コンテナ名
    // @param blob ブロブ名
    // @param expiry_second トークン有効期間
    // @param filename ファイル名
    getUrl(container, blob, expiry_second, filename) {
        const token = this.getToken(container, blob, expiry_second, filename);
        const url = `https://${this.account_name}.blob.core.windows.net/${container}/${blob}`;
        return `${url}?${token}`;
    }

    // トークンを生成する
    // @param container コンテナ名
    // @param blob ブロブ名
    // @param expiry_second トークン有効期間
    // @param filename ファイル名
    getToken(container, blob, expiry_second, filename) {
        // パラメータ
        const now = new Date();
        const start = ""; // this.isoDate(now, start_second);
        const expiry = this.isoDate(now, expiry_second);

        const version = "2018-11-09";
        const resource = "b";
        const permission = "r";
        const ip = ""; // "0.0.0.0/0";
        const protocol = ""; // "https";

        const canonicalizedResource = `/blob/${this.account_name}/${container}/${blob}`;

        const rscc = "";
        const rscd = `attachment; filename="${filename}"`;
        const rsce = "";
        const rscl = "";
        const rsct = "";

        const identifier = "";
        const snapshot = "";

        // シグネチャ (順番は厳守)
        const stringToSign = [
            permission,
            start,
            expiry,
            canonicalizedResource,
            identifier,
            ip,
            protocol,
            version,
            resource,
            snapshot,
            rscc, // Cache-Control
            rscd, // Content-Disposition
            rsce, // Content-Encoding
            rscl, // Content-Language
            rsct, // Content-Type
        ].join("\n");

        // HMAC256
        const signature = this.getHMAC256(stringToSign);

        // クエリパラメータ (順番は任意)
        const sas = [
            ["sp", permission], //
            ["sr", resource],
            // ["st", start],
            ["se", expiry],
            // ["sip", ip],
            // ["spr", protocol],
            ["sv", version],
            ["sig", signature],
            ["rscd", rscd],
        ]
            .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
            .join("&");

        return sas;
    }

    isoDate(now, second = 0) {
        const date = new Date(now.getTime() + second * 1000);
        return date.toISOString().substring(0, 19) + "Z";
    }

    getHMAC256(input) {
        // 1. キー は Buffer (Base64 で デコード)
        const key = Buffer.from(this.account_key, "base64");
        const hmac = crypto.createHmac("sha256", key);
        // 2. 入力 は Buffer (UTF8 で デコード)
        const inp = Buffer.from(input, "utf8");
        // 3. 出力 は Buffer を Base64 で エンコード
        const out = hmac.update(inp).digest();
        return out.toString("base64");
    }

    // 上を簡単に書き直すと
    getHMAC256_(input) {
        const key = Buffer.from(this.account_key, "base64");
        return crypto.createHmac("sha256", key).update(input).digest("base64");
    }
}

module.exports = SAS;

01/29 追記

ちなみに、az コマンドで同じことをやるには、こんな感じ

ACCOUNT_NAME='ストレージアカウント名'
ACCOUNT_KEY='キー'
FUTURE_DATE=$(date -v+30M '+%Y-%m-%dT%H:%MZ')

az storage account generate-sas \
  --permissions r \
  --resource-types o \
  --services b \
  --expiry $FUTURE_DATE \
  --account-name $ACCOUNT_NAME \
  --account-key $ACCOUNT_KEY
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?