ハッシュ化(暗号化)におけるソルトとは

この記事では、ハッシュ化で使われる「ソルト」について、説明していきます。
どちらかと言うと初心者向けです。


【ハッシュ化とは】

ハッシュ化とは、与えられた文字列を特定の方式(アルゴリズム)に従って変換することです。
変換後の文字列から変換前の文字列に戻すことは困難であるため、変換前の文字列を知られたくない場合に用いられます。
具体的には、パスワードを使用した認証処理(ログイン処理)で使われることが多いです。

例えば、「SHA-512」と呼ばれる方式では、「password」という文字列は以下のように変換されます。

【認証処理におけるハッシュ化の使い方】

認証処理においては、パスワードを管理するファイルやデータベースに、ハッシュ化された形でパスワードが保持されます。
認証を行う際には、ユーザーから入力されたパスワードをハッシュ化し、ファイルやデータベースに保持されているハッシュ化された文字列と一致することを確認することで妥当性を確認します。

なお、システムの管理者もパスワードの平文を知ることができないので、ユーザーがパスワードを忘れてしまった場合はパスワードを再作成する運用を行います。

【レインボーテーブルによる攻撃】

不正ログインを試みる攻撃者は、何らかの方法でハッシュ化されたパスワードの一覧を入手した後、レインボーテーブルによる攻撃を試みることがあります。
レインボーテーブルとは、平文とハッシュ化後文字列の対応表のことを指し、この対応表を用いることでハッシュ化後文字列から平文を推測することができます。
例えば、以下のような対応表がレインボーテーブルです。全ての平文について対応表を作ることは困難ですが、良く使われる平文はこれで突破されてしまいます。

【ソルトによる対策】

ここでソルトの登場です。
ソルトとは、平文からハッシュ化を行う前に、平文に付加される文字列のことを指します。
ソルトは、平文の前に付加されても後に付加されても構いません。
ソルトを付加することにより、レインボーテーブルによる攻撃で平文のパスワードを推測されることを防ぎやすくなります。

以下は、「password」という平文の後ろに「LdCTFPMk」という平文を追加する例です。
ハッシュ化後文字列が全く違うものになり、レインボーテーブルで逆引きすることが困難になります。

なお、ソルトに用いる文字列は、複雑で長い文字列(例えばランダムで生成された文字列)、かつユーザー毎で使い分ける(ログイン時に入力されたユーザーから対応するソルトを取得する処理を別途作成する)ことが望ましいです。


いかがでしたでしょうか。

ソルトは良く出てくる技術ですが、その意味はあまり知られていないように思うので、今回の記事が参考になれば幸いです。

リーダーシップ状況論の理論と実践

今回の記事では、リーダーシップの理論と、IT業界における実践について述べていきます。
情報処理技術者試験対策としては理論を知っていれば良いですが、実際に現場で活かす上で役に立つ情報(経験則)も書いていきます。


【理論】

組織とリーダシップの関係は下記図のようになるとされています。
組織が成熟するにつれて、有効なリーダーシップの取り方がa→b→c→dに移行するとされています。

aは組織の発足時の段階です。
構成員のスキルも自主性も不足しているため、仕事関係本位でリーダー自ら先頭に立ってチームを引っ張る必要があります。
スポーツに例えると「選手をきちんと管理することが勝つための条件だ。」という状態です。

bはaから成熟度が進み、構成員がスキルを身に付け始めた頃です。
構成員を引っ張るだけでなく、リーダーが1から10まで指示しなくてもチームがパフォーマンスを発揮できるようにするために、構成員の自主性を育て大切にすることも考える必要が出てきます。言い換えると、仕事関係本位のリーダーシップだけでなく人間関係本位のリーダーシップも強めていくことが有効になります。
スポーツに例えると、「うるさく言うのも半分くらいで勝てるようになってきた。」という状態です。

cは更に成熟度が進み、構成員が自主性を身に付けはじめ、自分の仕事を自ら見つけ、必要なスキルを自ら身に付けるようになり始める段階です。
この段階になると、仕事本位のリーダーシップを取る必要性が薄くなり、構成員の自主性を維持するための人間関係本位のリーダーシップを前面に押し出すことが有効になります。
スポーツに例えると、「勝つためには選手と十分に話し合って戦略を作ることだ。」という状態です。

dは成熟度が最も進んだ段階です。
各々の構成員が自らリーダーシップを取るようになるので、リーダーは人間関係本位のリーダーシップも弱め、構成員に任せて見守るというリーダーシップの取り方が有効になります。
スポーツに例えると、「勝つためには選手に戦術の立案と実行を任せることだ。」という状態です。


【実践】

IT業界の開発の現場では、具体的に以下のことが言えます。

最も成熟度が低いaの状態というのは、構成員のスキルも自主性も不足している状態のことです。もう少し具体的に言うと、成果を出すために何をすれば良いのかわからない状態や、そもそも成果を出すための意欲が低い状態のことを指します。
現場としては、全くの新規プロジェクトの現場や、前任者が離脱して残された構成員が右往左往している現場、組織再編や短納期化等でこれまで頼っていた社内プロセスを見直さざるを得なくなった現場等が、これに該当するでしょう。
この状態の組織では、十分な能力や経験を持つ者がリーダーとなり、リーダー主導で作業計画や品質強化を行う必要がありますし、リーダー自ら設計や開発に関わる必要もあります。炎上プロジェクトの火消し役、というのもここに含まれます。
リーダーのワンマンプレイのようになるので人間関係に軋轢が生じることもありますが、この段階では副作用としてある程度受け入れざるを得ません

成熟度が少し高くなり、bの状態になると、構成員にスキルが身に付き始めます。この状態になると、末端の細かい判断や作業は構成員自らできるようになります。
現場で言うと、混沌とした時期を乗り越え、プロセスが正常に回り始めた現場と言うことができると思います。
この状態になると、構成員がプロセスに沿って自ら仕事ができるようになります。リーダーは、構成員がプロセスに沿って仕事ができるようにサポートするのが主な仕事になります。
まだまだリーダー自ら動かなければならない場面も多いですが、このあたりからは構成員に任せる仕事の範囲を徐々に広げ、構成員と良い人間関係を築くことも考える必要が出てきます。あまり長い間リーダーが前面に出ていると、構成員の自主性が育つのを阻害し、次の成熟度へ進むのが難しくなります。

成熟度が更に高くなり、cの状態になると、構成員に自主性が育ち、自ら考えられるようになります。
この段階になると、重厚長大なプロセスに頼らずとも、各々の構成員が適切な判断をするようになります。不測の事態が発生した時にも対応が楽になりますし、そもそも不測の事態が発生しないように各々の構成員が手を打つようになります。
この状態になると、これまでリーダーが行っていたような判断を構成員自ら行うようになりますし、その上で足りないスキルがあれば構成員自ら努力して身に付けるようになります。
この段階になれば、リーダー自ら動く必要はなくなります。構成員が気持ちよく仕事ができるように(このリーダーの下で働きたいと思ってもらえるように)、人間関係のメンテナンスに注力するのが有効になります。

成熟度が最高に高まり、dの状態になると、構成員自らリーダーシップを取るようになります。
各々の構成員が適切な判断をするようになるだけでなく、リーダーのように組織作りといったことも考えるようになります。
この段階に到達すると、リーダーは人間関係のメンテナンスをする必要もなくなります。
リーダーの仕事は、構成員に任せて見守るというものになります。この状態であれば、リーダーが更に上の立場に昇進したとしても、現場に問題は発生しないでしょう。


いかがでしたでしょうか。

今回は、リーダーシップ状況論について、情報処理技術者試験で問われることだけでなく、より実践的なことを経験則から補記しました。
実践的なことも知っていると、記憶が定着しやすくなりますし、知識も活かしやすくなると思います。

ソースコード修正の競合と統合

複数の案件が同時に動いており、1つのソースコードを複数の案件で修正する場合、ソースコード修正の競合(コンフリクト)の統合(マージ、差分取り込み)が必要になります。
統合には、全体のロジックの整合性や案件のリリース時期の違いを考慮する必要があり、機械的に判断できない場合もあるので、最終的には人の手で判断し統合する必要があります。


例えば、商品の販売価格を計算する以下のソースコードがあるとします。
(今回はjavaのソースコードであると仮定します)

・修正前ソース

このソースコードについて、下記3案件で修正するものとします。
案件1.消費税増税対応(3ヶ月後リリース)
案件2.店舗毎セール対応(3ヶ月後リリース)
案件3.タイムセール対応(6ヶ月後リリース)

3案件の修正後ソースはそれぞれ以下の通りとします。

・案件1

・案件2

・案件3

リリースすることを考えると、以下の2段階に分けてソースコードの修正を統合する必要があります。

・3ヶ月後を想定

 案件1と案件2を統合

・6ヶ月後を想定

 3ヶ月後のソースと案件3を統合


案件1と案件2の統合は容易です。
以下のように機械的に統合することができます。

・3ヶ月後のソース

対して、3ヶ月後のソースと案件3を統合は機械的に行うことができません。
修正ポイント2-5と修正ポイント3-4は同じ個所を修正しているので、両方の修正が反映されるようにソースを書き変えなければなりません。
そもそも、修正ポイント2-3でコンストラクタが追加になっているため、新たに追加したコンストラクタに対しても案件3の内容を反映しなければなりません。
よって、6ヶ月後のソースは以下のようになります。

・6ヶ月後のソース


なお、バージョン管理システムのGitのブランチやマージの機能を使えば、これらの統合の作業が楽になります。
機械的に統合できる箇所は自動で統合できますし、機械的に統合できないブランチについても以下のような方法を用いて比較的容易に統合することができます。
(具体的な操作方法は省略します。操作方法はWebで調べられるので、必要に応じて調べると良いでしょう。)

  • Gitが出力した差分を見てエディタで編集
  • 競合の原因となったコミットを取り消しコミットし直し
  • マスター管理しているブランチを元に新たにブランチを作り直し、作り直したブランチに対して同じ修正を行う

いかがでしたでしょうか。

「ソースコード修正の競合と統合」と言うと、Gitのテクニックが求められるように思われるかもしれません。
しかし、Gitの操作が多少不得手でも、何をしなければならないのかが理解できていれば、多少手間取ったり非効率になったりすることはあっても、最終的には問題無く修正作業を行うことができます。
まずは、何をしなければならないのかを理解することに努めましょう。
(それを理解する上で、今回の記事が参考になれば幸いです)

障害の発生原因の切り分けのポイント

テストや本番運用で障害が発生した場合、既知の障害等で原因が明らかな場合を除き、対応のために原因を調査する必要があります。
原因を調査する上ではどこに原因があるのかの切り分けが必要になります。
以下では、切り分け作業を行う上でのポイントを順を追って説明します。

1.障害が発生した状況を保全する

障害が発生したデータやその状況を記録したログは後の調査で使いますので、誤って更新したり削除したりしないように保全する必要があります。
テスト中であればそのデータを用いたテストは中断する必要がありますし、場合によってはコピーして別の場所に補完する必要があります。

2.期待される値との比較を行う

データやログを見て、設計上期待される値と異なる点を探していきます。
コーディングの単純な誤りであれば、大抵の場合は、データが期待値と異なるようになった箇所やログの出力内容から原因を特定できます。
場合によっては、その期待値自体が合っているのかどうか、設計や要件の確認・再検討が必要になる場合もあります。

3.障害発生手順を確立させる

ログの出力内容が不足している場合や原因が込み入っている場合は、データやログを見ただけでは原因がわからない場合があります。
その場合は、障害が発生した時と同じ手順で障害の再現を確認します。
障害が再現すれば、障害を発生させる手順が確立されたということになります。
厄介なのは障害が再現しなかった場合で、一意キー(顧客番号や受付番号の類)が障害発生時と異なることに注目して一意キーと結びつくデータを洗い出す、ランダムに処理が変わる箇所がないか(乱数を使っている箇所や振り分け先がランダムなロードバランサー等に注目する)、という観点で再現しなかった理由を調査し、障害発生手順を確立させます。
どうしても確立できない場合は、ハード障害である可能性を視野に、障害発生時の状況をまとめてハードウェアを提供するベンダーに確認する、というアクションを起こす必要がある場合もあります。
(私の経験上、「太陽フレアでビットが入れ替わってしまった」という冗談のような原因を告げられたこともあります)

4.再現手順を元に開発環境で原因調査を行う

再現手順が確立したら、開発環境で再現手順を試し、徐々に原因を絞り込んでいきます。
もし環境を変えたことで障害が再現しなくなった場合、環境問題である可能性が出てきます。
(例えば、マスタデータの不備、機器の構成の違いに起因する問題、等)
障害が再現する場合、開発環境ではデバッグを入れて変数の内容を表示することができますので、それをログの代わりにして原因調査を進めることができます。
また、開発環境ではデータも自由に書き変えられますので、データを少しずつ書き変えて挙動を確かめることでも、原因調査を進められます。


いかがでしたでしょうか。

障害調査は経験則で語られることが多いので、私自身の経験を基に文章にしてみました。
もし参考になれば幸いです。

カプセル化によるルールの強制

オブジェクト指向を適用すると、ソースコードの重複した記述を排除でき、生産性や品質を向上することができます。
それとは別に、他の開発者にルールを強制できるメリットもあります。
カプセル化を例にして説明するのがわかりやすいので、今回はカプセル化を例に挙げてルールを強制するメリットを書いていきます。


例えば、自分は商品の金額を計算するPriceCalcクラスを作成していて、他の開発者はPriceCalcクラスを利用するUserクラスを作成しているとします。
自分としては「このロジックで金額計算して欲しい」というのがあるのですが、他の開発者にそのロジックの実装をさせるようにしてしまうと、意図が上手く伝わらなかった時に誤ったロジックで計算されてしまいます。
この状況をソースコードで表すと下記のような形になり、PriceCalcクラスは計算に必要な変数をまとめるだけ、PriceCalc側でその変数を参照して独自に業務ロジックを実装するような形となります。


それでは困るので、クラス変数に付随する業務ロジックもPriceCalcクラス側に実装することにします。
変数とメソッドをひとまとまりにして一つのクラスとして提供することで、Userクラス側ではメソッドを呼び出すだけであるべき業務ロジックに基づいた計算を行うことができ、Userクラス側で業務ロジックを意識する必要が無くなります。

しかし、メンバ変数がpublicだと、Userクラス側でその変数を参照して独自に業務ロジックを実装することもできてしまいます。
自分としてはそれでは困るので、「変数を参照して独自に業務ロジックを実装しないでください」と伝えるのですが、それが上手く伝わらないと独自に業務ロジックを実装されてしまう可能性があります。
自分一人でプログラムを作っている場合は全ての変数をpublicにした方が便利だったりもしますが、複数人で開発している場合は意思疎通がうまく行かなかった場合に意図通りではないコーディングをされてしまうリスクがあります。


それを防ぐためには、変数のアクセスレベルをprivate等にして、変数に自由にアクセスできないようにするのが有効です。
こうすることで、変数を参照して独自に業務ロジックを実装しようとされた場合に、コンパイルエラーとして機械的に失敗させることができます。
これが、カプセル化によるルールの強制であり、意思疎通がうまく行かなかった場合に意図通りではないコーディングをされるリスクを減らすことができます。


いかがでしたでしょうか。

一人で開発する時と複数人で開発する時では、気を付けるべきポイントが変わることがあります。
オブジェクト指向の考え方の一つである「カプセル化」も、単なる慣習として捉えるのではなく、複数人で開発する場合にルールを規定するものとして捉えることで、実務で有効に使えるのではないかと思います。