(一)敏感信息混淆


最近網上出現的安全事故很多,最慘的莫過於那場酒店事件,因此項目整改也需要對用戶的敏感信息進行脫敏,由於前期的項目並沒有規划這一模塊,為了滿足安全規范達到上線標准,需要對項目進行改造。


改造方案

  1. 不能采用手動encode/decode的方式(避免在某個地方忘記encode/decode了)

  2. 非侵入性改造,后續的coding的時候無需在費心思考慮

方案1: 從數據層入手,在提交數據庫前對敏感字段進行encode,在取得數據集后對結果集的敏感字段進行decode

  項目中選擇mybatis為中間件,因此可以使用mybatis攔截器,方案實現起來簡單,符合條件2,但卻會帶來關鍵問題:在單業務編寫時基本沒有問題,但是在多業務關聯的時候卻會對關聯的字段有影響,需要在手動encode,不符合條件1,因此方案被pass。

方案2: 從控制層入手,在接收客戶端提交的數據時對敏感字段進行encode,在response序列化對象時對敏感字段進行decode。

  該方案能保證敏感字段在業務邏輯實現過程中都是混淆的,后續也無需關注哪個對象的哪個字段是需要encode/decode,滿足改造方案。


 實現方案

   1. 定義敏感字段類型,由於混淆后的字段類型均可以為字符串,因此我們可以仿造String類型來進行定義(由於String類是final,因此不考慮繼承的方式)

/**
 * @author zhangqiuyang
 * Created on 2018/8/17.
 */
public class SecretString implements java.io.Serializable, Comparable<String>, CharSequence {
    @Setter
    @Getter
    private String value;

    public SecretString(String value) {
        this.value = value;
    }

    /**
     * Returns the length of this character sequence.  The length is the number
     * of 16-bit <code>char</code>s in the sequence.
     *
     * @return the number of <code>char</code>s in this sequence
     */
    @Override
    public int length() {
        return value.length();
    }

    /**
     * Returns a string representation of the object. In general, the
     * {@code toString} method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * <p>
     * The {@code toString} method for class {@code Object}
     * returns a string consisting of the name of the class of which the
     * object is an instance, the at-sign character `{@code @}', and
     * the unsigned hexadecimal representation of the hash code of the
     * object. In other words, this method returns a string equal to the
     * value of:
     * <blockquote>
     * <pre>
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * </pre></blockquote>
     *
     * @return a string representation of the object.
     */
    @Override
    public String toString() {
        return value;
    }

    /**
     * Returns the <code>char</code> value at the specified index.  An index ranges from zero
     * to <tt>length() - 1</tt>.  The first <code>char</code> value of the sequence is at
     * index zero, the next at index one, and so on, as for array
     * indexing.
     *
     * <p>If the <code>char</code> value specified by the index is a
     * <a href="{@docRoot}/java/lang/Character.html#unicode">surrogate</a>, the surrogate
     * value is returned.
     *
     * @param index the index of the <code>char</code> value to be returned
     * @return the specified <code>char</code> value
     * @throws IndexOutOfBoundsException if the <tt>index</tt> argument is negative or not less than
     *                                   <tt>length()</tt>
     */
    @Override
    public char charAt(int index) {
        return value.charAt(index);
    }

    /**
     * Returns a <code>CharSequence</code> that is a subsequence of this sequence.
     * The subsequence starts with the <code>char</code> value at the specified index and
     * ends with the <code>char</code> value at index <tt>end - 1</tt>.  The length
     * (in <code>char</code>s) of the
     * returned sequence is <tt>end - start</tt>, so if <tt>start == end</tt>
     * then an empty sequence is returned.
     *
     * @param start the start index, inclusive
     * @param end   the end index, exclusive
     * @return the specified subsequence
     * @throws IndexOutOfBoundsException if <tt>start</tt> or <tt>end</tt> are negative,
     *                                   if <tt>end</tt> is greater than <tt>length()</tt>,
     *                                   or if <tt>start</tt> is greater than <tt>end</tt>
     */
    @Override
    public CharSequence subSequence(int start, int end) {
        return value.subSequence(start, end);
    }


    /**
     * Compares this object with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     *
     * <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
     * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
     * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
     * <tt>y.compareTo(x)</tt> throws an exception.)
     *
     * <p>The implementor must also ensure that the relation is transitive:
     * <tt>(x.compareTo(y)&gt;0 &amp;&amp; y.compareTo(z)&gt;0)</tt> implies
     * <tt>x.compareTo(z)&gt;0</tt>.
     *
     * <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
     * implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
     * all <tt>z</tt>.
     *
     * <p>It is strongly recommended, but <i>not</i> strictly required that
     * <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>.  Generally speaking, any
     * class that implements the <tt>Comparable</tt> interface and violates
     * this condition should clearly indicate this fact.  The recommended
     * language is "Note: this class has a natural ordering that is
     * inconsistent with equals."
     *
     * <p>In the foregoing description, the notation
     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
     * <i>expression</i> is negative, zero or positive.
     *
     * @param o the object to be compared.
     * @return a negative integer, zero, or a positive integer as this object
     * is less than, equal to, or greater than the specified object.
     * @throws NullPointerException if the specified object is null
     * @throws ClassCastException   if the specified object's type prevents it
     *                              from being compared to this object.
     */
    @Override
    public int compareTo(String o) {
        return value.compareTo(o);
    }

    /**
     * 解密后的值
     *
     * @return
     */
    public String decryptValue() {
        return SecurityUtil.decryptString(value);
    }
}

   2. 定義敏感字段類型的序列化器

/**
 * @author zhangqiuyang
 * Created on 2018/8/17.
 */
@JsonComponent
public class SecretStringJsonCombinedSerializer {


    /**
     * 密文序列化
     */
    public static class SecretStringJsonSerializer extends JsonSerializer<SecretString> {
        /**
         * 重寫序列化SecretString類型字段
         *
         * @param secretString
         * @param jsonGenerator
         * @param serializerProvider
         * @throws IOException
         */
        @Override
        public void serialize(SecretString secretString, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
                throws IOException {

            jsonGenerator.writeString(secretString.decryptValue());
        }
    }

    /**
     * 密文反序列化
     */
    public static class SecretStringJsonDeserializer extends JsonDeserializer<SecretString> {

        @Override
        public SecretString deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
                throws IOException {
            TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
            return new SecretString(SecurityUtil.encryptString(((TextNode) treeNode).asText()));
        }
    }


}

 

綜上,改造基本完成。

github:https://github.com/zqyx5201/SecretStringTest


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM