メインコンテンツへスキップ
近年、社会的に個人情報の取り扱いが厳しくなっております。
社外向けのみならず社内向けの情報転送にも、当たり前のように個人情報の漏えいがないように気を付けています。
システムの高可用性を維持するために導入したマイクロサービスアーキテクチャは、その特徴によって同じ情報(形も意味も含めて)を複数のシステムでそれぞれ持つようになりました。アーキテクチャの特徴による個人情報を含めた様々な重複情報の存在は、データベース1箇所で集約したモノリシックアーキテクチャより遥かに頻雑になりました。 また、AIやデータサイエンスの台頭によるデータ分析基盤の構築で会社が扱っているデータの1箇所への集約も大きな課題になっています。 弊社のXplentyでは、多数のシステムに散在している様々な形の情報を取り集め、集計や結合などのデータ変換で高付加価値の情報への転換に適したSaaS型サービスです。
その点を有効活用によって会社として特定の個人情報に一貫した保護策を講じられます。
今回のブログでは、シャッフル、部分隠し、暗号化による個人情報の保護策をXplentyでどう実装するかを見てみましょう。

個人情報マスキングの種類

個人情報の保護は、下記の3種類の方法があります。各々の方法には長所と短所があり、用途に沿って使う必要があります。
  • シャッフル
    • データの文字や数字を置き換える、または順番を変えること
    • 本番データをテストデータとして提供する場合に有効
    • 意味を持つ部分の置き換えは、意味を維持しつつ変換するための工夫が必要
  • 部分隠し
    • データの一部分を無意味な文字に置き換えること
    • フォーマットは維持と特定の部分(元の値を置き換えが必要)のみを見せる場合に有効
    • シャッフルと同じく意味を持つ部分の置き換えは工夫が必要
  • 暗号化
    • 単方向ハッシュ関数を使い、別のデータに変換すること
    • 種類が少ないデータの原型が把握出来なくする場合に有効
    • 復号化が出来ない関数を使うのうが大事である

個人情報のマスキングの実行箇所

個人情報の保護について実装箇所は、主にselectコンポーネントにて行われます。
etc-part10-jp image 1
selectコンポーネントの中を覗いてみると下記の通りです。
etc-part10-jp image 2
etc-part10-jp image 3
また、各々のフィールドの設定は、図の赤の矢印が示している部分をクリックすると現れる下記のExpression Editor上で行われます。
etc-part10-jp image 4
もちろん、Expressionに該当関数式をコピペしても問題ありません。

個人情報のマスキングの例

例に使われているデータは、すべてSmartHRのサンドボックス環境から取得したものです。 SmartHRのサンドボックス環境やAPIについては、SmartHR API Specificationsをご参照頂ければ幸いです。

社員番号(数字のみ)

元のデータシャッフル部分隠し
0000800903##008
00023031030##23
0002603104000##
0002503304000##
0001702502##017
0000601400000##
0000100104##001
0002403201##024
0001902701##019
00018025040##18
● シャッフル
SPRINTF('%03d%02d',
	(int)SUBSTRING(emp_code, 3, Length(emp_code)) + (ROUND(RANDOM()*10) % 10),
	(int)SUBSTRING(emp_code, 0, 3) + (ROUND(RANDOM()*10) % 10)
)
● 部分隠し
CASE ROUND(RANDOM() * 10) % 4 
WHEN 1 THEN 
	CONCAT(SUBSTRING(emp_code, 0, Length(emp_code)-2), '##')
WHEN 2 THEN 
	CONCAT(SUBSTRING(emp_code, 0, 1), '##', SUBSTRING(emp_code, Length(emp_code)-2, Length(emp_code)))
WHEN 3 THEN 
	CONCAT(SUBSTRING(emp_code, 0, 2), '##', SUBSTRING(emp_code, Length(emp_code)-1, Length(emp_code)))
ELSE
	CONCAT('##', SUBSTRING(emp_code, 2, Length(emp_code)))
END

名前(漢字)

Last NameFirst NameLast Name(シャッフル)First Name(シャッフル)Last Name(部分隠し)
石井玲佳玲井石佳#井
古賀祐奈古奈祐賀#賀
菊池舞麗菊麗舞池菊#
湯浅海琴海浅海浅湯#
高松高暢高#
河野咲枝咲野河枝河#
上野真綾真野上綾#野
小山泰治泰山小治小#
大場孝浩大浩大浩#場
松原敏雄敏原松雄松#
● シャッフル(last_name, first_name)
CASE ROUND(RANDOM() * 10) % 2
WHEN 0 THEN
	CONCAT(SUBSTRING(last_name, 0, 1), SUBSTRING(first_name, 1, Length(first_name)))
ELSE
	CONCAT(SUBSTRING(Reverse(first_name), 1, Length(first_name)), SUBSTRING(Reverse(last_name), 0, 1))
END

CASE ROUND(RANDOM() * 10) % 2
WHEN 1 THEN
	CONCAT(SUBSTRING(first_name, 0, 1), SUBSTRING(last_name, 1, Length(last_name)))
ELSE
	CONCAT( SUBSTRING(Reverse(last_name), 1, Length(last_name)), SUBSTRING(Reverse(first_name), 0, 1))
END
● 部分隠し(last_name, first_name)
CASE ROUND(RANDOM() * 10) % 2
WHEN 0 THEN
	SPRINTF('%s%s', SUBSTRING(last_name, 0, 1), REPLACE(SUBSTRING(last_name, 1, Length(last_name)), '.', '#'))
ELSE
	SPRINTF('%s%s', REPLACE(SUBSTRING(last_name, 0, Length(last_name)-1), '.', '#'), SUBSTRING(last_name, Length(last_name)-1, Length(last_name)))
END

CASE ROUND(RANDOM() * 10) % 2
WHEN 1
	THEN SPRINTF('%s%s', SUBSTRING(first_name, 0, 1), REPLACE(SUBSTRING(first_name, 1, Length(first_name)), '.', '#'))
ELSE
	SPRINTF('%s%s', REPLACE(SUBSTRING(first_name, 0, Length(first_name)-1), '.', '#'), SUBSTRING(first_name, Length(first_name)-1, Length(first_name)))
END

誕生日(年月日)

元のデータ部分隠し
1948-12-2219**-**-**
● 部分隠し
CONCAT(
    SUBSTRING(birth_at, 0, 2), 
    REPLACE(SUBSTRING(birth_at, 2, Length(birth_at)), '[0-9]', '*')
)

性別

元のデータ部分隠し
male07cf4
female273b9
● 暗号化
SUBSTRING(MD5(gender), 0, 5)

--> MD5の代わりにSHA256などの他の単方向暗号化関数でも良い

電話番号

元のデータシャッフル部分隠し
090-5302-5398090-2053-8953090-****-****
070-4596-8665070-6945-5686070-****-****
070-7119-7796070-9171-6977070-****-****
090-3071-1160090-1730-0611090-****-****
070-1688-0980070-8816-0809070-****-****
090-8426-7235090-6284-5372090-****-****
070-7213-2746070-3172-6427070-****-****
090-8151-8277090-1581-7782090-****-****
090-3304-2880090-4033-0828090-****-****
080-5377-6182080-7753-2861080-****-****
● シャッフル
SPRINTF('%s-%s%s-%s%s',
	STRSPLIT(tel_number, '-').$0,
	SUBSTRING(Reverse(STRSPLIT(tel_number, '-').$1), 0, 2), 
	SUBSTRING(STRSPLIT(tel_number, '-').$1, 0, 2),
	SUBSTRING(Reverse(STRSPLIT(tel_number, '-').$2), 0, 2),
	SUBSTRING(STRSPLIT(tel_number, '-').$2, 0, 2)
)
● 部分隠し
CONCAT(STRSPLIT(tel_number, '-').$0, '-',
	REPLACE(STRSPLIT(tel_number, '-').$1, '[0-9]', '*'),'-', 
	REPLACE(STRSPLIT(tel_number, '-').$2, '[0-9]', '*')
)

郵便番号

元のデータ部分隠し
567-0896#6#-#896
480-1122#8#-11##
484-08634#4-0###
414-00264#4-002#
409-2946##9-#9#6
347-0104#47-0#04
938-01789##-0#7#
950-11139##-1113
964-09359#4-09##
957-00669##-##66
● 部分隠し
(ROUND(RANDOM()*10) % 2 == 0 ?
	REPLACE(address#'zip_code', '[13568]' ,'#') :
	REPLACE(address#'zip_code', '[02457]' ,'#')
)

住所(番地)

元のデータシャッフル_1シャッフル_2部分隠し
4-23-114-11-2310-27-144-2#-##
7-22-77-22-716-29-9#-22-#
7-5-117-11-57-14-137-5-11
1-13-191-31-916-20-26#-##-##
3-9-293-9-925-15-373-9-#9
7-27-307-72-39-34-397-#7-3#
8-22-218-22-128-23-258-22-2#
6-4-176-17-413-11-196-4-##
1-26-161-16-2610-27-23#-26-#6
7-23-107-10-2314-27-197-#3-1#
● シャッフル_1
(ROUND(RANDOM()*10) % 2 == 0 ?
	SPRINTF('%s-%d-%d',
		STRSPLIT(address#'street', '-').$0,
		(int)STRSPLIT(address#'street', '-').$2,
		(int)STRSPLIT(address#'street', '-').$1
	) :
	SPRINTF('%s-%d-%d',
		STRSPLIT(address#'street', '-').$0,
		(int)Reverse(STRSPLIT(address#'street', '-').$1),
		(int)Reverse(STRSPLIT(address#'street', '-').$2)
	)
)
● シャッフル_2
SPRINTF('%d-%d-%d',
	(int)STRSPLIT(address#'street', '-').$0 + (ROUND(RANDOM()*10) %10),
	(int)STRSPLIT(address#'street', '-').$1 + (ROUND(RANDOM()*10) %10),
	(int)STRSPLIT(address#'street', '-').$2 + (ROUND(RANDOM()*10) %10)
)
● 部分隠し
(ROUND(RANDOM()*10) % 2 == 0 ?
	REPLACE(address#'street', '[13579]' ,'#') :
	REPLACE(address#'street', '[02468]' ,'#')
)

メールアドレス

元のデータ部分隠し_1部分隠し_2
gaston.nikolaus@example.comga*************@######.comga*******@######.com
salvatore@example.comsa*******@######.comsa*******@######.com
bruno_abbott@example.netbr**********@######.netbr*******@######.net
arnold.shanahan@example.netar*************@######.netar*******@######.net
george@example.netge****@######.netge*******@######.net
celestina@example.orgce*******@######.orgce*******@######.org
avery.homenick@example.orgav************@######.orgav*******@######.org
tyson@example.netty***@######.netty*******@######.net
sang.hirthe@example.orgsa*********@######.orgsa*******@######.org
hunter@example.comhu****@######.comhu*******@######.com
● 部分隠し_1
SPRINTF('%s%s@%s%s',
	SUBSTRING(STRSPLIT(email, '@').$0, 0, 2),
	REPLACE(SUBSTRING(STRSPLIT(email, '@').$0, 2, Length(STRSPLIT(email, '@').$0)), '.', '*'),
	REPLACE(SUBSTRING(STRSPLIT(email, '@').$1, 0, LAST_INDEX_OF(STRSPLIT(email, '@').$1, '.') - 1), '.', '#' ),
	SUBSTRING(STRSPLIT(email, '@').$1, LAST_INDEX_OF(STRSPLIT(email, '@').$1, '.'), Length(STRSPLIT(email, '@').$1))
)
● 部分隠し_2
SPRINTF('%s%s@%s%s',
	SUBSTRING(STRSPLIT(email, '@').$0, 0, 2),
	'*******',
	'######',
	SUBSTRING(STRSPLIT(email, '@').$1, LAST_INDEX_OF(STRSPLIT(email, '@').$1, '.'), Length(STRSPLIT(email, '@').$1))
)

各種社会保険番号

元のデータシャッフル部分隠し_1部分隠し_2部分隠し_3
5063-196700-35107-007833-550##-#9#700-#5**3-1####0-3*0@@-@967@@-@
4598-668799-14603-997988-2#59#-###799-14**8-6####9-1*5@@-@687@@-@
4676-458881-54685-189071-8##7#-#5###1-54**6-4####1-5*6@@-@588@@-@
5105-769271-15180-173803-85#05-7#927#-#5**5-7####1-1*1@@-@692@@-@
5323-496653-15358-356889-353#3-#9##53-15**3-4####3-1*3@@-@966@@-@
8875-203981-78965-189912-788##-20##8#-#8**5-2####1-7*8@@-@039@@-@
8845-730489-58883-984208-6###5-73###9-58**5-7####9-5*8@@-@304@@-@
2765-123674-22856-476816-927#5-#2##7#-22**5-1####4-2*7@@-@236@@-@
8968-785929-69023-930160-48#68-#8####-68**8-7####9-6*9@@-@859@@-@
4879-952886-94923-688784-6##79-952###-94**9-9####6-9*8@@-@528@@-@
● シャッフル
SPRINTF('%04d-%06d-%d',
	((int)STRSPLIT(emp_ins_insured_person_number, '-').$0 + ROUND(RANDOM()*100)) % 10000,
	((int)Reverse(STRSPLIT(emp_ins_insured_person_number, '-').$1) + ROUND(RANDOM()*1000)) % 1000000,
	((int)STRSPLIT(emp_ins_insured_person_number, '-').$2 + ROUND(RANDOM()*10)) % 10
)
● 部分隠し_1
CASE ROUND(RANDOM() * 10) % 4 
WHEN 1 THEN
	REPLACE(emp_ins_insured_person_number, '[13579]', '#')
WHEN 2 THEN
	REPLACE(emp_ins_insured_person_number, '[02579]', '#')
WHEN 3 THEN
	REPLACE(emp_ins_insured_person_number, '[13468]', '#')
ELSE
	REPLACE(emp_ins_insured_person_number, '[02468]', '#')
END
● 部分隠し_2
SPRINTF('%s%s%s-%s%s%s-%s',
	REGEX_EXTRACT_ALL(emp_ins_insured_person_number, '(\\d)(\\d+)(\\d)-(\\d)(\\d+)(\\d)-(\\d)').$0,
	REPLACE(REGEX_EXTRACT_ALL(emp_ins_insured_person_number, '(\\d)(\\d+)(\\d)-(\\d)(\\d+)(\\d)-(\\d)').$1, '.', '*'),
	REGEX_EXTRACT_ALL(emp_ins_insured_person_number, '(\\d)(\\d+)(\\d)-(\\d)(\\d+)(\\d)-(\\d)').$2,
	REGEX_EXTRACT_ALL(emp_ins_insured_person_number, '(\\d)(\\d+)(\\d)-(\\d)(\\d+)(\\d)-(\\d)').$3,
	REPLACE(REGEX_EXTRACT_ALL(emp_ins_insured_person_number, '(\\d)(\\d+)(\\d)-(\\d)(\\d+)(\\d)-(\\d)').$4, '.', '#'),
	REGEX_EXTRACT_ALL(emp_ins_insured_person_number, '(\\d)(\\d+)(\\d)-(\\d)(\\d+)(\\d)-(\\d)').$5,
	REGEX_EXTRACT_ALL(emp_ins_insured_person_number, '(\\d)(\\d+)(\\d)-(\\d)(\\d+)(\\d)-(\\d)').$6
)
● 部分隠し_3
REPLACE(REPLACE(emp_ins_insured_person_number, '\\d\\d-\\d', '@@-@'), '^\\d', '*')

まとめ

Xplentyのselectコンポーネントには、このようなマスキング以外にも様々な機能が実装できます。ぜひ、Xplentyの公式マニュアルやX-consoleにて機能を確認してみてはいかがでしょうか? 無料トライアル申請にていつでもXplentyをお試し頂けます。
最終更新日 2026年7月13日