セキュアスカイによる安全なWebサイト運営のためのセキュリティ情報

エンジニアブログ
  • shareSNSでシェア
  • Facebookでシェアする
  • Xでシェアする
  • Pocketに投稿する
  • はてなブックマークに投稿する

HTTP通信データの圧縮アルゴリズムについて

HTTP通信データの圧縮アルゴリズムについて

はじめに

こんにちは
アルバイトのMarikaです。
今回はHTTP通信データの圧縮アルゴリズムについて調査しました。

背景

脆弱性診断において大量のHTTP通信データが生成されます。
そのため診断ではHTTP通信データをいかに小さくし、アーカイブ等の運用面でのコストを削減するかという工夫が必要になります。
現状、gzipやbizp2などを使用し圧縮して保管していますが、最近ではwebコンテンツ向けの圧縮アルゴリズム、つまりHTTP通信を圧縮することに適したアルゴリズムが開発されています。
よりよい圧縮率を求めて圧縮アルゴリズムを調査しました。

 

調査の概要

まず現状のgzip圧縮の圧縮率を調査し、現状把握とします。
次にgzip以外の3つの圧縮アルゴリズムの候補と比較します。
圧縮アルゴリズムの候補はbzip2、brotli、zstandardです。
今回は圧縮率と圧縮にかかる時間について比較しました。
gzipとbzip2はともに汎用的に使うために開発された圧縮アルゴリズムですが、brotliとzstandardはwebコンテンツ向けに開発された圧縮アルゴリズムです。

 

調査の詳細

使用したデータセットは以下のようになっています。

今回のデータセットは1つの診断案件の中に6つのセッション(1つの診断対象リクエストについてクロール~スキャンまでを行う単位)があり、その中にクロールやスキャン時のHTTP通信が含まれています。
セッションごと通信内容の特徴は以下の通りです。

1bba3516c5c9bea615ed0cd430d2f37a/ 小さめのJSONを返す単一通信をスキャン
3632b9f842e0b905fd72979c481c7c64/ HTMLを返す複数通信をスキャン
5603397aa5f4f43641ae280d06b101bc/ ランダムな英数字を返す単一通信をスキャン
7c11aac069d56b7a309d85d181d5597c/ 58KBほどのHTMLを返す単一通信をスキャン
ce2c2ea2a4a5dcca1cf0304ba53db3d7/ HTMLを返す単一通信をスキャン
f435b2e81c23442f8519998421db66a3/ JPEG画像を返す単一通信をスキャン

現状はHTTPリクエスト1つにつき1ファイル、HTTPレスポンス1つにつき1ファイルずつgzipで圧縮しています。

今回の調査で使用した環境について説明します。

 

作成したコード:
圧縮

public static void show(final File srcDir, final Path destDirPath) throws IOException {
   System.out.println("## " + srcDir + " -> " + destDirPath);
   for (File f : srcDir.listFiles()) {
       if (f.isDirectory()) {
           final File nextSrcDir = f.getAbsoluteFile();
           final Path nextDestDirPath = Paths.get(destDirPath.toString(), f.getName());
           Files.createDirectory(nextDestDirPath);
           show(nextSrcDir, nextDestDirPath);
       } else {
           if (f.toString().contains("_")) {
               continue;
           }
           final Path zipFilePath = destDirPath.resolve(f.getName() + ".gz");
           try (
                   InputStream is = Files.newInputStream(f.toPath());
                   GZIPOutputStream bos = new GZIPOutputStream(Files.newOutputStream(zipFilePath, StandardOpenOption.CREATE,
                           StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE))) {
               is.transferTo(bos);
           }
       }
   }
}

 

展開

public static void show(final File srcDir, final Path destDirPath) throws IOException {
   System.out.println("## " + srcDir + " -> " + destDirPath);
   for (File f : srcDir.listFiles()) {
       if (f.isDirectory()) {
           final File nextSrcDir = f.getAbsoluteFile();
           final Path nextDestDirPath = Paths.get(destDirPath.toString(), f.getName());
           Files.createDirectory(nextDestDirPath);
           show(nextSrcDir, nextDestDirPath);
       } else {
           if (!f.toString().endsWith(".gz")) {
               continue;
           }
           final Path ungzipFilePath = destDirPath.resolve(f.getName().replace(".gz", ""));
           try (
                   GZIPInputStream gis = new GZIPInputStream(Files.newInputStream(f.toPath()));
                   OutputStream os = Files.newOutputStream(ungzipFilePath, StandardOpenOption.CREATE,
                           StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE);) {
               gis.transferTo(os);
           }
       }
   }
}

例はgzipで圧縮、展開するものです。
その他の場合も同様ですが、ライブラリを変更し、それぞれのInputStream、OutputStreamを使用します。
関数内では対象のディレクトリ以下にあるものがディレクトリだった場合、対象を更新し、再帰的に調べるようになっています。

 

調査結果

 

ファイル単位での圧縮

小さめのJSONを返す単一通信をスキャン HTMLを返す複数通信をスキャン ランダムな英数字を返す単一通信をスキャン 58KBほどのHTMLを返す単一通信をスキャン HTMLを返す単一通信をスキャン JPEG画像を返す単一通信をスキャン
sessionId 1bba3516c5c9bea615ed0cd430d2f37a 3632b9f842e0b905fd72979c481c7c64 5603397aa5f4f43641ae280d06b101bc 7c11aac069d56b7a309d85d181d5597c ce2c2ea2a4a5dcca1cf0304ba53db3d7 f435b2e81c23442f8519998421db66a3
unpacked(a) 4,556,362 10,708,363 2,694,324 92,831,189 2,672,359 23,861,782
gz(b) 3,392,310 6,355,647 1,999,613 16,430,489 1,781,604 20,301,764
bzip2(c) 4,433,422 7,925,849 2,186,239 12,847,486 2,440,012 20,505,975
brotli(d) 2,869,636 5,216,632 1,911,398 10,937,152 1,438,902 19,918,599
zstandard(e) 3,503,913 6,538,763 1,997,627 14,898,578 1,832,019 20,573,608
圧縮率(gz) 74.45% 59.35% 74.22% 17.70% 66.67% 85.08%
圧縮率(bzip2) 97.30% 74.02% 81.14% 13.84% 91.31% 85.94%
圧縮率(brotli) 62.98% 48.72% 70.94% 11.78% 53.84% 83.47%
圧縮率(zstd) 76.90% 61.06% 74.14% 16.05% 68.55% 86.22%

 

セッション単位での圧縮

小さめのJSONを返す単一通信をスキャン HTMLを返す複数通信をスキャン ランダムな英数字を返す単一通信をスキャン 58KBほどのHTMLを返す単一通信をスキャン HTMLを返す単一通信をスキャン JPEG画像を返す単一通信をスキャン
sessionid 1bba3516c5c9bea615ed0cd430d2f37a 3632b9f842e0b905fd72979c481c7c64 5603397aa5f4f43641ae280d06b101bc 7c11aac069d56b7a309d85d181d5597c ce2c2ea2a4a5dcca1cf0304ba53db3d7 f435b2e81c23442f8519998421db66a3
unpacked(a) 4555079 2842402 2689306 92772478 2671025 23798937
gz(b) 167,301 1,016,581 1,602,729 16,326,552 110,716 20,186,295
bz2(c) 89,752 917,229 1,502,407 10,019,523 55,643 2,523,037
br(d) 94,321 713,226 1,430,503 7,930,658 57,316 77,500
zstd(e) 138,848 848,406 1,444,978 14,058,027 79,591 128,635
圧縮率(gz) 3.67% 35.76% 59.60% 17.60% 4.15% 84.82%
圧縮率(bz2) 1.97% 32.27% 55.87% 10.80% 2.08% 10.60%
圧縮率(br) 2.07% 25.09% 53.19% 8.55% 2.15% 0.33%
圧縮率(zstd) 3.05% 29.85% 53.73% 15.15% 2.98% 0.54%

 

圧縮する単位による違い

案件単位でtarでアーカイブした場合 セッション単位でtarでアーカイブした場合 ファイルごとに圧縮した場合
展開されたファイル(a) 137,324,379 137,324,379 137,324,379
gz(b) 39,428,300 39,410,174 45,534,450
bz2(c) 15,136,886 15,107,591 44,442,653
br(d) 10,304,951 10,303,524 38,407,974
zstd(e) 16,657,555 16,698,485 44,482,611
圧縮率(gz) 28.71% 28.70% 33.16%
圧縮率(bz2) 11.02% 11.00% 32.36%
圧縮率(br) 7.50% 7.50% 27.97%
圧縮率(zstd) 12.13% 12.16% 32.39%

 

所要時間

gz bz2 br zstd
各ファイルの圧縮 28,732 273,057 328,368 32,259
各ファイルの解凍 21,324 197,194 22,337 20,809
セッションごとの圧縮 4,572 70,522 243,830 1,004
セッションごとの解凍 1,910 59,788 804 819
案件単位の圧縮 3,609 57,174 200,820 918
案件単位の解凍 1,595 45,522 723 672

 

圧縮率はgzip > bzip2 >>> brotli, zstandard という印象でした。
圧縮にかかる時間はbrotli > bzip2 > gzip > zstandard、
解凍にかかる時間はbzip2 > gzip > zstandard > brotliとなりましたが、数回の計測なので作業環境等により変わる可能性があります。

 

まとめ

圧縮率はやはりwebコンテンツ向けの2つが目立つ結果となりました。
圧縮時間はbrotliが異常なほど時間がかかりましたが、tarでアーカイブして圧縮する回数を減らすことで時間短縮になりました。
圧縮率に差が出た要因として、同じ要素が多く含まれるものだとまとめて圧縮できるそうなのでファイル単位よりフォルダ単位で大きく圧縮するほうが圧縮率が高くなりました。
圧縮時間はbrotliが長かったため原因追跡までできればなおよかったと思いました。
調べた限りではセキュリティ対策ソフトが原因のことがあるようです。

  • shareSNSでシェア
  • Facebookでシェアする
  • Xでシェアする
  • Pocketに投稿する
  • はてなブックマークに投稿する

この記事の筆者

筆者:SST-blog