システムログ内の機密フィールドマスク処理
33579 ワード
金融業務の開発として、多くのインタフェースがユーザー情報を使用する必要がありますが、ユーザー情報にはユーザー名、銀行カード番号などの敏感なフィールドがあります.したがって,ユーザ機密情報の保存やログ印刷の際には,これらの機密情報を明文的に保存することはできない.データベースに対してユーザ機密情報を保存する場合、一般的なシステムには復号化されたシステムがあります.ユーザ機密情報を保存する必要がある場合は,ユーザ情報を暗号化してデータベースに保存する.これは本明細書で説明する範疇ではなく、データベースに保存する際に推奨されるフォーマットは以下の通りです.
ユーザー情報を比較する際にマスク情報を使用して、会員情報の本当の身分証明書番号が合っているかどうかを確認する可能性があります.
1、ログ印刷に考慮すべき問題
次に,システムにおける機密ログ印刷の問題について議論する.
ログを印刷する必要がある場合、通常は2つの方法でログの印刷を行います.ObjectのtoStringを書き換え、注目フィールドの情報を印刷 fastjson(または他のJSON処理フレームワーク)のJSONを呼び出す.toStringメソッド また、ログ処理では、次の2つの処理が一般的に使用されます.ログは無視され、このフィールドがログクエリに影響を与えない場合、フィールドログは無視されます.オブジェクトフィールドが空の場合は印刷しません(またはnull値を印刷します)、値がある場合は 正規印刷ログは、このフィールドがログクエリに影響を与える場合に正規印刷ログを使用することができる.例えば、ユーザの身分証明書番号は、ログに身分証明書の上位3桁、および下位4桁の間に6個の
以上の考慮に基づいて、私たちはそれを実現します.
2、対象toString印刷
2.1 Ignore
注釈は、オブジェクト内のこのフィールドのログが無視できることを示します.
2.2 Mask
注釈は、オブジェクト内のこのフィールドのログがログのクエリーに影響することを示し、正規の方法で印刷できます.
2.3 AnnotationToStringBuilder
common-lang 3のReflectionToStringBuilderを実現し、反射によって印刷対象のログを呼び出す.
この方式はObjectのtoString法を実現する必要がある.例:
3、JSONのtoJSOnString方法
この場合、2つのケースがあります.1つは、このオブジェクトがjava beanオブジェクトであり、もう1つはJSONObjectです.これらは、fastjsonが提供する
Java beanに対しては元の@Ignoreと@Mask注記を用いて処理する.JSOnObjectの場合は、処理するフィールドを手動で追加し、ログ印刷ポリシーを指定できます.ポリシーインタフェースは次のとおりです.
ここでidNoは、空の印刷ポリシーを使用します.つまり、印刷しません.
JSON方式でログの脱敏を行うには、ObjectオブジェクトのtoStringメソッドを書き換える必要はありません.
:511911202005281234
:P1234567
DB :P1234567:511******1234
ユーザー情報を比較する際にマスク情報を使用して、会員情報の本当の身分証明書番号が合っているかどうかを確認する可能性があります.
1、ログ印刷に考慮すべき問題
次に,システムにおける機密ログ印刷の問題について議論する.
ログを印刷する必要がある場合、通常は2つの方法でログの印刷を行います.
***
を印刷します.*
番号を印刷することができる.例:511******1234
.以上の考慮に基づいて、私たちはそれを実現します.
2、対象toString印刷
2.1 Ignore
注釈は、オブジェクト内のこのフィールドのログが無視できることを示します.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Ignore {
}
2.2 Mask
注釈は、オブジェクト内のこのフィールドのログがログのクエリーに影響することを示し、正規の方法で印刷できます.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Mask {
String pattern() default "";
}
2.3 AnnotationToStringBuilder
common-lang 3のReflectionToStringBuilderを実現し、反射によって印刷対象のログを呼び出す.
ReflectionToStringBuilder#appendFieldsIn
メソッドを書き換えて、私たちの印刷効果を実現しました.public class AnnotationToStringBuilder extends ReflectionToStringBuilder {
public AnnotationToStringBuilder(Object object) {
super(object);
}
public AnnotationToStringBuilder(Object object, ToStringStyle style) {
super(object, style);
}
public AnnotationToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
super(object, style, buffer);
}
public <T> AnnotationToStringBuilder(T object, ToStringStyle style, StringBuffer buffer, Class<? super T> reflectUpToClass, boolean outputTransients, boolean outputStatics) {
super(object, style, buffer, reflectUpToClass, outputTransients, outputStatics);
}
public <T> AnnotationToStringBuilder(T object, ToStringStyle style, StringBuffer buffer, Class<? super T> reflectUpToClass, boolean outputTransients, boolean outputStatics, boolean excludeNullValues) {
super(object, style, buffer, reflectUpToClass, outputTransients, outputStatics, excludeNullValues);
}
@Override
protected void appendFieldsIn(Class clazz) {
if (clazz.isArray()) {
this.reflectionAppendArray(this.getObject());
return;
}
Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
String fieldName = field.getName();
if (this.accept(field)) {
try {
Object fieldValue = this.getValue(field);
Mask mask = AnnotationUtils.getAnnotation(field, Mask.class);
if( (fieldValue instanceof String) && mask != null && StringUtils.isNotBlank(mask.pattern())) {
String value = String.class.cast(fieldValue);
String pattern = mask.pattern();
this.append(fieldName, OutMaskUtil.replaceWithMask(pattern, value));
continue;
}
Ignore ignore = AnnotationUtils.getAnnotation(field, Ignore.class);
if(ignore != null) {
if(fieldValue != null) {
this.append(fieldName, "***");
} else {
this.append(fieldName, "null");
}
continue;
}
this.append(fieldName, fieldValue);
} catch (IllegalAccessException ex) {
throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
}
}
}
}
}
この方式はObjectのtoString法を実現する必要がある.例:
@Data
public class UserInfo {
/** */
private String username;
/** */
@Mask(pattern = "[\\w]{5}([\\w]*)[\\w]{3}")
private String idNo;
/** */
@Ignore
private String address;
@Override
public String toString(){
return new AnnotationToStringBuilder(this).toString();
}
}
3、JSONのtoJSOnString方法
この場合、2つのケースがあります.1つは、このオブジェクトがjava beanオブジェクトであり、もう1つはJSONObjectです.これらは、fastjsonが提供する
com.alibaba.fastjson.serializer.ValueFilter
を実装することによって処理することができる.Java beanでは反射処理が可能であり,JSONObjectの処理には特殊な処理が必要であり,汎用化できない.public class LoggerJSON {
static final SerializeConfig SERIALIZE_CONFIG;
static final MaskFilter MASK_FILTER;
static final Map<String, MaskStrategy> MASK_FIELDS;
static {
SERIALIZE_CONFIG = new SerializeConfig();
MASK_FILTER = new MaskFilter();
MASK_FIELDS = buildMaskConfig();
}
/**
*
*/
public static Map<String, MaskStrategy> buildMaskConfig() {
Map<String, MaskStrategy> result = new HashMap<>();
result.put("idNo", new NullMaskStrategy());
return result;
}
public static <T> String toMaskJsonString(T content) {
return JSON.toJSONString(content, SERIALIZE_CONFIG, MASK_FILTER);
}
static class MaskFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
if(!isString(value)){
return value;
}
String stringValue = String.class.cast(value);
Class<?> clazz = object.getClass();
try {
// JSONObject
if(MASK_FIELDS.containsKey(name)){
MaskStrategy maskStrategy = MASK_FIELDS.get(name);
return maskStrategy.process(stringValue);
}
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
// Java Bean @Ignore
Ignore ignore = AnnotationUtils.getAnnotation(field, Ignore.class);
if(ignore != null && value != null) {
return "***";
}
// Java Bean @Mask
Mask mask = AnnotationUtils.getAnnotation(field, Mask.class);
if(mask != null && StringUtils.isNotBlank(mask.pattern())) {
return OutMaskUtil.replaceWithMask(mask.pattern(), stringValue);
}
} catch (Exception e) {
// ignore
}
return value;
}
private boolean isString(Object value){
if(value == null) {
return false;
}
return value instanceof String;
}
}
}
Java beanに対しては元の@Ignoreと@Mask注記を用いて処理する.JSOnObjectの場合は、処理するフィールドを手動で追加し、ログ印刷ポリシーを指定できます.ポリシーインタフェースは次のとおりです.
public interface MaskStrategy {
String process(String value);
}
ここでidNoは、空の印刷ポリシーを使用します.つまり、印刷しません.
public class NullMaskStrategy implements MaskStrategy {
@Override
public String process(String value) {
return null;
}
}
JSON方式でログの脱敏を行うには、ObjectオブジェクトのtoStringメソッドを書き換える必要はありません.